mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-02 20:23:32 +08:00
maintain (admin): admin page upgrade
This commit is contained in:
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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" /> );
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user