maintain (admin): admin page upgrade

This commit is contained in:
Mickael Kerjean
2021-08-16 02:09:24 +10:00
parent 6e27086ac4
commit 44fc901b4b
13 changed files with 346 additions and 349 deletions

View File

@ -7,24 +7,20 @@ import React, { useState, useEffect } from "react";
* to realise it would be easier to write this simpler wrapper than migrate things over
*/
export function CSSTransition({ transitionName = "animate", children = null, transitionAppearTimeout = 300 }) {
const [child, setChildren] = useState(React.cloneElement(children, {
className: `${children.props.className} ${transitionName}-appear`
}));
const [className, setClassName] = useState(`${transitionName} ${transitionName}-appear`);
useEffect(() => {
setChildren(React.cloneElement(child, {
className: `${children.props.className} ${transitionName}-appear ${transitionName}-appear-active`
}))
const timeout = setTimeout(() => {
setChildren(React.cloneElement(child, {
className: `${children.props.className}`
}))
}, transitionAppearTimeout);
setClassName(`${transitionName} ${transitionName}-appear ${transitionName}-appear-active`)
return () => {
clearTimeout(timeout);
};
const timeout = setTimeout(() => {
setClassName(`${transitionName}`)
}, transitionAppearTimeout);
return () => clearTimeout(timeout);
}, []);
return child;
return (
<div className={className}>
{ children }
</div>
)
}

View File

@ -22,3 +22,4 @@ export { MapShot } from "./mapshot";
export { LoggedInOnly, ErrorPage, LoadingPage } from "./decorator";
export { FormBuilder } from "./formbuilder";
export { UploadQueue } from "./upload_queue";
export { CSSTransition } from "./animation";

View File

