Commit c6bf6f36 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov Committed by Matija Obreza
Browse files

Added css files, final form and material ui support

parent 32ccc2d4
import * as React from 'react';
import Button from '@material-ui/core/Button';
import CircularProgress from '@material-ui/core/CircularProgress';
import withStyles from '@material-ui/core/styles/withStyles';
import { ButtonProps } from '@material-ui/core'
const styles = () => ({
/* eslint-disable */
progress: {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
marginTop: '-12px',
marginLeft: '-12px',
}
/* eslint-enable */
});
interface IActionButtonProps extends React.ClassAttributes<any> {
title: string;
action: () => any;
style?: any;
className?: string;
sync?: boolean;
classes: any;
}
class ActionButton extends React.Component<IActionButtonProps & ButtonProps, any> {
private _isMounted: boolean = false;
public constructor(props) {
super(props);
this.state = {
running: false,
};
}
public componentDidMount() {
this._isMounted = true;
}
public componentWillUnmount() {
this._isMounted = false;
}
private runAction = () => {
const { action } = this.props;
if (! action) {
return;
}
const { running } = this.state;
if (! running) {
const r = action();
if (r instanceof Promise) {
// console.log('Action response is a promise');
this.setState({ running: true });
(r as Promise<any>).then((result) => {
console.log('ActionButton sync action completed');
return result;
}).catch((e) => {
console.log('ActionButton sync error', e);
throw e;
}).finally(() => {
if (this._isMounted) {
this.setState({ running: false });
}
});
} else {
// console.log(`Action response:`, r, typeof r, r instanceof Promise);
// No need to set state
// this.setState({ running: false });
}
} else {
console.log('ActionButton sync action is running');
}
}
public render() {
const { title, action, variant = 'contained', style, className, classes, sync = false } = this.props;
const { running } = this.state;
if (sync) {
return (
<Button disabled={ running } className={ className } style={ style } variant={ variant } onClick={ this.runAction }>
{ title }
{ running && <CircularProgress size={ 24 } className={ classes.progress }/> }
</Button>
);
} else {
return (
<Button className={ className } style={ style } variant={ variant } onClick={ action }>
{ title }
</Button>
);
}
}
}
export default withStyles(styles)(ActionButton);
import * as React from 'react';
import { WithTranslation, withTranslation } from 'react-i18next';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Button from '@material-ui/core/Button';
import navigateTo from '@gringlobal/client/action/navigation';
interface IBackButtonProps extends React.ClassAttributes<any>, WithTranslation {
lastPath: string;
defaultTarget: string;
defaultBackText?: string;
hasHistory: boolean;
lastLocation: string;
navigateTo: (path: string) => void;
style?: any;
itemState?: string;
}
class BackButton extends React.Component<IBackButtonProps, any> {
public constructor(props: any) {
super(props);
}
protected onGoBack = () => {
const { defaultTarget, hasHistory, lastLocation, navigateTo } = this.props;
if (hasHistory) {
navigateTo(lastLocation);
} else {
navigateTo(defaultTarget);
}
};
public render() {
const { defaultBackText, style, t } = this.props;
return (
<Button variant="contained" style={style} onClick={this.onGoBack}>
{defaultBackText || t('common:action.back')}
</Button>
);
}
}
const mapStateToProps = (state) => ({
hasHistory: state.history.hasHistory,
lastLocation: state.history.lastLocation,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
navigateTo,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(withTranslation()(BackButton));
import * as React from 'react';
import { WithTranslation, withTranslation } from 'react-i18next';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import InputLabel from '@material-ui/core/InputLabel';
import MuiFormControl from '@material-ui/core/FormControl';
interface IProps extends WithStyles, WithTranslation {
fullWidth?: boolean;
required?: boolean;
label?: string;
children: any;
meta?: any;
disabled?: boolean;
disableAnimation?: boolean;
shrink?: boolean;
className?: string;
t: any;
}
const styles = (theme) => {
// console.log(theme);
return ({
helper: {
paddingTop: '0.5rem',
color: '#FF0000',
},
});
};
const FormControl = ({ fullWidth = false, required = false, label = null, children, meta = {}, disableAnimation = false, disabled = false, shrink, classes, t }: IProps) => {
// console.log('Meta', meta);
const { error, warning } = meta;
return (
<MuiFormControl fullWidth={ fullWidth } disabled={ disabled }>
{ label && <InputLabel error={ error && true } required={ required } disableAnimation={ disableAnimation } shrink={ shrink }>{ t(label) }</InputLabel> }
{ children }
{
((error &&
<div className={ classes.helper }>
{ typeof error === 'object' ? t(...error) : t(error) }
</div>
) || (warning &&
<div className={ classes.helper }>
{ typeof warning === 'object' ? t(...warning) : t(warning) }
</div>
))
}
</MuiFormControl>
);
};
export default withStyles(styles)(withTranslation()(FormControl));
import * as React from 'react';
import Input from '@material-ui/core/Input';
import FormControl from '@gringlobal/client/ui/common/form/FormControl';
export const TextField = ({ input, InputLabelProps, label, meta, shrink, required, ...custom }) => (
<FormControl fullWidth required={ required } label={ label } shrink={ shrink } meta={ meta }>
<Input error={ meta.touched && meta.error && true } { ...input } { ...custom } />
</FormControl>
);
import * as React from 'react';
import { Grid } from '@material-ui/core';
export default (props) => (
<Grid justify="center" alignItems="flex-start" container item xs={ 12 } {...props}>
{ props.children }
</Grid>
);
......@@ -8,7 +8,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes" />
<meta name="author" content="Genesys Team, helpdesk@genesys-pgr.org" />
SERVER_RENDERED_DESCRIPTION
<style type="text/css" id="server-side-styles">
SERVER_RENDERED_CSS
</style>
......
......@@ -115,7 +115,7 @@ module.exports = {
},
entry: {
gringlobal: [ 'babel-polyfill', './entrypoints/client.tsx' ],
gringlobal: [ 'babel-polyfill', './entrypoints/vendor.ts', './entrypoints/client.tsx' ],
},
output: {
......@@ -151,6 +151,53 @@ module.exports = {
},
],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: false
},
},
'resolve-url-loader', {
loader: 'sass-loader',
options: {
sourceMap: true,
sassOptions: {
includePaths: ['node_modules']
}
}
}
]
},
{
test: /\.css$/,
exclude: /node_modules\/react\-toolbox/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.(woff|woff2|eot)(\?.*)?$/,
loader: 'file-loader?name=fonts/[hash].[ext]'
},
{
test: /\.ttf(\?.*)?$/,
loader: 'url-loader?limit=10000&mimetype=application/octet-stream&name=fonts/[hash].[ext]'
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
exclude: /node_modules\/leaflet/,
loader: 'url-loader?limit=100000&name=images/[name].[ext]'
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
include: /node_modules\/leaflet/,
loader: 'file-loader?limit=100000&name=images/[name].[ext]'
}
]
},
plugins: [
......
......@@ -47,6 +47,9 @@ const store = composeEnhancers(applyMiddleware(thunk, routerMiddleware(history),
const SsrI18nProvider = withSSR()(I18nextProvider) as any;
import { MuiThemeProvider } from '@material-ui/core/styles';
import theme from 'core/ui/theme';
// Configure axios for client
configureBackendApi({ apiUrl: store.getState().applicationConfig.apiUrl || process.env.API_URL || 'http://localhost:8080' });
......@@ -67,7 +70,9 @@ if (__PRELOADED_STATE__ === undefined) {
<Provider store={store}>
<ConnectedRouter history={history}>
<I18nextProvider i18n={ i18nC }>
{renderRoutes(routes)}
<MuiThemeProvider theme={ theme(direction) }>
{renderRoutes(routes)}
</MuiThemeProvider>
</I18nextProvider>
</ConnectedRouter>
</Provider>,
......@@ -84,16 +89,18 @@ if (__PRELOADED_STATE__ === undefined) {
<Provider store={store}>
<ConnectedRouter history={history}>
<SsrI18nProvider i18n={ i18nC } initialLanguage={initialLanguage} initialI18nStore={initialI18nStore}>
{renderRoutes(routes)}
<MuiThemeProvider theme={ theme(direction) }>
{ renderRoutes(routes) }
</MuiThemeProvider>
</SsrI18nProvider>
</ConnectedRouter>
</Provider>,
document.getElementById('the-app'),
() => {
// We don't need the static css any more once we have launched our application.
// log('Removing SSR-rendered styles');
// const ssStyles = document.getElementById('server-side-styles');
// ssStyles.parentNode.removeChild(ssStyles);
log('Removing SSR-rendered styles');
const ssStyles = document.getElementById('server-side-styles');
ssStyles.parentNode.removeChild(ssStyles);
// store.dispatch(refreshAuthTokenAuto());
},
);
......
/**
* This is an entry point for additional assets
*
* Don't mess with this file if you don't know what you are doing.
*
* Thanks,
* @mobreza
*/
// Normalize.css
require('normalize.css/normalize.css');
// Roboto font
// require('roboto-fontface/css/roboto/roboto-fontface.css');
require('../styles/roboto-fontface.css');
// react virtualized
require('react-virtualized/styles.css');
// fontawesome
require('../styles/font-awesome/css/font-awesome.css');
// Catalog styles
require('../styles/app.styles.scss');
// flexboxgrid
// require('flexboxgrid/dist/flexboxgrid.css');
......@@ -14,12 +14,18 @@
},
"dependencies": {
"@gringlobal/client": "*",
"@material-ui/core": "^4.9.4",
"@material-ui/styles": "^4.9.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.44",
"axios": "^0.19.2",
"connected-react-router": "^6.6.1",
"cookie-parser": "^1.4.4",
"cross-env": "^7.0.0",
"express": "^4.17.1",
"express-http-proxy": "^1.6.0",
"final-form": "^4.18.7",
"final-form-arrays": "^3.0.2",
"history": "^4.10.1",
"i18next": "^19.0.3",
"i18next-browser-languagedetector": "^4.0.1",
......@@ -29,17 +35,20 @@
"immutability-helper": "^3.0.1",
"js-md5": "^0.7.3",
"jsonwebtoken": "^8.5.1",
"normalize.css": "^8.0.1",
"path": "^0.12.7",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-final-form": "^6.3.5",
"react-fontawesome": "^1.7.1",
"react-i18next": "^11.3.1",
"react-loadable": "^5.5.0",
"react-redux": "^7.1.3",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"react-virtualized": "^9.21.2",
"redux": "^4.0.5",
"redux-form": "^8.2.6",
"redux-saga": "^1.1.3",
"redux-thunk": "^2.3.0",
"url-template": "^2.0.8"
......@@ -70,6 +79,7 @@
"babel-preset-typescript": "^7.0.0-alpha.19",
"copy-webpack-plugin": "^5.1.1",
"cross-env": "^7.0.0",
"css-loader": "^3.4.2",
"es6-promise": "^4.2.8",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.0.1",
......@@ -79,21 +89,33 @@
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prefer-arrow": "^1.1.7",
"eslint-plugin-react": "^7.18.0",
"file-loader": "^5.0.2",
"git-revision-webpack-plugin": "^3.0.4",
"html-webpack-exclude-assets-plugin": "0.0.7",
"html-webpack-plugin": "^3.2.0",
"lerna": "^3.20.2",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-mixins": "^6.2.3",
"precss": "^4.0.0",
"react-hot-loader": "^4.12.18",
"react-jss": "^10.0.3",
"resolve-url-loader": "^3.1.1",
"rimraf": "^3.0.1",
"roboto-fontface": "^0.10.0",
"sass-loader": "^8.0.2",
"script-ext-html-webpack-plugin": "^2.1.4",
"style-loader": "^1.1.3",
"stylelint": "^13.0.0",
"terser-webpack-plugin": "^2.3.4",
"ts-node": "^8.6.2",
"tslint": "^6.0.0",
"tslint-loader": "^3.5.4",
"tslint-react": "^4.2.0",
"typescript": "^3.7.5",
"url-loader": "^3.0.0",
"webpack": "^4.41.5",
"webpack-chunk-hash": "^0.6.0",
"webpack-cli": "^3.3.10",
......
......@@ -17,6 +17,10 @@ import rootReducer from 'core/reducer';
import languages from '@gringlobal/i18n/data/Languages';
import { SheetsRegistry } from 'jss';
import { MuiThemeProvider, StylesProvider, createGenerateClassName } from '@material-ui/core/styles';
import theme from 'core/ui/theme';
// import checkAuthToken from './checkAuthToken';
import { routes } from 'core/ui/routes';
import matchRoutes from '@gringlobal/client/ui/matchRoutes';
......@@ -64,6 +68,9 @@ const prerenderer = (html, errHtml) => (req, res) => {
function renderView() {
// const jss = createJss(jssPreset());
const sheets = new SheetsRegistry();
const generateClassName = createGenerateClassName();
const initialLanguage = req.i18n.language;
const initialI18nStore = {};
const defaultLanguage = 'en';
......@@ -75,16 +82,21 @@ const prerenderer = (html, errHtml) => (req, res) => {
const basename = initialLanguage !== defaultLanguage ? `${config.frontendPath}/${initialLanguage}` : `${config.frontendPath}`;
const pathWithoutLang = req.url.substr(initialLanguage !== defaultLanguage ? 3 : 0, req.url.length);
console.log(`<StaticRouter location="${pathWithoutLang}" basename="${basename}"`);
const direction = getDir(initialLanguage);
const modules = [];
const InitialView = (
<Loadable.Capture report={ (moduleName) => modules.push(moduleName) }>
<ReduxProvider store={ store }>
<I18nextProvider i18n={ req.i18n }>
<StaticRouter location={ pathWithoutLang } context={ context } basename={ basename }>
{ renderRoutes(routes) }
</StaticRouter>
</I18nextProvider>
<StylesProvider generateClassName={ generateClassName } sheetsRegistry={ sheets }>
<MuiThemeProvider theme={ theme(direction) }>
<I18nextProvider i18n={ req.i18n }>
<StaticRouter location={ pathWithoutLang } context={ context } basename={ basename }>
{ renderRoutes(routes) }
</StaticRouter>
</I18nextProvider>
</MuiThemeProvider>
</StylesProvider>
</ReduxProvider>
</Loadable.Capture>
);
......@@ -128,6 +140,7 @@ const prerenderer = (html, errHtml) => (req, res) => {
// you can use the publicPath value from bundle, e.g:
// return `<script src="${bundle.publicPath}"></script>`
}).join('\n');
case 'SERVER_RENDERED_CSS': return sheets.toString();
case 'SERVER_RENDERED_STATE': return serialize(initialState, { isJSON: true });
case 'SERVER_RENDERED_HTML': return componentHTML;
case 'SERVER_RENDERED_TITLE': return titleState;
......
import { createMuiTheme } from '@material-ui/core/styles';
import { ThemeOptions } from '@material-ui/core/styles/createMuiTheme';
const HEADING_FONT_FAMILY = '"Roboto", "Helvetica", "Arial", sans-serif';
const theme = (dir) => createMuiTheme({
direction: dir,
shape: {
borderRadius: 0,
},
overrides: {
MuiFormControl: {
root: {
marginTop: '0.71rem', // 10px
marginBottom: '0.71rem', // 10px
},
},
MuiFormControlLabel: {
root: {
'html[dir="ltr"] &' : {
marginRight: 0,
},
'html[dir="rtl"] &' : {
marginRight: '-14px',
marginLeft: 0,
},
},
},
MuiTypography: {
body2: {
color: 'initial',
fontWeight: 'initial',
},
h5: {
color: 'unset',
fontWeight: 600,
fontSize: '24px',
},
},
MuiCheckbox: {
colorSecondary: {
'&$checked': {
color: '#88ba42',
},
},
},
MuiRadio: {
colorSecondary: {
'&$checked': {
color: '#88ba42',
},
},
},
MuiSwitch: {
colorSecondary: {
'&$checked': {
'color': '#88ba42',
'& + $bar': {
backgroundColor: '#9ac45e',
},
},
},
},
MuiChip: {
deleteIcon: {
'html[dir="rtl"] &': {
marginLeft: '8px',
},
},
},
MuiFormLabel: {
root: {
'fontSize': '1em',
'color': 'rgba(0, 0, 0, 0.54);',
'html[dir="rtl"] &': {