📑 Added a AirDCPP Settings form WIP
This commit is contained in:
@@ -10,6 +10,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8050:8050"
|
- "8050:8050"
|
||||||
- "3050:3050"
|
- "3050:3050"
|
||||||
|
- "8051:8051"
|
||||||
depends_on:
|
depends_on:
|
||||||
- rabbitmq
|
- rabbitmq
|
||||||
- nginx
|
- nginx
|
||||||
|
|||||||
54
src/client/components/AirDCPPSettingsForm.tsx
Normal file
54
src/client/components/AirDCPPSettingsForm.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import React, { ReactElement } from "react";
|
||||||
|
import { Form, Field } from "react-final-form";
|
||||||
|
|
||||||
|
export const AirDCPPSettingsForm = (): ReactElement => {
|
||||||
|
const onSubmit = () => {};
|
||||||
|
const validate = async () => {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
validate={validate}
|
||||||
|
render={({ handleSubmit }) => (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<h2>Simple Default Input</h2>
|
||||||
|
<div>
|
||||||
|
<label>First Name</label>
|
||||||
|
<Field
|
||||||
|
name="firstName"
|
||||||
|
component="input"
|
||||||
|
placeholder="First Name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Render Function</h2>
|
||||||
|
<Field
|
||||||
|
name="bio"
|
||||||
|
render={({ input, meta }) => (
|
||||||
|
<div>
|
||||||
|
<label>Bio</label>
|
||||||
|
<textarea {...input} />
|
||||||
|
{meta.touched && meta.error && <span>{meta.error}</span>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>Render Function as Children</h2>
|
||||||
|
<Field name="phone">
|
||||||
|
{({ input, meta }) => (
|
||||||
|
<div>
|
||||||
|
<label>Phone</label>
|
||||||
|
<input type="text" {...input} placeholder="Phone" />
|
||||||
|
{meta.touched && meta.error && <span>{meta.error}</span>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AirDCPPSettingsForm;
|
||||||
@@ -260,9 +260,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
{!isEmpty(extractedComicBookArchive) ? (
|
{!isEmpty(extractedComicBookArchive) ? (
|
||||||
<DnD data={extractedComicBookArchive} />
|
<DnD data={extractedComicBookArchive} />
|
||||||
) : (
|
) : null}
|
||||||
"..."
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{!isEmpty(extractedComicBookArchive) ? (
|
{!isEmpty(extractedComicBookArchive) ? (
|
||||||
<div className="column mt-5">
|
<div className="column mt-5">
|
||||||
|
|||||||
@@ -1,52 +1,71 @@
|
|||||||
import React, { useState, useEffect, useCallback, ReactElement } from "react";
|
import React, { useState, useEffect, useCallback, ReactElement } from "react";
|
||||||
|
import { AirDCPPSettingsForm } from "./AirDCPPSettingsForm";
|
||||||
|
import settingsObject from "../constants/settings/settingsMenu.json";
|
||||||
|
import { isUndefined, map } from "lodash";
|
||||||
|
|
||||||
interface ISettingsProps {}
|
interface ISettingsProps {}
|
||||||
|
|
||||||
export const Settings = (props: ISettingsProps): ReactElement => {
|
export const Settings = (props: ISettingsProps): ReactElement => {
|
||||||
|
const [active, setActive] = useState("gen-db");
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<section className="container">
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="section column is-one-quarter">
|
<div className="section column is-one-quarter">
|
||||||
<h1 className="title">Settings</h1>
|
<h1 className="title">Settings</h1>
|
||||||
<aside className="menu">
|
<aside className="menu">
|
||||||
<p className="menu-label">General</p>
|
{map(settingsObject, (settingObject, idx) => {
|
||||||
<ul className="menu-list">
|
return (
|
||||||
<li>
|
<>
|
||||||
<a>Dashboard</a>
|
<p className="menu-label" key={idx}>
|
||||||
</li>
|
{settingObject.category}
|
||||||
<li>
|
</p>
|
||||||
<a>Global Search</a>
|
{/* First level children */}
|
||||||
</li>
|
{!isUndefined(settingObject.children) ? (
|
||||||
</ul>
|
<ul className="menu-list" key={settingObject.id}>
|
||||||
<p className="menu-label">Acquisition</p>
|
{map(settingObject.children, (item, idx) => {
|
||||||
<ul className="menu-list">
|
return (
|
||||||
<li>
|
<li key={item.id}>
|
||||||
<a className="is-active">AirDC++</a>
|
<a
|
||||||
<ul>
|
className={
|
||||||
<li>
|
item.id.toString() === active ? "is-active" : ""
|
||||||
<a>Connection</a>
|
}
|
||||||
</li>
|
onClick={() => setActive(item.id.toString())}
|
||||||
<li>
|
>
|
||||||
<a>Hubs</a>
|
{item.displayName}
|
||||||
</li>
|
</a>
|
||||||
<li>
|
{/* Second level children */}
|
||||||
<a>Additional Configuration</a>
|
{!isUndefined(item.children) ? (
|
||||||
</li>
|
<ul>
|
||||||
</ul>
|
{map(item.children, (item, idx) => (
|
||||||
</li>
|
<li key={item.id}>
|
||||||
</ul>
|
<a
|
||||||
<p className="menu-label">ComicVine</p>
|
className={
|
||||||
<ul className="menu-list">
|
item.id.toString() === active
|
||||||
<li>
|
? "is-active"
|
||||||
<a>API</a>
|
: ""
|
||||||
</li>
|
}
|
||||||
<li>
|
onClick={() =>
|
||||||
<a>Configuration</a>
|
setActive(item.id.toString())
|
||||||
</li>
|
}
|
||||||
</ul>
|
>
|
||||||
|
{item.displayName}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : null}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AirDCPPSettingsForm />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
58
src/client/constants/settings/settingsMenu.json
Normal file
58
src/client/constants/settings/settingsMenu.json
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"category": "general",
|
||||||
|
"displayName": "General",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "gen-db",
|
||||||
|
"displayName": "Dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gen-gls",
|
||||||
|
"displayName": "Global Search"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"category": "acquisition",
|
||||||
|
"displayName": "Acquisition",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "adc",
|
||||||
|
"displayName": "AirDC++",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "adc-connection",
|
||||||
|
"displayName": "Connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "adc-hubs",
|
||||||
|
"displayName": "Hubs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "adc-add-config",
|
||||||
|
"displayName": "Additional Configuration"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"category": "comicvine",
|
||||||
|
"displayName": "Comic Vine",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "api",
|
||||||
|
"displayName": "API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "options",
|
||||||
|
"displayName": "Options"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -20,3 +20,35 @@ const changes = (object, base) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type TraverseFunction<T> = (
|
||||||
|
obj: T,
|
||||||
|
prop: string,
|
||||||
|
value: unknown,
|
||||||
|
scope: string[],
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep diff between two object, using lodash
|
||||||
|
* @param {Object} object Object to traverse
|
||||||
|
* @param {Object} fn Callback function
|
||||||
|
* @return {Object} Return a new object who represent the diff
|
||||||
|
*/
|
||||||
|
export const traverseObject = <T = Record<string, unknown>>(
|
||||||
|
object: T,
|
||||||
|
fn: TraverseFunction<T>,
|
||||||
|
): void => traverseInternal(object, fn, []);
|
||||||
|
|
||||||
|
const traverseInternal = <T = Record<string, unknown>>(
|
||||||
|
object: T,
|
||||||
|
fn: TraverseFunction<T>,
|
||||||
|
scope: string[] = [],
|
||||||
|
): void => {
|
||||||
|
Object.entries(object).forEach(([key, value]) => {
|
||||||
|
fn.apply(this, [object, key, value, scope]);
|
||||||
|
if (value !== null && typeof value === "object") {
|
||||||
|
traverseInternal(value, fn, scope.concat(key));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user