@ -1,9 +1,57 @@
import React from "react";
import React, { useRef, useState, useLayoutEffect } from "react";
import PropTypes from "prop-types";
import "./textarea.scss";
export class Textarea extends React.Component {
export function Textarea({ ...props }) {
const $el = useRef();
const [className, setClassName] = useState(
"component_textarea"
+ (/Firefox/.test(navigator.userAgent) ? " firefox" : "")
+ (props.value && props.value.length > 0 ? " hasText" : "")
);
useLayoutEffect(() => {
if($el.current && $el.current.value.length > 0 && className.indexOf("hasText") === -1){
setClassName(`${className} hasText`)
}
}, []);
const disabledEnter = (e) => {
if(e.key === "Enter" && e.shiftKey === false){
e.preventDefault();
const $form = getForm($el.current.ref);
if($form){
$form.dispatchEvent(new Event("submit", { cancelable: true }));
}
}
function getForm($el){
if(!$el.parentElement) return $el;
if($el.parentElement.nodeName == "FORM"){
return $el.parentElement;
}
return getForm($el.parentElement);
}
};
const inputProps = (p) => {
return Object.keys(p).reduce((acc, key) => {
if(key === "disabledEnter") return acc;
acc[key] = p[key];
return acc;
}, {});
};
return (
<textarea
onKeyPress={disabledEnter}
{...inputProps(props)}
className={className}
ref={$el}>
</textarea>
)
}
export class Textarea2 extends React.Component {
constructor(props){
super(props);
}

View File

@ -15,7 +15,7 @@ window.addEventListener("DOMContentLoaded", () => {
const $loader = document.querySelector("#n-lder");
function render(){
ReactDOM.render(<Router/>, document.querySelector("div[role='main']"));
ReactDOM.render(<React.StrictMode><Router/></React.StrictMode>, document.querySelector("div[role='main']"));
return Promise.resolve();
};
function waitFor(n){

View File

@ -1,118 +1,96 @@
import React from 'react';
import Path from 'path';
import { Route, Switch, Link, NavLink } from 'react-router-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './error.scss';
import './adminpage.scss';
import { Icon, LoadingPage } from '../components/';
import { Config, Admin } from '../model';
import { notify } from '../helpers/';
import { HomePage, BackendPage, SettingsPage, LogPage, SetupPage, LoginPage } from './adminpage/';
import { t } from '../locales/';
import React, { useState, useEffect } from "react";
import Path from "path";
import { Route, Switch, Link, NavLink, useRouteMatch } from "react-router-dom";
import "./error.scss";
import "./adminpage.scss";
import { Icon, LoadingPage, CSSTransition } from "../components/";
import { Config, Admin } from "../model";
import { notify } from "../helpers/";
import { HomePage, BackendPage, SettingsPage, LogPage, SetupPage, LoginPage } from "./adminpage/";
import { t } from "../locales/";
function AdminOnly(WrappedComponent){
return class extends React.Component {
constructor(props){
super(props);
this.state = {
isAdmin: null
};
this.admin = () => {
Admin.isAdmin().then((t) => {
this.setState({isAdmin: t});
}).catch((err) => {
notify.send("Error: " + (err && err.message) , "error");
});
};
this.timeout = window.setInterval(this.admin.bind(this), 30 * 1000);
let initIsAdmin = null;
return function(props) {
const [isAdmin, setIsAdmin] = useState(initIsAdmin);
const refresh = () => {
Admin.isAdmin().then((t) => {
initIsAdmin = t
setIsAdmin(t)
}).catch((err) => {
notify.send("Error: " + (err && err.message) , "error");
});
}
useEffect(() => {
refresh()
const timeout = window.setInterval(refresh, 5 * 1000);
return () => clearInterval(timeout);
}, []);
if(isAdmin === true) {
return ( <WrappedComponent {...props} /> );
} else if(isAdmin === false) {
return ( <LoginPage reload={refresh} /> );
}
componentDidMount(){
this.admin.call(this);
}
componentWillUnmount(){
window.clearInterval(this.timeout);
}
render(){
if(this.state.isAdmin === true){
return ( <WrappedComponent {...this.props} /> );
} else if(this.state.isAdmin === false) {
return ( <LoginPage reload={() => this.admin()} /> );
}
return ( <LoadingPage />);
}
};
return ( <LoadingPage /> );
}
}
@AdminOnly
export class AdminPage extends React.Component {
constructor(props){
super(props);
this.state = {
isAdmin: null,
isSaving: false
};
}
isSaving(yesOrNo){
this.setState({isSaving: yesOrNo});
}
render(){
return (
<div className="component_page_admin">
<SideMenu url={this.props.match.url} isLoading={this.state.isSaving}/>
<div className="page_container scroll-y">
<ReactCSSTransitionGroup key={window.location.pathname} transitionName="adminpage" transitionLeave={true} transitionEnter={true} transitionLeaveTimeout={15000} transitionEnterTimeout={20000} transitionAppear={true} transitionAppearTimeout={20000}>
<Switch>
<Route path={this.props.match.url + "/backend"} render={()=><BackendPage isSaving={this.isSaving.bind(this)}/>} />
<Route path={this.props.match.url + "/settings"} render={()=><SettingsPage isSaving={this.isSaving.bind(this)}/>} />
<Route path={this.props.match.url + "/logs"} render={() =><LogPage isSaving={this.isSaving.bind(this)}/>} />
<Route path={this.props.match.url + "/setup"} component={SetupPage} />
<Route path={this.props.match.url} component={HomePage} />
</Switch>
</ReactCSSTransitionGroup>
</div>
export default AdminOnly((props) => {
const match = useRouteMatch();
const [isSaving, setIsSaving] = useState(false);
return (
<div className="component_page_admin">
<SideMenu url={match.url} isLoading={isSaving}/>
<div className="page_container scroll-y">
<CSSTransition key={location.pathname} transitionName="adminpage" transitionAppearTimeout={30000}>
<Switch>
<Route path={match.url + "/backend"} render={()=><BackendPage isSaving={setIsSaving}/>} />
<Route path={match.url + "/settings"} render={()=><SettingsPage isSaving={setIsSaving}/>} />
<Route path={match.url + "/logs"} render={() =><LogPage isSaving={setIsSaving}/>} />
<Route path={match.url + "/setup"} component={SetupPage} />
<Route path={match.url} component={HomePage} />
</Switch>
</CSSTransition>
</div>
);
}
}
</div>
);
});
const SideMenu = (props) => {
function SideMenu(props) {
return (
<div className="component_menu_sidebar no-select">
{ props.isLoading ?
<div className="header">
<Icon name="arrow_left" style={{"opacity": 0}}/>
<Icon name="loading" />
</div> :
<NavLink to="/" className="header">
<Icon name="arrow_left" />
<img src="/assets/logo/android-chrome-512x512.png" />
</NavLink>
}
<h2>{ t("Admin console") }</h2>
<ul>
<li>
<NavLink activeClassName="active" to={props.url + "/backend"}>
{ t("Backend") }
{ props.isLoading ?
<div className="header">
<Icon name="arrow_left" style={{"opacity": 0}}/>
<Icon name="loading" />
</div> :
<NavLink to="/" className="header">
<Icon name="arrow_left" />
<img src="/assets/logo/android-chrome-512x512.png" />
</NavLink>
</li>
<li>
<NavLink activeClassName="active" to={props.url + "/settings"}>
{ t("Settings") }
</NavLink>
</li>
<li>
<NavLink activeClassName="active" to={props.url + "/logs"}>
{ t("Logs") }
</NavLink>
</li>
</ul>
}
<h2>{ t("Admin console") }</h2>
<ul>
<li>
<NavLink activeClassName="active" to={props.url + "/backend"}>
{ t("Backend") }
</NavLink>
</li>
<li>
<NavLink activeClassName="active" to={props.url + "/settings"}>
{ t("Settings") }
</NavLink>
</li>
<li>
<NavLink activeClassName="active" to={props.url + "/logs"}>
{ t("Logs") }
</NavLink>
</li>
</ul>
</div>
);
};

View File

@ -1,8 +1,8 @@
import React from 'react';
import React from "react";
import { FormBuilder, Icon, Input, Alert } from "../../components/";
import { Backend, Config } from "../../model/";
import { FormObjToJSON, notify, format, createFormBackend } from "../../helpers/";
import { t } from '../../locales/';
import { t } from "../../locales/";
import "./backend.scss";
@ -26,7 +26,7 @@ export class BackendPage extends React.Component {
let [backend, config] = data;
this.setState({
backend_available: backend,
backend_enabled: window.CONFIG["connections"].map((conn) => {
backend_enabled: window.CONFIG["connections"].filter((b) => b).map((conn) => {
return createFormBackend(backend, conn);
}),
config: config
@ -56,12 +56,12 @@ export class BackendPage extends React.Component {
return Config.save(json, true, () => {
this.props.isSaving(false);
}, (err) => {
notify.send(err && err.message || t('Oops'), 'error');
notify.send(err && err.message || t("Oops"), "error");
this.props.isSaving(false);
});
}
addBackend(backend_id){
addBackend(backend_id){
this.setState({
backend_enabled: this.state.backend_enabled.concat(
createFormBackend(this.state.backend_available, {
@ -105,7 +105,7 @@ export class BackendPage extends React.Component {
const isActiveBackend = (backend_key) => {
return this.state.backend_enabled
.map((b) => Object.keys(b)[0])
.indexOf(backend_key) !== -1;
.indexOf(backend_key) !== -1;
};
const isActiveAuth = (auth_key) => {
@ -134,7 +134,7 @@ export class BackendPage extends React.Component {
</div>
<h2>Authentication Middleware</h2>
<Alert>
Integrate Filestash with your identity management system
</Alert>
@ -163,8 +163,6 @@ export class BackendPage extends React.Component {
<Alert className="success">
<i><strong>Register your interest: <a href={`mailto:mickael@kerjean.me?Subject=Filestash - Authentication Middleware - ${this.state.auth_enabled}`}>mickael@kerjean.me</a></strong></i>
</Alert>
</React.Fragment>
)
}
@ -181,14 +179,14 @@ export class BackendPage extends React.Component {
<div className="icons no-select" onClick={this.removeBackend.bind(this, index)}>
<Icon name="delete" />
</div>
<FormBuilder onChange={this.onChange.bind(this)}
idx={index}
<FormBuilder onChange={this.onChange.bind(this)}
idx={index}
key={index}
form={{"": backend_enable}}
autoComplete="new-password"
render={ ($input, props, struct, onChange) => {
let $checkbox = (
<Input type="checkbox" style={{width: "inherit", marginRight: '6px', top: '6px'}}
<Input type="checkbox" style={{width: "inherit", marginRight: "6px", top: "6px"}}
checked={enable(struct)} onChange={(e) => onChange(update.bind(this, e.target.checked))}/>
);
if(struct.label === "label"){
@ -203,13 +201,13 @@ export class BackendPage extends React.Component {
{ $checkbox }
{ format(struct.label) }:
</span>
<div style={{width: '100%'}}>
<div style={{width: "100%"}}>
{ $input }
</div>
</div>
<div>
<span className="nothing"></span>
<div style={{width: '100%'}}>
<div style={{width: "100%"}}>
{
struct.description ? (<div className="description">{struct.description}</div>) : null
}
@ -224,7 +222,7 @@ export class BackendPage extends React.Component {
}
</form>
</div> : <Alert>You need to enable a backend first.</Alert>
}
}
</div>
);
}

View File

@ -1,5 +1,4 @@
.component_dashboard{
.alert { margin-top: -15px; }
.box-container {
display: flex;
@ -41,6 +40,7 @@
text-shadow: none;
background: var(--emphasis-primary);
padding: 18px 0;
@media (max-width: 750px){ padding: 8px 0; }
margin: 6px;
opacity: 0.95;
.icon{

View File

@ -1,15 +1,6 @@
import React from 'react';
import { Redirect } from 'react-router-dom';
import React from "react";
import { Redirect } from "react-router-dom";
export class HomePage extends React.Component {
constructor(props){
super(props);
this.state = {
stage: "loading"
}
}
render(){
return ( <Redirect to="/admin/backend" /> );
}
export function HomePage() {
return ( <Redirect to="/admin/backend" /> );
}

View File

@ -1,91 +1,86 @@
import React from 'react';
import { FormBuilder, Loader, Button, Icon } from '../../components/';
import { Config, Log } from '../../model/';
import { FormObjToJSON, notify, format } from '../../helpers/';
import { t } from '../../locales/';
import React, { useState, useEffect, useRef } from "react";
import { FormBuilder, Loader, Button, Icon } from "../../components/";
import { Config, Log } from "../../model/";
import { FormObjToJSON, notify, format, nop } from "../../helpers/";
import { t } from "../../locales/";
import "./logger.scss";
export class LogPage extends React.Component {
constructor(props){
super(props);
this.state = {
form: {},
log: "",
config: {}
};
}
componentDidMount(){
Config.all().then((config) => {
this.setState({
form: {"":{"params":config["log"]}},
config: FormObjToJSON(config)
});
});
Log.get(1024*100).then((log) => { // get only the last 100kb of log
this.setState({log: log}, () => {
this.refs.$log.scrollTop = this.refs.$log.scrollHeight;
});
});
}
onChange(r){
this.state.config["log"] = r[""].params;
this.state.config["connections"] = window.CONFIG.connections;
this.props.isSaving(true);
Config.save(this.state.config, true, () => {
this.props.isSaving(false);
export function LogPage({ isSaving = nop }) {
const [log, setLog] = useState("");
const [form, setForm] = useState({});
const [config, setConfig] = useState({});
const $log = useRef();
const filename = () => {
const t = new Date().toISOString().substring(0,10).replace(/-/g, "");
return `access_${t}.log`;
};
const onChange = (r) => {
const c = Object.assign({}, config)
c["log"] = r[""]["params"];
c["connections"] = window.CONFIG.connections;
delete c["constant"]
isSaving(true);
Config.save(c, true, () => {
isSaving(false);
}, () => {
notify.send(err && err.message || t('Oops'), 'error');
this.props.isSaving(false);
isSaving(false);
notify.send(err && err.message || t("Oops"), "error");
});
}
};
const fetchLogs = () => {
Log.get(1024*100).then((log) => { // get only the last 100kb of log
setLog(log + "\n\n\n\n\n");
if($log.current.scrollTop === 0) {
$log.current.scrollTop = $log.current.scrollHeight;
}
});
};
render(){
const filename = () => {
let tmp = "access_";
tmp += new Date().toISOString().substring(0,10).replace(/-/g, "");
tmp += ".log";
};
return (
<div className="component_logpage">
<h2>Logging</h2>
<div style={{minHeight: '150px'}}>
<FormBuilder form={this.state.form} onChange={this.onChange.bind(this)}
render={ ($input, props, struct, onChange) => {
return (
<label className={"no-select input_type_" + props.params["type"]}>
<div>
<span>
{ format(struct.label) }:
</span>
<div style={{width: '100%'}}>
{ $input }
</div>
</div>
<div>
<span className="nothing"></span>
<div style={{width: '100%'}}>
{
struct.description ? (<div className="description">{struct.description}</div>) : null
}
</div>
</div>
</label>
);
}} />
</div>
useEffect(() => {
Config.all().then((config) => {
setForm({"":{"params":config["log"]}});
setConfig(FormObjToJSON(config));
});
fetchLogs();
const id = setInterval(fetchLogs, 5000);
return () => clearInterval(id)
}, []);
<pre style={{height: '350px'}} ref="$log">
{
this.state.log === "" ? <Loader/> : this.state.log + "\n\n\n\n\n"
}
</pre>
<div>
<a href={Log.url()} download={filename()}><Button className="primary">{ t("Download") }</Button></a>
</div>
return (
<div className="component_logpage">
<h2>Logging</h2>
<div style={{minHeight: "150px"}}>
<FormBuilder
form={form}
onChange={onChange}
render={ ($input, props, struct, onChange) => (
<label className={"no-select input_type_" + props.params["type"]}>
<div>
<span>
{ format(struct.label) }:
</span>
<div style={{width: "100%"}}>
{ $input }
</div>
</div>
<div>
<span className="nothing"></span>
<div style={{width: "100%"}}>
{
struct.description ? (<div className="description">{struct.description}</div>) : null
}
</div>
</div>
</label>
)} />
</div>
);
}
<pre style={{height: "350px"}} ref={$log}>
{ log === "" ? <Loader/> : log }
</pre>
<div>
<a href={Log.url()} download={filename()}><Button className="primary">{ t("Download") }</Button></a>
</div>
</div>
);
}

View File

@ -1,54 +1,44 @@
import React from 'react';
import { Redirect } from 'react-router';
import React, { useState, useRef } from "react";
import { Redirect } from "react-router";
import { Input, Button, Container, Icon, Loader } from '../../components/';
import { Config, Admin } from '../../model/';
import { t } from '../../locales/';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { Input, Button, Container, Icon } from "../../components/";
import { Admin } from "../../model/";
import { nop } from "../../helpers/";
import { t } from "../../locales/";
export class LoginPage extends React.Component {
constructor(props){
super(props);
this.state = {
loading: false,
error: null
};
}
componentDidMount(){
this.refs.$input.ref.focus();
}
authenticate(e){
export function LoginPage({ reload = nop }) {
const [isLoading, setIsLoading] = useState(false);
const [hasError, setHasError] = useState(false);
const $input = useRef();
const marginTop = () => ({ marginTop: `${parseInt(window.innerHeight / 3)}px` })
const authenticate = (e) => {
e.preventDefault();
this.setState({loading: true});
Admin.login(this.refs.$input.ref.value)
.then(() => this.props.reload())
.catch(() => {
this.refs.$input.ref.value = "";
this.setState({
loading: false,
error: true
}, () => {
window.setTimeout(() => {
this.setState({error: false});
}, 500);
});
setIsLoading(true);
Admin.login($input.current.ref.value)
.then(() => reload())
.catch(() => {
$input.current.ref.value = "";
setIsLoading(false)
setHasError(true);
setTimeout(() => {
setHasError(false);
}, 500);
});
}
render(){
const marginTop = () => { return {marginTop: parseInt(window.innerHeight / 3)+'px'};};
useRef(() => {
$input.current.ref.focus();
}, []);
return (
<Container maxWidth="300px" className="sharepage_component">
<form className={this.state.error ? "error" : ""} onSubmit={this.authenticate.bind(this)} style={marginTop()}>
<Input ref="$input" type="password" placeholder={ t("Password") } />
return (
<Container maxWidth="300px" className="sharepage_component">
<form className={hasError ? "error" : ""} onSubmit={authenticate} style={marginTop()}>
<Input ref={$input} type="password" placeholder={ t("Password") } />
<Button theme="transparent">
<Icon name={this.state.loading ? "loading" : "arrow_right"}/>
<Icon name={isLoading ? "loading" : "arrow_right"}/>
</Button>
</form>
</Container>
);
}
</form>
</Container>
)
}

View File

@ -1,26 +1,12 @@
import React from 'react';
import { FormBuilder } from '../../components/';
import { Config } from '../../model/';
import { format, notify } from '../../helpers';
import { t } from '../../locales/';
import React, { useState, useEffect } from "react";
import { FormBuilder } from "../../components/";
import { Config } from "../../model/";
import { format, notify, nop } from "../../helpers";
import { t } from "../../locales/";
export class SettingsPage extends React.Component {
constructor(props){
super(props);
this.state = {
form: {}
};
}
componentDidMount(){
Config.all().then((c) => {
delete c.constant; // The constant key contains read only global variable that are
// application wide truth => not editable from the admin area
this.setState({form: c});
});
}
format(name){
export function SettingsPage({ isSaving = nop }) {
const [form, setForm] = useState({});
const format = (name) => {
if(typeof name !== "string"){
return "N/A";
}
@ -34,45 +20,52 @@ export class SettingsPage extends React.Component {
})
.join(" ");
}
onChange(form){
form.connections = window.CONFIG.connections;
this.props.isSaving(true);
Config.save(form, true, () => {
this.props.isSaving(false);
const onChange = (_form) => {
_form.connections = window.CONFIG.connections;
delete _form.constant;
refresh(Math.random())
isSaving(true)
Config.save(_form, true, () => {
isSaving(false)
}, (err) => {
notify.send(err && err.message || t('Oops'), 'error');
this.props.isSaving(false);
isSaving(false)
notify.send(err && err.message || t("Oops"), "error");
});
}
const [_, refresh] = useState(null);
render(){
return (
<form className="sticky">
<FormBuilder form={this.state.form}
onChange={this.onChange.bind(this)}
autoComplete="new-password"
render={ ($input, props, struct, onChange) => {
return (
<label className={"no-select input_type_" + props.params["type"]}>
<div>
useEffect(() => {
Config.all().then((c) => {
delete c.constant; // The constant key contains read only global variable that are
// application wide truth => not editable from the admin area
setForm(c)
});
}, []);
return (
<form className="sticky">
<FormBuilder
form={form}
onChange={onChange}
autoComplete="new-password"
render={ ($input, props, struct, onChange) => (
<label className={"no-select input_type_" + props.params["type"]}>
<div>
<span>
{ format(struct.label) }:
{ format(struct.label) }:
</span>
<div style={{width: '100%'}}>
{ $input }
<div style={{width: "100%"}}>
{ $input }
</div>
</div>
<div>
</div>
<div>
<span className="nothing"></span>
<div style={{width: '100%'}}>
{ struct.description ? (<div className="description">{struct.description}</div>) : null }
<div style={{width: "100%"}}>
{ struct.description ? (<div className="description">{struct.description}</div>) : null }
</div>
</div>
</label>
);
}}/>
</div>
</label>
)} />
</form>
);
}
);
}

View File

@ -1,10 +1,10 @@
import React from 'react';
import React, { createRef } from "react";
import { Input, Button, Container, Icon, NgIf, Loader } from '../../components/';
import { Config, Admin } from '../../model/';
import { notify, FormObjToJSON, alert, prompt } from '../../helpers';
import { bcrypt_password } from '../../helpers/bcrypt';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { Input, Button, Container, Icon, NgIf, Loader, CSSTransition } from "../../components/";
import { Config, Admin } from "../../model/";
import { notify, FormObjToJSON, alert, prompt } from "../../helpers";
import { bcrypt_password } from "../../helpers/bcrypt";
//import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import "./setup.scss";
@ -28,12 +28,12 @@ export class SetupPage extends React.Component {
this.unlisten();
alert.now((
<div>
<p style={{textAlign: 'justify'}}>
<p style={{textAlign: "justify"}}>
Help making this software better by sending crash reports and anonymous usage statistics
</p>
<form onSubmit={start.bind(this)} style={{fontSize: '0.9em', marginTop: '10px'}}>
<form onSubmit={start.bind(this)} style={{fontSize: "0.9em", marginTop: "10px"}}>
<label>
<Input type="checkbox" style={{width: 'inherit', marginRight: '10px'}} onChange={(e) => this.enableLog(e.target.checked)} defaultChecked={config.log.telemetry.value} />
<Input type="checkbox" style={{width: "inherit", marginRight: "10px"}} onChange={(e) => this.enableLog(e.target.checked)} defaultChecked={config.log.telemetry.value} />
I accept but the data is not to be share with any third party
</label>
</form>
@ -134,6 +134,7 @@ class MultiStepForm extends React.Component {
has_answered_password: false,
deps: []
};
this.$input = createRef()
}
componentDidMount(){
@ -166,17 +167,17 @@ class MultiStepForm extends React.Component {
<FormStage navleft={false} navright={this.state.has_answered_password === true} current={this.state.current} onStepChange={this.onStepChange.bind(this)}>
Admin Password
</FormStage>
<ReactCSSTransitionGroup transitionName="stepper-form" transitionEnterTimeout={600} transitionAppearTimeout={600} transitionAppear={true} transitionEnter={true} transitionLeave={false}>
<CSSTransition transitionName="stepper-form" transitionEnterTimeout={600} transitionAppearTimeout={600} transitionAppear={true} transitionEnter={true} transitionLeave={false}>
<div key={this.state.current}>
<p>Create your instance admin password: </p>
<form onSubmit={this.onAdminPassword.bind(this)}>
<Input ref="$input" type="password" placeholder="Password" value={this.state.answer_password} onChange={(e) => this.setState({answer_password: e.target.value})}/>
<Input ref={this.$input} type="password" placeholder="Password" value={this.state.answer_password} onChange={(e) => this.setState({answer_password: e.target.value})}/>
<Button theme="transparent">
<Icon name={this.props.loading ? "loading" : "arrow_right"}/>
</Button>
</form>
</div>
</ReactCSSTransitionGroup>
</CSSTransition>
{hideMenu}
</div>
);
@ -186,7 +187,7 @@ class MultiStepForm extends React.Component {
<FormStage navleft={true} navright={false} current={this.state.current} onStepChange={this.onStepChange.bind(this)}>
Summary
</FormStage>
<ReactCSSTransitionGroup transitionName="stepper-form" transitionEnterTimeout={600} transitionAppearTimeout={600} transitionAppear={true} transitionEnter={true} transitionLeave={false}>
<CSSTransition transitionName="stepper-form" transitionEnterTimeout={600} transitionAppearTimeout={600} transitionAppear={true} transitionEnter={true} transitionLeave={false}>
<div key={this.state.current}>
<NgIf cond={!!this.props.loading}>
<Loader/>
@ -204,7 +205,7 @@ class MultiStepForm extends React.Component {
}
</NgIf>
</div>
</ReactCSSTransitionGroup>
</CSSTransition>
</div>
);
}

View File

@ -1,13 +1,15 @@
import React from "react";
import React, { Suspense } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { NotFoundPage, ConnectPage, HomePage, SharePage, LogoutPage, FilesPage, ViewerPage } from "./pages/";
import { URL_HOME, URL_FILES, URL_VIEWER, URL_LOGIN, URL_LOGOUT, URL_ADMIN, URL_SHARE } from "./helpers/";
import { Bundle, ModalPrompt, ModalAlert, ModalConfirm, Notification, UploadQueue } from "./components/";
import { ModalPrompt, ModalAlert, ModalConfirm, Notification, UploadQueue, LoadingPage } from "./components/";
const AdminPage = (props) => (
<Bundle loader={import(/* webpackChunkName: "admin" */"./pages/adminpage")} symbol="AdminPage">
{(Comp) => <Comp {...props}/>}
</Bundle>
const LazyAdminPage = React.lazy(() => import(/* webpackChunkName: "admin" */"./pages/adminpage"));
const AdminPage = () => (
<Suspense fallback={<LoadingPage/>}>
<LazyAdminPage/>
</Suspense>
);
export default class AppRouter extends React.Component {
@ -26,8 +28,12 @@ export default class AppRouter extends React.Component {
<Route component={NotFoundPage} />
</Switch>
</BrowserRouter>
{
/*
<ModalPrompt /> <ModalAlert /> <ModalConfirm />
<Notification /> <UploadQueue/>
*/
}
</div>
);
}