Commit 123e998e authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '6-application-layout' into 'master'

Resolve "Application layout"

Closes #6

See merge request genesys-pgr/genesys-ui!4
parents f43d7b05 cefad1e2
......@@ -66,8 +66,9 @@ module.exports = {
if(grantType === 'client_credentials' || grantType === 'password') {
p = `${p}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}`;
}
// remove all headers from request
req.headers = {};
// We need some, if not all headers
// // remove all headers from request
// req.headers = {};
} else if (p.indexOf('/google/verify-token') === 0) {
p = `${p}&clientId=${CLIENT_ID}`;
}
......
......@@ -113,7 +113,6 @@ const prerenderer = (html) => (req, res) => {
}).then((html) => {
const serverRenderTime = `${Date.now() - startTime}ms`;
console.log('Server render time:', startTime, Date.now(), serverRenderTime);
const r = html.replace('__SERVER_RENDER_TIME__', serverRenderTime);
const keyStatus = 'status';
const keyUrl = 'url';
......@@ -131,7 +130,7 @@ const prerenderer = (html) => (req, res) => {
}
}
return res.set('Content-Type', 'text/html').send(r);
return res.set('Content-Type', 'text/html').send(html);
}).catch((err) => {
console.error('Error:', err);
res.status(500).end(err.message);
......
......@@ -18,13 +18,6 @@
<![endif]-->
<div id="the-app">SERVER_RENDERED_HTML</div>
<footer class="footer">
<div class="footer-container">
<div class="footer-link">
Processing this page took __SERVER_RENDER_TIME__.
</div>
</div>
</footer>
<script>
__PRELOADED_STATE__ = SERVER_RENDERED_STATE;
window.initialLanguage = SERVER_RENDERED_LANG;
......
const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000';
const clientUrl = `${origin}/proxy`;
const origin = typeof window !== 'undefined' ?
// if we're on the client
window.document.baseURI.replace(window.location.origin, '').replace(/\/$/, '')
// or on the server
: 'http://localhost:3000';
export const LOGIN_URL = `${clientUrl}/oauth/token`;
export const LOGOUT_URL = `${clientUrl}/token`;
export const CHECK_TOKEN_URL = `${clientUrl}/oauth/check_token`;
export const VERIFY_GOOGLE_TOKEN_URL = `${clientUrl}/google/verify-token`;
export const API_ROOT = `${origin}/proxy`;
export const API_BASE_URL = `${clientUrl}/api/v0`;
export const LOGIN_URL = `${API_ROOT}/oauth/token`;
export const LOGOUT_URL = `${API_ROOT}/api/v0/me/logout`;
export const CHECK_TOKEN_URL = `${API_ROOT}/oauth/check_token`;
export const VERIFY_GOOGLE_TOKEN_URL = `${API_ROOT}/google/verify-token`;
export const API_BASE_URL = `${API_ROOT}/api/v0`;
export const SERVER_INFO_URL = `${API_BASE_URL}/info/version`;
......@@ -3,7 +3,8 @@ import axios from 'axios';
import authenticatedRequest from 'utilities/requestUtils';
import { LOGIN_URL, LOGOUT_URL, CHECK_TOKEN_URL, VERIFY_GOOGLE_TOKEN_URL } from 'constants/apiURLS';
import { API_ROOT, LOGIN_URL, CHECK_TOKEN_URL, VERIFY_GOOGLE_TOKEN_URL } from 'constants/apiURLS';
const URL_LOGOUT = `${API_ROOT}/api/v0/me/logout`;
export class LoginService {
......@@ -32,14 +33,22 @@ export class LoginService {
.then(({ data }) => data);
}
public static logout(token: string) {
return authenticatedRequest(token, {
url: LOGOUT_URL,
method: 'DELETE',
params: {
token,
},
});
/**
* logout at /api/v0/me/logout
*
* @param authToken Authorization token
*/
public static logout(authToken: string): Promise<any> {
const apiUrl = URL_LOGOUT;
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return authenticatedRequest(authToken, {
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as any);
}
public static checkToken(token: string) {
......
......@@ -2,8 +2,6 @@ import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {logoutRequest, loginAppRequest} from 'actions/login';
import {setAppMounted} from 'actions/appMounted';
import {updateHistory} from 'actions/history';
import { withRouter } from 'react-router-dom';
......@@ -13,8 +11,6 @@ import TimeAgo from 'javascript-time-ago';
import * as en from 'javascript-time-ago/locale/en';
TimeAgo.locale(en);
import Header from './layout/Header';
import Footer from './layout/Footer';
import Snackbar from 'ui/common/snackbar/Snackbar';
import renderRoutes from 'ui/renderRoutes';
......@@ -23,9 +19,6 @@ interface IAppProps extends React.ClassAttributes<any> {
login: any;
route: any;
location: any;
logoutRequest: () => Promise<any>;
loginAppRequest: () => Promise<any>;
setAppMounted: () => void;
updateHistory: (path: string) => void;
}
......@@ -45,12 +38,10 @@ class App extends React.Component<IAppProps, any> {
}
public render() {
const { login, logoutRequest, loginAppRequest, route: { routes } } = this.props;
const { route: { routes } } = this.props;
return (
<div>
<Header login={ login } logoutRequest={ logoutRequest } loginAppRequest={ loginAppRequest } />
{ renderRoutes(routes) }
<Footer />
<Snackbar />
</div>
);
......@@ -62,9 +53,6 @@ const mapStateToProps = (state) => ({
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
logoutRequest,
loginAppRequest,
setAppMounted,
updateHistory,
}, dispatch);
......
import * as React from 'react';
import PageLayout from './PageLayout';
const DashboardMenu = () => (
<div>
<div>Haha</div>
<div>Haha</div>
</div>
);
const Layout = ({children = null}) => (
<PageLayout sidebar={ <DashboardMenu /> }>
{ children }
</PageLayout>
);
export default Layout;
import * as React from 'react';
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
import { NavLink, Link, withRouter } from 'react-router-dom';
import { translate } from 'react-i18next';
import {logoutRequest, loginAppRequest} from 'actions/login';
import AppBar from '@material-ui/core/AppBar';
import { withStyles } from '@material-ui/core/styles';
import Toolbar from '@material-ui/core/Toolbar';
......@@ -135,16 +139,8 @@ class Header extends React.Component<IHeaderProps | any, any> {
const { classes, t } = this.props;
return (
// <div className="top-navigation">
// <Link to="/">Home</Link>
// <Link to="/descriptors">Descriptors</Link>
// <Link to="/partners">Partners</Link>
// <Link to="/datasets">Datasets</Link>
// <Link to="/counter">Counter</Link>
// { this.renderLogin([ROLE_USER, ROLE_ADMINISTRATOR]) }
// </div>
<div className="header-bar">
<AppBar position="static" className={ classes.headerRoot } >
<AppBar position="fixed" className={ classes.headerRoot } >
<Toolbar className={ classes.blockHeader }>
<div className="mobile-navigation-block">
<IconButton className={ classes.menuBtn } aria-label="Menu" color="secondary" onClick={ this.handleLeftMenuOpen } >
......@@ -180,4 +176,9 @@ const mapStateToProps = (state) => ({
login: state.login,
});
export default translate()(withRouter(connect(mapStateToProps, null)(withStyles(styleSheet)(Header))));
const mapDispatchToProps = (dispatch) => bindActionCreators({
loginAppRequest,
logoutRequest,
}, dispatch);
export default translate()(withRouter(connect(mapStateToProps, mapDispatchToProps)(withStyles(styleSheet)(Header))));
import * as React from 'react';
import { withStyles } from '@material-ui/core/styles';
// import * as classnames from 'classnames';
import Header from './Header';
import Footer from './Footer';
import SidebarWrapper from './sidebar/SidebarWrapper';
const styles = (theme) => ({
root: {
paddingTop: '72px',
display: 'flex' as 'flex',
flexDirection: 'row' as 'row',
height: 'auto',
width: '100%',
overflow: 'visible' as 'visible',
position: 'absolute' as 'absolute',
},
content: {
flexGrow: 1,
display: 'flex' as 'flex',
minHeight: 'calc(100vh - 72px)',
flexDirection: 'column' as 'column',
// flexBasis: '70%',
},
children: {
flexGrow: 1,
width: '100%',
},
footer: {
flexGrow: 'initial' as 'initial',
},
});
interface ILayoutProps extends React.Props<any> {
classes?: any;
children?: any;
sidebar?: any;
}
const Layout = ({classes, children = null, sidebar = null}: ILayoutProps) => (
<div>
<Header />
<div className={ classes.root }>
{ sidebar && <SidebarWrapper sidebarContent={ sidebar }/> }
{ children && (
<div className={ classes.content }>
<div className={ classes.children }>
{ children }
</div>
<div className={ classes.footer }>
<Footer />
</div>
</div>
) }
</div>
</div>
);
export default withStyles(styles)(Layout);
import * as React from 'react';
import { withStyles } from '@material-ui/core/styles';
// import * as classnames from 'classnames';
import Footer from 'ui/layout/Footer';
const styles = (theme) => ({
root: {
display: 'flex' as 'flex',
flexDirection: 'row' as 'row',
height: '100%',
width: '100%',
position: 'fixed' as 'fixed',
},
content: {
flexGrow: 1,
flexBasis: '70%',
display: 'flex' as 'flex',
flexDirection: 'column' as 'column',
justifyContent: 'space-around' as 'space-around',
},
children: {
flexGrow: 3,
flexBasis: '70%',
},
footer: {
flexGrow: 1,
flexBasis: '210px',
},
});
interface ISimpleLayoutProps extends React.Props<any> {
classes?: any;
children?: any;
}
const SimpleLayout = ({classes, children = null}: ISimpleLayoutProps) => (
<div className={ classes.root }>
{ children && (
<div className={ classes.content }>
<div className={ classes.children }>
{ children }
</div>
<div className={ classes.footer }>
<Footer />
</div>
</div>
) }
</div>
);
export default withStyles(styles)(SimpleLayout);
import * as React from 'react';
import Drawer from '@material-ui/core/Drawer';
const SidebarDrawer = ({variant, className, isOpen, children}) => (
<Drawer
variant={ variant }
className={ className }
open={ isOpen }
>
{ children }
</Drawer>
);
export default SidebarDrawer;
import * as React from 'react';
import {withStyles} from '@material-ui/core/styles';
import SidebarDrawer from './SidebarDrawer';
import ChevronRight from '@material-ui/icons/ChevronRight';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
interface ISidebarProps extends React.ClassAttributes<any> {
classes: any;
sidebarContent: any;
isCollapsed: boolean;
setIsCollapsed: (isCollapsed: boolean) => void;
}
const styles = (theme) => ({
/* tslint:disable */
drawer: {
position: 'sticky' as 'sticky',
top: '72px',
height: 'calc(100vh - 72px)',
'& > div': {
top: 'auto' as 'auto',
position: 'initial' as 'initial',
height: '100%' as '100%'
},
},
sidebar: {
whiteSpace: 'nowrap' as 'nowrap',
minWidth: '240px',
transition: theme.transitions.create('min-width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
sidebarCollapsed: {
overflowX: 'hidden' as 'hidden',
transition: theme.transitions.create('min-width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
minWidth: theme.spacing.unit * 7,
// maxWidth: theme.spacing.unit * 7,
[theme.breakpoints.up('sm')]: {
maxWidth: theme.spacing.unit * 9,
},
},
sidebarContents: {
width: '100%',
},
/* tslint:disable */
collapseButton: {
width: '100%',
position: 'absolute' as 'absolute',
// height: '50px',
bottom: 0,
left: 0,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
'&:hover': {
backgroundColor: '#ccc',
},
},
buttonCollapsed: {
// width: '72px',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
}
});
class SidebarWrapper extends React.Component<ISidebarProps, any> {
setIsCollapsed = (isOpen: boolean) => {
this.setState({isOpen: isOpen});
};
public constructor(props: any) {
super(props);
this.state = {
isOpen: true,
};
}
public render() {
const { sidebarContent, classes } = this.props;
const { isOpen } = this.state;
return (
<SidebarDrawer
className={ classes.drawer }
isOpen={ isOpen }
variant={ 'permanent' }
>
<div className={ `${ classes.sidebar } ${!isOpen ? classes.sidebarCollapsed : '' }` }>
<div className={ classes.sidebarContents }>
{ sidebarContent }
<div
onClick={() => { this.setIsCollapsed(!isOpen); }}
className={ `${classes.collapseButton} ${!isOpen ? classes.buttonCollapsed : ''}` }
>
{ isOpen ? <span><ChevronLeft /> Collapse</span> : <ChevronRight /> }
</div>
</div>
</div>
</SidebarDrawer>
);
}
}
export default (withStyles(styles)(SidebarWrapper));
......@@ -7,9 +7,10 @@ import {log} from 'utilities/debug';
import { parse } from 'query-string';
import { listAccessions } from 'actions/accession';
import { Page, Pagination } from 'model/common.model';
import PageLayout from 'ui/layout/PageLayout';
import ContentHeader from 'ui/common/heading/ContentHeader';
import Loading from 'ui/common/Loading';
import PaginationComponent from 'ui/common/pagination';
import AccessionsBrowseFilters from './c/Filters';
......@@ -18,8 +19,6 @@ import DOI from 'ui/common/DOI';
import { MultiGrid as VMultiGrid, AutoSizer, InfiniteLoader } from 'react-virtualized';
import Grid from '@material-ui/core/Grid';
const styles = (theme) => ({
filterSection: theme.leftPanel.root,
BodyGrid: {
......@@ -216,57 +215,45 @@ class BrowsePage extends React.Component<IBrowsePageProps, any> {
}
public render() {
const { classes } = this.props;
const { paged, filter, list } = this.state;
const stillLoading: boolean = (! paged || ! paged.content);
return (
<Grid container spacing={ 0 }>
<Grid item xs={ 12 } md={ 3 } lg={ 2 } className={ classes.filterSection }>
<AccessionsBrowseFilters initialValues={ filter } onSubmit={ this.applyFilters } />
</Grid>
<Grid item xs={ 12 } md={ 9 } lg={ 10 } className="back-gray">
<Grid container spacing={ 0 }>
<Grid item xs={ 12 }>
<PaginationComponent
pageObj={ paged }
onChange={ this.onPaginationChange }
displayName="accessions"
sortOptions={ SORT_OPTIONS }
infinite
/>
</Grid>
<Grid item xs={ 12 }>
<PrettyFilters
prefix="accessions"
filterObj={ filter }
onSubmit={ this.applyFilters }
/>
</Grid>
</Grid>
{ stillLoading ? <Loading /> :
<InfiniteLoader
isRowLoaded={ this.isRowLoaded }
loadMoreRows={ this.loadMoreRows }
rowCount={ paged.last ? list.length : list.length + 1 }
>
{ this._infiniteLoaderChildFunction }
</InfiniteLoader>
}
</Grid>
<Grid container spacing={ 0 }>
<Grid item xs={ 12 }>
<PaginationComponent
pageObj={ paged }
onChange={ this.onPaginationChange }
displayName="accessions"
sortOptions={ SORT_OPTIONS }
infinite
/>
</Grid>
</Grid>
</Grid>
<PageLayout sidebar={ <AccessionsBrowseFilters initialValues={ filter } onSubmit={ this.applyFilters } /> }>
<ContentHeader title="Accession browser" subTitle="Explore Genesys PGR database" />
<PaginationComponent
pageObj={ paged }
onChange={ this.onPaginationChange }
displayName="accessions"
sortOptions={ SORT_OPTIONS }
infinite
/>
<PrettyFilters
prefix="accessions"
filterObj={ filter }
onSubmit={ this.applyFilters }
/>
{ stillLoading ? <Loading /> :
<InfiniteLoader
isRowLoaded={ this.isRowLoaded }
loadMoreRows={ this.loadMoreRows }
rowCount={ paged.last ? list.length : list.length + 1 }
>
{ this._infiniteLoaderChildFunction }
</InfiniteLoader>
}
<PaginationComponent
pageObj={ paged }
onChange={ this.onPaginationChange }
displayName="accessions"
sortOptions={ SORT_OPTIONS }
infinite
/>
</PageLayout>
);
}
......@@ -288,7 +275,7 @@ class BrowsePage extends React.Component<IBrowsePageProps, any> {
const { displayedColumns, paged, list } = this.state;
return (
<AutoSizer disableHeight>
<AutoSizer>
{ ({ width }) => (
<VMultiGrid
{ ...list }
......
......@@ -10,6 +10,7 @@ import {loginRequest, checkTokenRequest} from 'actions/login';
import ContentHeader from 'ui/common/heading/ContentHeader';
import PageLayout from 'ui/layout/PageLayout';
import LoginForm from './c/LoginForm';
import Grid from '@material-ui/core/Grid';
import Card, { CardHeader, CardContent } from 'ui/common/Card';
......@@ -45,19 +46,19 @@ class LoginContainer extends React.Component<ILoginContainerProps, void> {
public render() {
return (
<div>
<ContentHeader title="Welcome to Genesys" subTitle="Log in to manage datasets" />
<Grid container spacing={ 0 } justify="center" className="back-gray p-20">
<Grid item xs={ 12 } md={ 5 } lg={ 4 } xl={ 3 }>
<