Merged in OS-158-Installation (pull request #136)
OS-158 Installation Frontend
This commit is contained in:
commit
d14873d85f
|
@ -5,15 +5,10 @@ module.exports = {
|
||||||
'serverport': 3000,
|
'serverport': 3000,
|
||||||
|
|
||||||
'scripts': {
|
'scripts': {
|
||||||
'src': './src/**/*.js',
|
'src': './src/*.js',
|
||||||
'dest': './build/js/'
|
'dest': './build/js/'
|
||||||
},
|
},
|
||||||
|
|
||||||
'phpserver': {
|
|
||||||
'base': './src/server/',
|
|
||||||
'port': 8000
|
|
||||||
},
|
|
||||||
|
|
||||||
'images': {
|
'images': {
|
||||||
'src': './src/assets/images/**/*.{jpeg,jpg,png}',
|
'src': './src/assets/images/**/*.{jpeg,jpg,png}',
|
||||||
'dest': './build/images/'
|
'dest': './build/images/'
|
||||||
|
|
|
@ -66,4 +66,10 @@ gulp.task('browserify', function() {
|
||||||
// Only run watchify if NOT production
|
// Only run watchify if NOT production
|
||||||
return buildScript('index.js', !global.isProd);
|
return buildScript('index.js', !global.isProd);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('config', function() {
|
||||||
|
|
||||||
|
return gulp.src(config.sourceDir + 'config.js')
|
||||||
|
.pipe(gulp.dest(config.scripts.dest))
|
||||||
});
|
});
|
|
@ -10,6 +10,6 @@ gulp.task('dev', ['clean'], function(callback) {
|
||||||
global.isProd = false;
|
global.isProd = false;
|
||||||
|
|
||||||
// Run all tasks once
|
// Run all tasks once
|
||||||
return runSequence(['sass', 'imagemin', 'browserify', 'copyFonts', 'copyIndex', 'copyIcons', 'serverphp', 'fonts'], 'watch', callback);
|
return runSequence(['sass', 'imagemin', 'browserify', 'copyFonts', 'copyIndex', 'copyIcons', 'config', 'fonts'], 'watch', callback);
|
||||||
|
|
||||||
});
|
});
|
|
@ -1,9 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var config = require('../config');
|
|
||||||
var gulp = require('gulp');
|
|
||||||
var connect = require('gulp-connect-php');
|
|
||||||
|
|
||||||
gulp.task('serverphp', function() {
|
|
||||||
//connect.server(config.phpserver);
|
|
||||||
});
|
|
|
@ -4,7 +4,6 @@ import classNames from 'classnames';
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
class ToggleButton extends React.Component {
|
class ToggleButton extends React.Component {
|
||||||
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: React.PropTypes.bool,
|
value: React.PropTypes.bool,
|
||||||
|
@ -22,6 +21,7 @@ class ToggleButton extends React.Component {
|
||||||
getClass() {
|
getClass() {
|
||||||
let classes = {
|
let classes = {
|
||||||
'toggle-button': true,
|
'toggle-button': true,
|
||||||
|
'toggle-button_disabled': (this.props.disabled),
|
||||||
[this.props.className]: (this.props.className)
|
[this.props.className]: (this.props.className)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class ToggleButton extends React.Component {
|
||||||
|
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange && !this.props.disabled) {
|
||||||
this.props.onChange({
|
this.props.onChange({
|
||||||
target: {
|
target: {
|
||||||
value: !this.props.value
|
value: !this.props.value
|
||||||
|
|
|
@ -7,4 +7,11 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&_disabled {
|
||||||
|
cursor: default;
|
||||||
|
background-color: transparent;
|
||||||
|
color: $dark-grey;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -53,6 +53,15 @@ import AdminPanelSystemPreferences from 'app/admin/panel/settings/admin-panel-sy
|
||||||
import AdminPanelAdvancedSettings from 'app/admin/panel/settings/admin-panel-advanced-settings';
|
import AdminPanelAdvancedSettings from 'app/admin/panel/settings/admin-panel-advanced-settings';
|
||||||
import AdminPanelEmailTemplates from 'app/admin/panel/settings/admin-panel-email-templates';
|
import AdminPanelEmailTemplates from 'app/admin/panel/settings/admin-panel-email-templates';
|
||||||
|
|
||||||
|
// INSTALLATION
|
||||||
|
import InstallLayout from 'app/install/install-layout';
|
||||||
|
import InstallStep1Language from 'app/install/install-step-1-language';
|
||||||
|
import InstallStep2Requirements from 'app/install/install-step-2-requirements';
|
||||||
|
import InstallStep3Database from 'app/install/install-step-3-database';
|
||||||
|
import InstallStep4UserSystem from 'app/install/install-step-4-user-system';
|
||||||
|
import InstallStep5Admin from 'app/install/install-step-5-admin';
|
||||||
|
import InstallStep6Completed from 'app/install/install-step-6-completed';
|
||||||
|
|
||||||
const history = syncHistoryWithStore(browserHistory, store);
|
const history = syncHistoryWithStore(browserHistory, store);
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
|
@ -82,6 +91,15 @@ export default (
|
||||||
<Route path='ticket/:ticketNumber' component={DashboardTicketPage}/>
|
<Route path='ticket/:ticketNumber' component={DashboardTicketPage}/>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="install" component={InstallLayout}>
|
||||||
|
<IndexRedirect to="step-1" />
|
||||||
|
<Route path="step-1" component={InstallStep1Language}/>
|
||||||
|
<Route path="step-2" component={InstallStep2Requirements} />
|
||||||
|
<Route path="step-3" component={InstallStep3Database} />
|
||||||
|
<Route path="step-4" component={InstallStep4UserSystem} />
|
||||||
|
<Route path="step-5" component={InstallStep5Admin} />
|
||||||
|
<Route path="step-6" component={InstallStep6Completed} />
|
||||||
|
</Route>
|
||||||
<Route path="admin">
|
<Route path="admin">
|
||||||
<IndexRoute component={AdminLoginPage} />
|
<IndexRoute component={AdminLoginPage} />
|
||||||
<Route path="panel" component={AdminPanelLayout}>
|
<Route path="panel" component={AdminPanelLayout}>
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
|
import Widget from 'core-components/widget';
|
||||||
|
import Icon from 'core-components/icon';
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
'LANGUAGE',
|
||||||
|
'SERVER_REQUIREMENTS',
|
||||||
|
'DATABASE_CONFIGURATION',
|
||||||
|
'USER_SYSTEM',
|
||||||
|
'ADMIN_SETUP',
|
||||||
|
'COMPLETED'
|
||||||
|
];
|
||||||
|
|
||||||
|
class InstallLayout extends React.Component {
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
API.call({
|
||||||
|
path: '/system/installation-step',
|
||||||
|
data: {}
|
||||||
|
}).then((result) => {
|
||||||
|
if(result.data != this.getCurrentStep()) {
|
||||||
|
browserHistory.push('/install/step-' + (result.data + 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Widget className="install-layout">
|
||||||
|
<div className="install-layout__header">
|
||||||
|
<div className="install-layout__header-logo">
|
||||||
|
<img width="100%" src="../../images/logo.png" alt="OpenSupports Installation"/>
|
||||||
|
</div>
|
||||||
|
<div className="install-layout__header-text">
|
||||||
|
<div className="install-layout__header-title">
|
||||||
|
{i18n('INSTALL_HEADER_TITLE')}
|
||||||
|
</div>
|
||||||
|
<div className="install-layout__header-description">
|
||||||
|
{i18n('INSTALL_HEADER_DESCRIPTION')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="separator"/>
|
||||||
|
<div className="install-layout__body row">
|
||||||
|
<div className="col-md-3">
|
||||||
|
<ul className="install-layout__steps">
|
||||||
|
{steps.map(this.renderStep.bind(this))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="install-layout__content col-md-9">
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStep(key, index) {
|
||||||
|
let classes = {
|
||||||
|
'install-layout__step': true,
|
||||||
|
'install-layout__step_current': index === this.getCurrentStep(),
|
||||||
|
'install-layout__step_previous': index < this.getCurrentStep()
|
||||||
|
};
|
||||||
|
let icon = 'circle-thin';
|
||||||
|
|
||||||
|
if(index === this.getCurrentStep()) {
|
||||||
|
icon = 'arrow-circle-right';
|
||||||
|
} else if(index < this.getCurrentStep()) {
|
||||||
|
icon = 'check-circle';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className={classNames(classes)} key={index}>
|
||||||
|
<Icon name={icon} size="sm" className="install-layout__step-icon"/>
|
||||||
|
<span className="install-layout__step-text">
|
||||||
|
{index+1}. {i18n(key)}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentStep() {
|
||||||
|
const pathname = this.props.location.pathname;
|
||||||
|
|
||||||
|
if(_.includes(pathname, 'step-1')) {
|
||||||
|
return 0;
|
||||||
|
} else if(_.includes(pathname, 'step-2')) {
|
||||||
|
return 1;
|
||||||
|
} else if(_.includes(pathname, 'step-3')) {
|
||||||
|
return 2;
|
||||||
|
} else if(_.includes(pathname, 'step-4')) {
|
||||||
|
return 3;
|
||||||
|
} else if(_.includes(pathname, 'step-5')) {
|
||||||
|
return 4;
|
||||||
|
} else if(_.includes(pathname, 'step-6')) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstallLayout;
|
|
@ -0,0 +1,75 @@
|
||||||
|
@import "../../scss/vars";
|
||||||
|
|
||||||
|
.install-layout {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 900px;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&-logo {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: $font-size--md;
|
||||||
|
color: $primary-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-description {
|
||||||
|
text-align: left;
|
||||||
|
color: $dark-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__steps {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
border-right: 1px solid $primary-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step {
|
||||||
|
color: $primary-black;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
color: $primary-blue;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_current {
|
||||||
|
|
||||||
|
.install-layout__step-icon {
|
||||||
|
color: $secondary-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_previous {
|
||||||
|
|
||||||
|
.install-layout__step-icon {
|
||||||
|
color: $primary-green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
min-height: 130px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import ConfigActions from 'actions/config-actions';
|
||||||
|
|
||||||
|
import LanguageSelector from 'app-components/language-selector';
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
|
||||||
|
class InstallStep1Language extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="install-step-1">
|
||||||
|
<Header title={i18n('STEP_TITLE', {title: i18n('SELECT_LANGUAGE'), current: 1, total: 6})} description={i18n('STEP_1_DESCRIPTION')}/>
|
||||||
|
<LanguageSelector {...this.getLanguageSelectorProps()} />
|
||||||
|
<div className="install-step-1__button">
|
||||||
|
<Button size="medium" type="secondary" onClick={() => browserHistory.push('/install/step-2')}>
|
||||||
|
{i18n('NEXT')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLanguageSelectorProps() {
|
||||||
|
return {
|
||||||
|
className: 'install-step-1__languages',
|
||||||
|
value: this.props.config.language,
|
||||||
|
type: 'allowed',
|
||||||
|
onChange: this.changeLanguage.bind(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
changeLanguage(event) {
|
||||||
|
this.props.dispatch(ConfigActions.changeLanguage(event.target.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default connect((store) => {
|
||||||
|
return {
|
||||||
|
config: store.config
|
||||||
|
};
|
||||||
|
})(InstallStep1Language);
|
|
@ -0,0 +1,13 @@
|
||||||
|
@import "../../scss/vars";
|
||||||
|
|
||||||
|
.install-step-1 {
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: $font-size--md;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
|
import Icon from 'core-components/icon';
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Table from 'core-components/table';
|
||||||
|
|
||||||
|
class InstallStep2Requirements extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
loading: true,
|
||||||
|
requirements: {
|
||||||
|
phpVersion: {},
|
||||||
|
PDO: {},
|
||||||
|
files: {},
|
||||||
|
configFile: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.retrieveRequirements();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="install-step-2">
|
||||||
|
<Header title={i18n('STEP_TITLE', {title: i18n('SERVER_REQUIREMENTS'), current: 2, total: 6})} description={i18n('STEP_2_DESCRIPTION')}/>
|
||||||
|
<div className="install-step-2__refresh">
|
||||||
|
<Button className="install-step-2__refresh-button" type="secondary" size="medium" onClick={this.retrieveRequirements.bind(this)}>
|
||||||
|
<Icon className="install-step-2__refresh-icon" name="refresh" /> {i18n('REFRESH')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Table {...this.getTableProps()} />
|
||||||
|
<div className="install-step-2__next">
|
||||||
|
<Button disabled={!this.isAllOk()}size="medium" type="secondary" onClick={() => browserHistory.push('/install/step-3')}>
|
||||||
|
{i18n('NEXT')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableProps() {
|
||||||
|
return {
|
||||||
|
className: 'install-step-2__requirement-list',
|
||||||
|
pageSize: 30,
|
||||||
|
loading: this.state.loading,
|
||||||
|
headers: [
|
||||||
|
{key: 'name', value: i18n('REQUIREMENT'), className: 'col-md-3'},
|
||||||
|
{key: 'value', value: i18n('VALUE'), className: 'col-md-9'}
|
||||||
|
],
|
||||||
|
rows: Object.keys(this.state.requirements).map(this.getRequirement.bind(this))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequirement(key) {
|
||||||
|
const requirement = this.state.requirements[key];
|
||||||
|
|
||||||
|
return {
|
||||||
|
className: this.getRequirementClass(requirement),
|
||||||
|
name: requirement.name,
|
||||||
|
value: (
|
||||||
|
<div className="install-step-2__requirement-value">
|
||||||
|
{requirement.value}
|
||||||
|
<Icon name={(requirement.ok) ? 'check' : 'times'} className="install-step-2__requirement-assert" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequirementClass(requirement) {
|
||||||
|
let classes = {
|
||||||
|
'install-step-2__requirement': true,
|
||||||
|
'install-step-2__requirement_error': !requirement.ok
|
||||||
|
};
|
||||||
|
|
||||||
|
return classNames(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllOk() {
|
||||||
|
return _.every(this.state.requirements, {ok: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieveRequirements() {
|
||||||
|
this.setState({loading: true}, () => API.call({path: '/system/check-requirements'}).then(({data}) => this.setState({requirements: data, loading: false})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstallStep2Requirements;
|
|
@ -0,0 +1,51 @@
|
||||||
|
@import "../../scss/vars";
|
||||||
|
|
||||||
|
.install-step-2 {
|
||||||
|
|
||||||
|
&__refresh {
|
||||||
|
margin: 15px 0;
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
width: 120px;
|
||||||
|
position: relative;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 8px;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__requirement-list {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__requirement {
|
||||||
|
color: $secondary-blue;
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-assert {
|
||||||
|
color: $primary-green;
|
||||||
|
float: right;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_error {
|
||||||
|
color: $secondary-red;
|
||||||
|
background-color: #ffdfe0 !important;
|
||||||
|
|
||||||
|
.install-step-2__requirement-assert {
|
||||||
|
color: $secondary-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__next {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Form from 'core-components/form';
|
||||||
|
import FormField from 'core-components/form-field';
|
||||||
|
import SubmitButton from 'core-components/submit-button';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
|
class InstallStep3Database extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
errorMessage: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="install-step-3">
|
||||||
|
<Header title={i18n('STEP_TITLE', {title: i18n('DATABASE_CONFIGURATION'), current: 3, total: 6})} description={i18n('STEP_3_DESCRIPTION')}/>
|
||||||
|
{this.renderMessage()}
|
||||||
|
<Form loading={this.state.loading} onSubmit={this.onSubmit.bind(this)}>
|
||||||
|
<FormField name="dbHost" label={i18n('DATABASE_HOST')} fieldProps={{size: 'large'}} required/>
|
||||||
|
<FormField name="dbName" label={i18n('DATABASE_NAME')} fieldProps={{size: 'large'}} required/>
|
||||||
|
<FormField name="dbUser" label={i18n('DATABASE_USER')} fieldProps={{size: 'large'}} required/>
|
||||||
|
<FormField name="dbPassword" label={i18n('DATABASE_PASSWORD')} fieldProps={{size: 'large', password: true}} required/>
|
||||||
|
<div className="install-step-3__buttons">
|
||||||
|
<SubmitButton className="install-step-3__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
|
||||||
|
<Button className="install-step-3__previous" size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMessage() {
|
||||||
|
let message = null;
|
||||||
|
|
||||||
|
if(this.state.error) {
|
||||||
|
message = (
|
||||||
|
<Message className="install-step-3__message" type="error">
|
||||||
|
{i18n('ERROR_UPDATING_SETTINGS')}: {this.state.errorMessage}
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreviousClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
browserHistory.push('/install/step-2');
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(form) {
|
||||||
|
this.setState({
|
||||||
|
loading: true
|
||||||
|
}, () => {
|
||||||
|
API.call({
|
||||||
|
path: '/system/init-database',
|
||||||
|
data: form
|
||||||
|
})
|
||||||
|
.then(() => browserHistory.push('/install/step-4'))
|
||||||
|
.catch(({message}) => this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: true,
|
||||||
|
errorMessage: message
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstallStep3Database;
|
|
@ -0,0 +1,18 @@
|
||||||
|
@import "../../scss/vars";
|
||||||
|
|
||||||
|
.install-step-3 {
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__previous {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__next {
|
||||||
|
float: left;
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 286px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
|
import ToggleButton from 'app-components/toggle-button';
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Form from 'core-components/form';
|
||||||
|
import FormField from 'core-components/form-field';
|
||||||
|
import SubmitButton from 'core-components/submit-button';
|
||||||
|
|
||||||
|
class InstallStep4UserSystem extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
form: {
|
||||||
|
'user-system-enabled': true,
|
||||||
|
'registration': true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="install-step-4">
|
||||||
|
<Header title={i18n('STEP_TITLE', {title: i18n('USER_SYSTEM'), current: 4, total: 6})} description={i18n('STEP_4_DESCRIPTION')}/>
|
||||||
|
<Form onSubmit={this.onSubmit.bind(this)} values={this.state.form} onChange={this.onChange.bind(this)}>
|
||||||
|
<FormField name="user-system-enabled" label={i18n('ENABLE_USER_SYSTEM')} decorator={ToggleButton}/>
|
||||||
|
<FormField name="registration" label={i18n('ENABLE_USER_REGISTRATION')} decorator={ToggleButton} fieldProps={{disabled: this.isDisabled()}}/>
|
||||||
|
<div className="install-step-4__buttons">
|
||||||
|
<SubmitButton className="install-step-4__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
|
||||||
|
<Button className="install-step-4__previous" size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(form) {
|
||||||
|
this.setState({
|
||||||
|
form: {
|
||||||
|
'user-system-enabled': form['user-system-enabled'],
|
||||||
|
'registration': form['user-system-enabled'] && form['registration']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreviousClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
browserHistory.push('/install/step-3');
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(form) {
|
||||||
|
API.call({
|
||||||
|
path: '/system/init-settings',
|
||||||
|
data: {
|
||||||
|
'user-system-enabled': form['user-system-enabled'],
|
||||||
|
'registration': form['registration']
|
||||||
|
}
|
||||||
|
}).then(() => browserHistory.push('/install/step-5'));
|
||||||
|
}
|
||||||
|
|
||||||
|
isDisabled() {
|
||||||
|
return !this.state.form['user-system-enabled'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstallStep4UserSystem;
|
|
@ -0,0 +1,14 @@
|
||||||
|
@import "../../scss/vars";
|
||||||
|
|
||||||
|
.install-step-4 {
|
||||||
|
|
||||||
|
&__previous {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__next {
|
||||||
|
float: left;
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 286px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Form from 'core-components/form';
|
||||||
|
import FormField from 'core-components/form-field';
|
||||||
|
import SubmitButton from 'core-components/submit-button';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
|
class InstallStep5Admin extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
errorMessage: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="install-step-5">
|
||||||
|
<Header title={i18n('STEP_TITLE', {title: i18n('ADMIN_SETUP'), current: 5, total: 6})} description={i18n('STEP_5_DESCRIPTION')}/>
|
||||||
|
{this.renderMessage()}
|
||||||
|
<Form onSubmit={this.onSubmit.bind(this)}>
|
||||||
|
<FormField name="name" validation="NAME" label={i18n('ADMIN_NAME')} fieldProps={{size: 'large'}} required/>
|
||||||
|
<FormField name="email" validation="EMAIL" label={i18n('ADMIN_EMAIL')} fieldProps={{size: 'large'}} required/>
|
||||||
|
<FormField name="password" validation="PASSWORD" label={i18n('ADMIN_PASSWORD')} infoMessage={i18n('ADMIN_PASSWORD_DESCRIPTION')} fieldProps={{size: 'large', autoComplete: 'off'}} required/>
|
||||||
|
<div className="install-step-5__buttons">
|
||||||
|
<SubmitButton className="install-step-5__next" size="medium" type="secondary">{i18n('NEXT')}</SubmitButton>
|
||||||
|
<Button className="install-step-5__previous" size="medium" onClick={this.onPreviousClick.bind(this)}>{i18n('PREVIOUS')}</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMessage() {
|
||||||
|
let message = null;
|
||||||
|
|
||||||
|
if(this.state.error) {
|
||||||
|
message = (
|
||||||
|
<Message className="install-step-5_message" type="error">
|
||||||
|
{i18n('ERROR_UPDATING_SETTINGS')}: {this.state.errorMessage}
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreviousClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
browserHistory.push('/install/step-4');
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(form) {
|
||||||
|
this.setState({
|
||||||
|
loading: true
|
||||||
|
}, () => {
|
||||||
|
API.call({
|
||||||
|
path: '/system/init-admin',
|
||||||
|
data: form
|
||||||
|
})
|
||||||
|
.then(() => browserHistory.push('/install/step-6'))
|
||||||
|
.catch(({message}) => this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: true,
|
||||||
|
errorMessage: message
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstallStep5Admin;
|
|
@ -0,0 +1,18 @@
|
||||||
|
@import "../../scss/vars";
|
||||||
|
|
||||||
|
.install-step-5 {
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__previous {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__next {
|
||||||
|
float: left;
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 286px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
|
class InstallStep6Completed extends React.Component {
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
setTimeout(() => browserHistory.push('/admin'), 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="install-step-6">
|
||||||
|
<Header title={i18n('STEP_TITLE', {title: i18n('COMPLETED'), current: 6, total: 6})} description={i18n('STEP_6_DESCRIPTION')}/>
|
||||||
|
<Message title={i18n('INSTALLATION_COMPLETED')} type="success">
|
||||||
|
{i18n('INSTALLATION_COMPLETED_DESCRIPTION')}
|
||||||
|
</Message>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstallStep6Completed;
|
|
@ -0,0 +1 @@
|
||||||
|
@import "../../scss/vars";
|
|
@ -0,0 +1,2 @@
|
||||||
|
root = 'http://localhost:3000';
|
||||||
|
apiRoot = 'http://localhost:3000/api';
|
|
@ -29,7 +29,8 @@ class FormField extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
field: 'input'
|
field: 'input',
|
||||||
|
fieldProps: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
static getDefaultValue(field) {
|
static getDefaultValue(field) {
|
||||||
|
@ -120,7 +121,7 @@ class FormField extends React.Component {
|
||||||
|
|
||||||
getFieldProps() {
|
getFieldProps() {
|
||||||
let props = _.extend({}, this.props.fieldProps, {
|
let props = _.extend({}, this.props.fieldProps, {
|
||||||
disabled: this.context.loading,
|
disabled: this.isDisabled(),
|
||||||
errored: !!this.props.error,
|
errored: !!this.props.error,
|
||||||
name: this.props.name,
|
name: this.props.name,
|
||||||
placeholder: this.props.placeholder,
|
placeholder: this.props.placeholder,
|
||||||
|
@ -171,6 +172,12 @@ class FormField extends React.Component {
|
||||||
this.props.onChange(event)
|
this.props.onChange(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDisabled() {
|
||||||
|
const fieldProps = this.props.fieldProps;
|
||||||
|
|
||||||
|
return (fieldProps.disabled === undefined) ? this.context.loading : fieldProps.disabled;
|
||||||
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
if (this.refs.nativeField) {
|
if (this.refs.nativeField) {
|
||||||
|
|
|
@ -146,6 +146,8 @@ class Table extends React.Component {
|
||||||
'table__row-highlighted': row.highlighted
|
'table__row-highlighted': row.highlighted
|
||||||
};
|
};
|
||||||
|
|
||||||
|
classes[row.className] = (row.className);
|
||||||
|
|
||||||
return classNames(classes);
|
return classNames(classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,37 @@ module.exports = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/system/init-settings',
|
||||||
|
time: 50,
|
||||||
|
response: function() {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/init-database',
|
||||||
|
time: 50,
|
||||||
|
response: function() {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
message: 'ERROR_SERVER',
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/init-admin',
|
||||||
|
time: 50,
|
||||||
|
response: function() {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/system/edit-settings',
|
path: '/system/edit-settings',
|
||||||
time: 50,
|
time: 50,
|
||||||
|
@ -63,7 +94,38 @@ module.exports = [
|
||||||
return {
|
return {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
data: {}
|
data: {}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/check-requirements',
|
||||||
|
time: 50,
|
||||||
|
response: function () {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
phpVersion: {
|
||||||
|
name: 'PHP Version',
|
||||||
|
value: 5.6,
|
||||||
|
ok: true
|
||||||
|
},
|
||||||
|
PDO: {
|
||||||
|
name: 'PDO Module',
|
||||||
|
value: 'Available',
|
||||||
|
ok: true
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
name: 'Folder: /api/files',
|
||||||
|
value: 'Writable',
|
||||||
|
ok: true
|
||||||
|
},
|
||||||
|
configFile: {
|
||||||
|
name: 'File: /api/config.php',
|
||||||
|
value: 'Writable',
|
||||||
|
ok: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -173,7 +173,8 @@ export default {
|
||||||
'ACTIVITY': 'Activity',
|
'ACTIVITY': 'Activity',
|
||||||
'HOME': 'Home',
|
'HOME': 'Home',
|
||||||
'TICKET_NUMBER': 'Ticket number',
|
'TICKET_NUMBER': 'Ticket number',
|
||||||
|
'NEXT': 'Next',
|
||||||
|
|
||||||
'CHART_CREATE_TICKET': 'Tickets created',
|
'CHART_CREATE_TICKET': 'Tickets created',
|
||||||
'CHART_CLOSE': 'Tickets closed',
|
'CHART_CLOSE': 'Tickets closed',
|
||||||
'CHART_SIGNUP': 'Signups',
|
'CHART_SIGNUP': 'Signups',
|
||||||
|
@ -208,6 +209,37 @@ export default {
|
||||||
'ACTIVITY_DELETE_USER': 'deleted user',
|
'ACTIVITY_DELETE_USER': 'deleted user',
|
||||||
'ACTIVITY_UN_BAN_USER': 'banned user',
|
'ACTIVITY_UN_BAN_USER': 'banned user',
|
||||||
|
|
||||||
|
'SERVER_REQUIREMENTS': 'Server requirements',
|
||||||
|
'DATABASE_CONFIGURATION': 'Database configuration',
|
||||||
|
'ADMIN_SETUP': 'Admin setup',
|
||||||
|
'COMPLETED': 'Completed',
|
||||||
|
'INSTALL_HEADER_TITLE': 'OpenSupports Installation Wizard',
|
||||||
|
'INSTALL_HEADER_DESCRIPTION': 'This wizard will help you to configure and install OpenSupports on your website',
|
||||||
|
'SELECT_LANGUAGE': 'Select language',
|
||||||
|
'REQUIREMENT': 'Requirement',
|
||||||
|
'VALUE': 'Value',
|
||||||
|
'REFRESH': 'Refresh',
|
||||||
|
'USER_SYSTEM': 'User System',
|
||||||
|
'PREVIOUS': 'Previous',
|
||||||
|
'DATABASE_HOST': 'MySQL server',
|
||||||
|
'DATABASE_NAME': 'MySQL database name',
|
||||||
|
'DATABASE_USER': 'MySQL user',
|
||||||
|
'DATABASE_PASSWORD': 'MySQL password',
|
||||||
|
'ADMIN_NAME': 'Admin account name',
|
||||||
|
'ADMIN_EMAIL': 'Admin account email',
|
||||||
|
'ADMIN_PASSWORD': 'Admin account password',
|
||||||
|
'ADMIN_PASSWORD_DESCRIPTION': 'Please remember this password. It is needed for accessing the admin panel. You can change it later.',
|
||||||
|
'INSTALLATION_COMPLETED': 'Installation completed.',
|
||||||
|
'INSTALLATION_COMPLETED_DESCRIPTION': 'The installation of OpenSupports is completed. Redirecting to admin panel...',
|
||||||
|
|
||||||
|
'STEP_TITLE': 'Step {current} of {total} - {title}',
|
||||||
|
'STEP_1_DESCRIPTION': 'Select your preferred language for the installation wizard.',
|
||||||
|
'STEP_2_DESCRIPTION': 'Here are listed the requirements for running OpenSupports. Please make sure that all requirements are satisfied.',
|
||||||
|
'STEP_3_DESCRIPTION': 'Please fill the MySQL database configuration.',
|
||||||
|
'STEP_4_DESCRIPTION': 'Please select your user system preferences.',
|
||||||
|
'STEP_5_DESCRIPTION': 'Please configure the administrator account.',
|
||||||
|
'STEP_6_DESCRIPTION': 'Installation is completed.',
|
||||||
|
|
||||||
//VIEW DESCRIPTIONS
|
//VIEW DESCRIPTIONS
|
||||||
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',
|
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',
|
||||||
'TICKET_LIST_DESCRIPTION': 'Here you can find a list of all tickets you have sent to our support team.',
|
'TICKET_LIST_DESCRIPTION': 'Here you can find a list of all tickets you have sent to our support team.',
|
||||||
|
|
|
@ -9,11 +9,13 @@
|
||||||
<title>OS4</title>
|
<title>OS4</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/main.css">
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
<link rel="icon" type="image/x-icon" href="/images/icon.png">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<script src="/js/config.js"></script>
|
||||||
<script src="/js/main.js"></script>
|
<script src="/js/main.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -2,9 +2,6 @@ const _ = require('lodash');
|
||||||
const APIUtils = require('lib-core/APIUtils');
|
const APIUtils = require('lib-core/APIUtils');
|
||||||
const SessionStore = require('lib-app/session-store');
|
const SessionStore = require('lib-app/session-store');
|
||||||
|
|
||||||
const url = 'http://localhost:3000';
|
|
||||||
const apiUrl = 'http://localhost:3000/api';
|
|
||||||
|
|
||||||
function processData (data, dataAsForm = false) {
|
function processData (data, dataAsForm = false) {
|
||||||
let newData;
|
let newData;
|
||||||
|
|
||||||
|
@ -31,7 +28,7 @@ module.exports = {
|
||||||
call: function ({path, data, plain, dataAsForm}) {
|
call: function ({path, data, plain, dataAsForm}) {
|
||||||
console.log('request ' + path, data);
|
console.log('request ' + path, data);
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
APIUtils.post(apiUrl + path, processData(data, dataAsForm), dataAsForm)
|
APIUtils.post(apiRoot + path, processData(data, dataAsForm), dataAsForm)
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
|
|
||||||
|
@ -53,14 +50,14 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
getFileLink(filePath) {
|
getFileLink(filePath) {
|
||||||
return apiUrl + '/system/download?file=' + filePath;
|
return apiRoot + '/system/download?file=' + filePath;
|
||||||
},
|
},
|
||||||
|
|
||||||
getAPIUrl() {
|
getAPIUrl() {
|
||||||
return apiUrl;
|
return apiRoot;
|
||||||
},
|
},
|
||||||
|
|
||||||
getURL() {
|
getURL() {
|
||||||
return url;
|
return root;
|
||||||
}
|
}
|
||||||
};
|
};
|
Loading…
Reference in New Issue