Add auto-update when adding/removing Manga
Style SearchResults
This commit is contained in:
parent
daa05a0b4d
commit
3f26d3bbd6
@ -1,42 +1,50 @@
|
||||
import React, {ReactElement, useEffect} from 'react';
|
||||
import React, {EventHandler, ReactElement, useEffect, useState} from 'react';
|
||||
import Footer from "./modules/Footer";
|
||||
import Search from "./modules/Search";
|
||||
import Header from "./modules/Header";
|
||||
import MonitorJobsList from "./modules/MonitorJobsList";
|
||||
import './styles/Manga.css'
|
||||
import './styles/index.css'
|
||||
|
||||
export default function App(){
|
||||
const [content, setContent] = React.useState<ReactElement>();
|
||||
|
||||
function ShowSearch() {
|
||||
setContent(<>
|
||||
<Search />
|
||||
<MonitorJobsList onStartSearch={ShowSearch} />
|
||||
</>);
|
||||
}
|
||||
const [connected, setConnected] = React.useState(false);
|
||||
const [showSearch, setShowSearch] = React.useState(false);
|
||||
const [lastMangaListUpdate, setLastMangaListUpdate] = React.useState<Date>(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
setContent(<h1>Testing connection to backend...</h1>)
|
||||
getData('http://127.0.0.1:6531/v2/Ping').then((result) => {
|
||||
console.log(result);
|
||||
if(result === null){
|
||||
setContent(<h1>No connection to backend</h1>);
|
||||
setConnected(false);
|
||||
}else{
|
||||
setContent(<>
|
||||
<MonitorJobsList onStartSearch={ShowSearch} />
|
||||
</>)
|
||||
setConnected(true);
|
||||
}
|
||||
})
|
||||
}, []);
|
||||
|
||||
const JobsChanged : EventHandler<any> = () => {
|
||||
console.log("Updating Mangalist");
|
||||
setLastMangaListUpdate(new Date());
|
||||
}
|
||||
|
||||
return(<div>
|
||||
<Header />
|
||||
{content}
|
||||
<Footer />
|
||||
<Header/>
|
||||
{connected
|
||||
? <>
|
||||
{showSearch
|
||||
? <>
|
||||
<Search onJobsChanged={JobsChanged}/>
|
||||
<hr/>
|
||||
</>
|
||||
: <></>}
|
||||
<MonitorJobsList onStartSearch={() => setShowSearch(true)} onJobsChanged={JobsChanged}
|
||||
key={lastMangaListUpdate.getTime()}/>
|
||||
</>
|
||||
: <h1>No connection to backend</h1>}
|
||||
<Footer/>
|
||||
</div>)
|
||||
}
|
||||
|
||||
export function getData (uri: string) : Promise<object> {
|
||||
export function getData(uri: string) : Promise<object> {
|
||||
return fetch(uri,
|
||||
{
|
||||
method: 'GET',
|
||||
@ -66,8 +74,10 @@ export function postData(uri: string, content: object) : Promise<object> {
|
||||
body: JSON.stringify(content)
|
||||
})
|
||||
.then(function(response){
|
||||
if(!response.ok) throw new Error("Could not fetch data");
|
||||
return response.json();
|
||||
if(!response.ok)
|
||||
throw new Error("Could not fetch data");
|
||||
let json = response.json();
|
||||
return json.then((json) => json).catch(() => null);
|
||||
})
|
||||
.catch(function(err){
|
||||
console.error(`Error POSTing Data ${uri}\n${err}`);
|
||||
@ -75,8 +85,8 @@ export function postData(uri: string, content: object) : Promise<object> {
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteData(uri: string) {
|
||||
fetch(uri,
|
||||
export function deleteData(uri: string) : Promise<void> {
|
||||
return fetch(uri,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers : {
|
||||
@ -84,6 +94,9 @@ export function deleteData(uri: string) {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(() =>{
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch(function(err){
|
||||
console.error(`Error DELETEing Data ${uri}\n${err}`);
|
||||
return Promise.reject();
|
||||
|
@ -9,7 +9,9 @@ export class Job
|
||||
return getData("http://127.0.0.1:6531/v2/Jobs")
|
||||
.then((json) => {
|
||||
console.debug("Got all Jobs");
|
||||
return (json as string[]);
|
||||
const ret = json as string[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,7 +20,9 @@ export class Job
|
||||
return getData("http://127.0.0.1:6531/v2/Jobs/Running")
|
||||
.then((json) => {
|
||||
console.debug("Got all running Jobs");
|
||||
return (json as string[]);
|
||||
const ret = json as string[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -27,7 +31,9 @@ export class Job
|
||||
return getData("http://127.0.0.1:6531/v2/Jobs/Waiting")
|
||||
.then((json) => {
|
||||
console.debug("Got all waiting Jobs");
|
||||
return (json as string[]);
|
||||
const ret = json as string[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -36,7 +42,9 @@ export class Job
|
||||
return getData("http://127.0.0.1:6531/v2/Jobs/Monitoring")
|
||||
.then((json) => {
|
||||
console.debug("Got all monitoring Jobs");
|
||||
return (json as string[]);
|
||||
const ret = json as string[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,7 +57,9 @@ export class Job
|
||||
return getData(`http://127.0.0.1:6531/v2/Job/${jobId}`)
|
||||
.then((json) => {
|
||||
console.debug(`Got Job ${jobId}`);
|
||||
return (json as IJob);
|
||||
const ret = json as IJob;
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -63,7 +73,9 @@ export class Job
|
||||
return getData(`http://127.0.0.1:6531/v2/Job?jobIds=${reqStr}`)
|
||||
.then((json) => {
|
||||
console.debug(`Got Jobs ${reqStr}`);
|
||||
return (json as IJob[]);
|
||||
const ret = json as IJob[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -72,11 +84,13 @@ export class Job
|
||||
return getData(`http://127.0.0.1:6531/v2/Job/${jobId}/Progress`)
|
||||
.then((json) => {
|
||||
console.debug(`Got Job ${jobId} Progress`);
|
||||
return (json as IProgressToken);
|
||||
const ret = json as IProgressToken;
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
static async CreateJob(internalId: string, jobType: string, interval: string): Promise<IJob> {
|
||||
static async CreateJob(internalId: string, jobType: string, interval: string): Promise<null> {
|
||||
console.debug(`Creating Job for Manga ${internalId} at ${interval} interval`);
|
||||
let data = {
|
||||
internalId: internalId,
|
||||
@ -85,19 +99,19 @@ export class Job
|
||||
return postData(`http://127.0.0.1:6531/v2/Job/Create/${jobType}`, data)
|
||||
.then((json) => {
|
||||
console.debug(`Created Job for Manga ${internalId} at ${interval} interval`);
|
||||
return (json as IJob);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
static DeleteJob(jobId: string) {
|
||||
deleteData(`http://127.0.0.1:6531/v2/Job/${jobId}`);
|
||||
static DeleteJob(jobId: string) : Promise<void> {
|
||||
return deleteData(`http://127.0.0.1:6531/v2/Job/${jobId}`);
|
||||
}
|
||||
|
||||
static StartJob(jobId: string) {
|
||||
postData(`http://127.0.0.1:6531/v2/Job/${jobId}/StartNow`, {});
|
||||
static StartJob(jobId: string) : Promise<object> {
|
||||
return postData(`http://127.0.0.1:6531/v2/Job/${jobId}/StartNow`, {});
|
||||
}
|
||||
|
||||
static CancelJob(jobId: string) {
|
||||
postData(`http://127.0.0.1:6531/v2/Job/${jobId}/Cancel`, {});
|
||||
static CancelJob(jobId: string) : Promise<object> {
|
||||
return postData(`http://127.0.0.1:6531/v2/Job/${jobId}/Cancel`, {});
|
||||
}
|
||||
}
|
@ -8,7 +8,9 @@ export class Manga
|
||||
return getData("http://127.0.0.1:6531/v2/Mangas")
|
||||
.then((json) => {
|
||||
console.debug("Got all Manga");
|
||||
return (json as IManga[]);
|
||||
const ret = json as IManga[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -17,7 +19,9 @@ export class Manga
|
||||
return await getData(`http://127.0.0.1:6531/v2/Manga/Search?title=${name}`)
|
||||
.then((json) => {
|
||||
console.debug(`Got Manga ${name}`);
|
||||
return (json as IManga[]);
|
||||
const ret = json as IManga[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -26,7 +30,9 @@ export class Manga
|
||||
return await getData(`http://127.0.0.1:6531/v2/Manga/${internalId}`)
|
||||
.then((json) => {
|
||||
console.debug(`Got Manga ${internalId}`);
|
||||
return (json as IManga);
|
||||
const ret = json as IManga;
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
@ -35,7 +41,9 @@ export class Manga
|
||||
return await getData(`http://127.0.0.1:6531/v2/Manga?internalIds=${internalIds.join(",")}`)
|
||||
.then((json) => {
|
||||
console.debug(`Got Manga ${internalIds.join(",")}`);
|
||||
return (json as IManga[]);
|
||||
const ret = json as IManga[];
|
||||
console.debug(ret);
|
||||
return (ret);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,17 @@
|
||||
import React, {MouseEventHandler, ReactElement, useEffect} from 'react';
|
||||
import React, {EventHandler, MouseEventHandler, ReactElement, useEffect, useState} from 'react';
|
||||
import {Job} from './Job';
|
||||
import '../styles/monitorMangaList.css';
|
||||
import IJob from "./interfaces/IJob";
|
||||
import IManga, {HTMLFromIManga} from "./interfaces/IManga";
|
||||
import IManga, {CoverCard} from "./interfaces/IManga";
|
||||
import {Manga} from './Manga';
|
||||
import '../styles/MangaCoverCard.css'
|
||||
|
||||
export default function MonitorJobsList({onStartSearch} : {onStartSearch() : void}){
|
||||
const [MonitoringJobs, setMonitoringJobs] = React.useState<IJob[]>([]);
|
||||
const [AllManga, setAllManga] = React.useState<IManga[]>([]);
|
||||
|
||||
function UpdateMonitoringJobsList(){
|
||||
Job.GetMonitoringJobs()
|
||||
.then((jobs) => {
|
||||
if(jobs.length > 0)
|
||||
return Job.GetJobs(jobs)
|
||||
return [];
|
||||
})
|
||||
.then((jobs) => setMonitoringJobs(jobs));
|
||||
}
|
||||
export default function MonitorJobsList({onStartSearch, onJobsChanged} : {onStartSearch() : void, onJobsChanged: EventHandler<any>}) {
|
||||
const [MonitoringJobs, setMonitoringJobs] = useState<IJob[]>([]);
|
||||
const [AllManga, setAllManga] = useState<IManga[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
console.debug("Updating display list.");
|
||||
//Remove all Manga that are not associated with a Job
|
||||
setAllManga(AllManga.filter(manga => MonitoringJobs.find(job => job.mangaInternalId == manga.internalId)));
|
||||
//Fetch Manga that are missing (from Jobs)
|
||||
@ -36,9 +28,20 @@ export default function MonitorJobsList({onStartSearch} : {onStartSearch() : voi
|
||||
UpdateMonitoringJobsList();
|
||||
}, []);
|
||||
|
||||
const DeleteJob:MouseEventHandler = (e) => {
|
||||
const DeleteJob : MouseEventHandler = (e) => {
|
||||
const jobId = e.currentTarget.id;
|
||||
Job.DeleteJob(jobId);
|
||||
Job.DeleteJob(jobId).then(() => onJobsChanged(jobId));
|
||||
}
|
||||
|
||||
function UpdateMonitoringJobsList(){
|
||||
console.debug("Updating MonitoringJobsList");
|
||||
Job.GetMonitoringJobs()
|
||||
.then((jobs) => {
|
||||
if(jobs.length > 0)
|
||||
return Job.GetJobs(jobs)
|
||||
return [];
|
||||
})
|
||||
.then((jobs) => setMonitoringJobs(jobs));
|
||||
}
|
||||
|
||||
function StartSearchMangaEntry() : ReactElement {
|
||||
@ -61,7 +64,7 @@ export default function MonitorJobsList({onStartSearch} : {onStartSearch() : voi
|
||||
if (job === undefined || job == null)
|
||||
return <div>Error. Could not find matching job for {manga.internalId}</div>
|
||||
return <div key={"monitorMangaEntry." + manga.internalId} className="monitorMangaEntry">
|
||||
{HTMLFromIManga(manga)}
|
||||
{CoverCard(manga)}
|
||||
{job.id}
|
||||
<button id={job.id} onClick={DeleteJob}>Delete</button>
|
||||
</div>;
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { ChangeEventHandler, MouseEventHandler, useEffect, useState} from 'react';
|
||||
import React, {ChangeEventHandler, EventHandler, MouseEventHandler, useEffect, useState} from 'react';
|
||||
import {MangaConnector} from "./MangaConnector";
|
||||
import {Job} from "./Job";
|
||||
import IMangaConnector from "./interfaces/IMangaConnector";
|
||||
import {isValidUri} from "../App";
|
||||
import IManga, {HTMLFromIManga} from "./interfaces/IManga";
|
||||
import IManga, {SearchResult} from "./interfaces/IManga";
|
||||
import '../styles/search.css';
|
||||
import '../styles/MangaSearchResult.css'
|
||||
|
||||
export default function Search(){
|
||||
export default function Search({onJobsChanged} : {onJobsChanged: EventHandler<any>}) {
|
||||
const [mangaConnectors, setConnectors] = useState<IMangaConnector[]>();
|
||||
const [selectedConnector, setSelectedConnector] = useState<IMangaConnector>();
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<string>();
|
||||
@ -88,28 +89,25 @@ export default function Search(){
|
||||
|
||||
return (<div>
|
||||
<div id="SearchBox">
|
||||
<input type="text" placeholder="Manganame" onChange={searchBoxValueChanged}></input>
|
||||
<select value={selectedConnector === undefined ? "" : selectedConnector.name} onChange={selectedConnectorChanged}>
|
||||
<input type="text" placeholder="Manganame" id="Searchbox-Manganame" onChange={searchBoxValueChanged}></input>
|
||||
<select id="Searchbox-Connector" value={selectedConnector === undefined ? "" : selectedConnector.name} onChange={selectedConnectorChanged}>
|
||||
<option value="" disabled hidden>Select</option>
|
||||
{mangaConnectors === undefined
|
||||
? <option value="Loading">Loading</option>
|
||||
: mangaConnectors.map(con => <option value={con.name} key={con.name}>{con.name}</option>)}
|
||||
</select>
|
||||
<select onChange={changeSelectedLanguage} value={selectedLanguage === null ? "" : selectedLanguage}>
|
||||
<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>)}
|
||||
</select>
|
||||
<button type="submit" onClick={ExecuteSearch}>Search</button>
|
||||
<button id="Searchbox-button" type="submit" onClick={ExecuteSearch}>Search</button>
|
||||
</div>
|
||||
<div>
|
||||
<div id="SearchResults">
|
||||
{searchResults === undefined
|
||||
? <p>No Results yet</p>
|
||||
: searchResults.map(result => <div key={"searchResult."+result.internalId} className="searchResult">
|
||||
{HTMLFromIManga(result)}
|
||||
<button onClick={(e) => {Job.CreateJob(result.internalId, "MonitorManga", "03:00:00")}}>Monitor</button>
|
||||
</div>)}
|
||||
? <p></p>
|
||||
: searchResults.map(result => SearchResult(result, onJobsChanged))}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
import IMangaConnector from "./IMangaConnector";
|
||||
import KeyValuePair from "./KeyValuePair";
|
||||
import {Manga} from "../Manga";
|
||||
import {ReactElement} from "react";
|
||||
import React, {ChangeEventHandler, EventHandler, ReactElement} from "react";
|
||||
import {Job} from "../Job";
|
||||
import Icon from '@mdi/react';
|
||||
import { mdiTagTextOutline, mdiAccountEdit } from '@mdi/js';
|
||||
|
||||
export default interface IManga{
|
||||
"sortName": string,
|
||||
@ -36,7 +39,7 @@ function ReleaseStatusFromNumber(n: number): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
export function HTMLFromIManga(manga: IManga) : ReactElement {
|
||||
export function CoverCard(manga: IManga) : ReactElement {
|
||||
return(
|
||||
<div className="Manga" key={manga.internalId}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)}></img>
|
||||
@ -46,4 +49,23 @@ export function HTMLFromIManga(manga: IManga) : ReactElement {
|
||||
<p className="Manga-name">{manga.sortName}</p>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
export function SearchResult(manga: IManga, jobsChanged: EventHandler<any>) : ReactElement {
|
||||
return(
|
||||
<div className="SearchResult" key={manga.internalId}>
|
||||
<img src={Manga.GetMangaCoverUrl(manga.internalId)}></img>
|
||||
<p className="connector-name">{manga.mangaConnector.name}</p>
|
||||
<div className="Manga-status" release-status={ReleaseStatusFromNumber(manga.releaseStatus)}></div>
|
||||
<p className="Manga-name">{manga.sortName}</p>
|
||||
<ul className="Manga-tags">
|
||||
{manga.authors.map(author => <li className="Manga-author" key={manga.internalId + "-author-" + author}> <Icon path={mdiAccountEdit} size={0.5} /> {author}</li>)}
|
||||
{manga.tags.map(tag => <li className="Manga-tag" key={manga.internalId + "-tag-" + tag}><Icon path={mdiTagTextOutline} size={0.5} /> {tag}</li>)}
|
||||
</ul>
|
||||
<p className="Manga-description">{manga.description}</p>
|
||||
<button className="Manga-AddButton" onClick={(e) => {
|
||||
Job.CreateJob(manga.internalId, "MonitorManga", "03:00:00").then(() => jobsChanged(manga.internalId));
|
||||
}}>Monitor
|
||||
</button>
|
||||
</div>);
|
||||
}
|
88
Website/styles/MangaSearchResult.css
Normal file
88
Website/styles/MangaSearchResult.css
Normal file
@ -0,0 +1,88 @@
|
||||
.SearchResult {
|
||||
background-color: var(--second-background-color);
|
||||
border-radius: 2px;
|
||||
padding: 5px 5px 9px 5px;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
width: fit-content;
|
||||
height: 328px;
|
||||
display: grid;
|
||||
grid-template-columns: 220px 600px 50px;
|
||||
grid-template-rows: 40px 40px 200px auto;
|
||||
column-gap: 10px;
|
||||
grid-template-areas:
|
||||
"cover header header"
|
||||
"cover alltags alltags"
|
||||
"cover description description"
|
||||
"cover footer button";
|
||||
}
|
||||
|
||||
.SearchResult p {
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.SearchResult > img {
|
||||
grid-area: cover;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
border: 2px solid var(--primary-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.SearchResult > .connector-name {
|
||||
grid-area: cover;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
width: 100%;
|
||||
background-color: var(--accent-color);
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
text-align: center;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.SearchResult > .Manga-status {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.SearchResult > .Manga-name {
|
||||
grid-area: header;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.SearchResult > .Manga-tags {
|
||||
grid-area: alltags;
|
||||
color: white;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.SearchResult > ul > li {
|
||||
display: inline;
|
||||
margin: 0 2px;
|
||||
padding: 5px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.SearchResult .Manga-author {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.SearchResult .Manga-tag {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
.SearchResult > .Manga-description {
|
||||
grid-area: description;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.SearchResult > .Manga-AddButton {
|
||||
grid-area: button;
|
||||
}
|
@ -1,3 +1,48 @@
|
||||
.searchResult{
|
||||
background-color: var(--second-background-color);
|
||||
#SearchBox{
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#SearchResults {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
#SearchBox select, #SearchBox button, #SearchBox input {
|
||||
border-color: var(--accent-color);
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
border-bottom-width: 2px;
|
||||
border-top-width: 2px;
|
||||
padding: 2px 5px;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
#Searchbox-Manganame {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-top-left-radius: 2px;
|
||||
border-left-width: 2px;
|
||||
border-right-width: 0;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#Searchbox-connector {
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
#Searchbox-language {
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
#Searchbox-button {
|
||||
border-bottom-right-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
border-left-width: 0;
|
||||
border-right-width: 2px;
|
||||
width: 90px;
|
||||
}
|
48
package-lock.json
generated
48
package-lock.json
generated
@ -5,6 +5,8 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"devDependencies": {
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@types/react": "^18.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@ -403,6 +405,23 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@mdi/js": {
|
||||
"version": "7.4.47",
|
||||
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
|
||||
"integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@mdi/react": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@mdi/react/-/react-1.6.1.tgz",
|
||||
"integrity": "sha512-4qZeDcluDFGFTWkHs86VOlHkm6gnKaMql13/gpIcUQ8kzxHgpj31NuCkD8abECVfbULJ3shc7Yt4HJ6Wu6SN4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
||||
@ -760,6 +779,16 @@
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@ -796,6 +825,18 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
@ -823,6 +864,13 @@
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
|
||||
|
@ -1,5 +1,7 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@types/react": "^18.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
Loading…
Reference in New Issue
Block a user