Commit 76f65841 authored by Andrew Hrdy's avatar Andrew Hrdy

Added routes

parent ffedfe28
This source diff could not be displayed because it is too large. You can view the blob instead.
import * as React from 'react';
import * as classNames from 'classnames';
import { removeBrackets } from '../utils/nameUtils';
import * as phoneFormatter from 'phone-formatter';
import { IFacility } from '../models/facility.model';
import { trackPiwikEvent } from '../piwik/piwik';
import { Link } from 'react-router-dom';
import TextwTitle from './TextwTitle';
import FacilitiesMap from '../containers/FacilitiesMap';
......@@ -15,7 +16,8 @@ import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import CloseIcon from '@material-ui/icons/Close';
import IconButton from '@material-ui/core/IconButton';
import { trackPiwikEvent } from '../piwik/piwik';
import FacilityDialog from './FacilityDialog';
import { History } from 'history';
class Sidebar extends React.Component<SidebarProps, SidebarState> {
......@@ -27,11 +29,6 @@ class Sidebar extends React.Component<SidebarProps, SidebarState> {
};
}
handleSidebarClose = () => {
this.props.setSelectedFacility(null);
this.props.setSidebar(false);
}
handleMapDialogClose = () => {
trackPiwikEvent('map-action', 'close');
......@@ -49,35 +46,40 @@ class Sidebar extends React.Component<SidebarProps, SidebarState> {
}
render() {
const {facility, isSidebarOpen, facilities} = this.props;
const {facility, facilities, history} = this.props;
const {mapDialogOpen} = this.state;
return (
<div
className={classNames(['card-container-offset', (isSidebarOpen && 'card-container-offset-open'), (!isSidebarOpen && 'card-container-offset-closed')])}>
<Paper
className={classNames(['sidebar-root', (isSidebarOpen && 'sidebar-open'), (!isSidebarOpen && 'sidebar-closed')])}>
<IconButton className={'sidebar-close-btn'} onClick={this.handleSidebarClose}>
<CloseIcon />
</IconButton>
<div className={'sidebar-container'}>
<Paper className={'sidebar-root'}>
<Link to="/">
<IconButton className={'sidebar-close-btn'}>
<CloseIcon />
</IconButton>
</Link>
<div className={'sidebar-row1'}>
<Avatar className={'sidebar-avatar'} src={facility.logo} />
<div className={'sidebar-title'}>
<Typography variant="h4">{removeBrackets(facility.facility_name)}</Typography>
</div>
</div>
<Divider className={'sidebar-divider'} />
<div className={'sidebar-scroll'}>
<div className={'sidebar-label-holder'}>
<TextwTitle label="Building"
content={facility.facility_location && facility.facility_location.building} />
content={facility.facility_location && facility.facility_location.building} />
<TextwTitle label="Address"
content={facility.facility_location && facility.facility_location.address} />
content={facility.facility_location && facility.facility_location.address} />
<TextwTitle label="Phone Number"
content={facility.phone_number ? phoneFormatter.format(facility.phone_number, '(NNN) NNN-NNNN') : 'Unknown'} />
content={facility.phone_number ? phoneFormatter.format(facility.phone_number, '(NNN) NNN-NNNN') : 'Unknown'} />
<TextwTitle label="Hours" content={<WeekHours facility={facility} />} />
</div>
</div>
<div className={'sidebar-row2'}>
<div className={'sidebar-map-container'} onClick={this.handleMapDialogOpen}>
<FacilitiesMap
......@@ -96,17 +98,17 @@ class Sidebar extends React.Component<SidebarProps, SidebarState> {
/>
</div>
</Paper>
<FacilityDialog facility={facility} facilities={facilities} isOpen={true} onClose={() => history.push('/')} />
</div>
);
}
}
export interface SidebarProps {
history: History;
facility: IFacility;
isSidebarOpen: boolean;
facilities: IFacility[];
setSelectedFacility: (facility: IFacility | null) => void;
setSidebar: (isOpen: boolean) => void;
}
export interface SidebarState {
......
......@@ -6,13 +6,14 @@ import FacilityUtils from '../utils/facilityUtils';
import { IFacility } from '../models/facility.model';
import { ApplicationState } from '../store';
import { Dispatch } from 'redux';
import { addFavoriteFacility, removeFavoriteFacility, setSelectedFacility, setSidebarExpansion } from '../store/ui/ui.actions';
import { trackPiwikEvent } from '../piwik/piwik';
import { addFavoriteFacility, removeFavoriteFacility } from '../store/ui/ui.actions';
import { NavLink } from 'react-router-dom';
import { withRouter, RouteComponentProps } from 'react-router';
import { Location } from 'history';
import FacilityStatus from '../components/FacilityStatus';
import FavoriteButton from '../components/FavoriteButton';
import FacilityCategory from '../components/FacilityCategory';
import FacilityDialog from '../components/FacilityDialog';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
......@@ -30,8 +31,10 @@ class FacilityCard extends React.Component<FacilityCardProps, FacilityCardState>
};
}
getSlugFromLocation = (location: Location) => location.pathname.substring(10);
shouldComponentUpdate = (nextProps: FacilityCardProps) => {
if (this.isFacilitySelected() !== (nextProps.selectedFacility.slug === this.props.facility.slug)) {
if (this.isFacilitySelected() !== (this.getSlugFromLocation(nextProps.location) === this.props.facility.slug)) {
return true;
}
......@@ -48,21 +51,10 @@ class FacilityCard extends React.Component<FacilityCardProps, FacilityCardState>
return false;
}
isFacilitySelected = (): boolean => this.props.selectedFacility.slug === this.props.facility.slug;
handleCardClick = () => {
const newState = !this.isFacilitySelected();
trackPiwikEvent('card-action', 'click');
setTimeout(() => {
this.props.setSelectedFacility(newState ? this.props.facility : null);
this.props.setSidebarExpansion(newState);
}, 0);
}
isFacilitySelected = (): boolean => this.getSlugFromLocation(this.props.location) === this.props.facility.slug;
render() {
const {facility, facilities, favorites, addFavoriteFacility, removeFavoriteFacility} = this.props;
const {facility, favorites, addFavoriteFacility, removeFavoriteFacility} = this.props;
const dayOfWeek = [6, 0, 1, 2, 3, 4, 5][new Date().getDay()];
......@@ -87,72 +79,68 @@ class FacilityCard extends React.Component<FacilityCardProps, FacilityCardState>
facility.facility_location.building;
return (
<Card onClick={this.handleCardClick} className={classNames('fc-root', this.isFacilitySelected() && 'fc-selected')}
elevation={3}>
<div className={'fc-logo-container'}>
<img className={'fc-logo'}
alt={facility.slug} src={facility.logo} />
</div>
<FavoriteButton slug={facility.slug} initialState={favorites.includes(facility.slug)}
addFavoriteFacility={addFavoriteFacility}
removeFavoriteFacility={removeFavoriteFacility} />
<CardContent className={'fc-card-content'}>
<Grid container={true} alignItems={'center'} direction={'column'}>
<Grid item={true}
className={classNames('fc-small-grid-item-spacing', 'fc-ellipsis-container', 'fc-title-container')}>
<Typography variant={'subtitle1'} align={'center'}
className={classNames('fc-title', 'fc-one-line-ellipsis')}>
{removeBrackets(facility.facility_name)}
</Typography>
<NavLink to={this.isFacilitySelected() ? '/' : `/facility/${facility.slug}`} style={{textDecoration: 'none', color: 'black'}}>
<Card className={classNames('fc-root', this.isFacilitySelected() && 'fc-selected')}
elevation={3}>
<div className={'fc-logo-container'}>
<img className={'fc-logo'}
alt={facility.slug} src={facility.logo} />
</div>
<FavoriteButton slug={facility.slug} initialState={favorites.includes(facility.slug)}
addFavoriteFacility={addFavoriteFacility}
removeFavoriteFacility={removeFavoriteFacility} />
<CardContent className={'fc-card-content'}>
<Grid container={true} alignItems={'center'} direction={'column'}>
<Grid item={true}
className={classNames('fc-small-grid-item-spacing', 'fc-ellipsis-container', 'fc-title-container')}>
<Typography variant={'subtitle1'} align={'center'}
className={classNames('fc-title', 'fc-one-line-ellipsis')}>
{removeBrackets(facility.facility_name)}
</Typography>
</Grid>
<Grid item={true} className={'fc-small-grid-item-spacing'}>
<FacilityCategory category={facility.facility_category} />
</Grid>
<Grid item={true} className={'fc-small-grid-item-spacing fc-display-hours'}>
<Typography variant={'body1'}>
{`Today: ${getDisplayHours()}`}
</Typography>
</Grid>
</Grid>
<Grid item={true} className={'fc-small-grid-item-spacing'}>
<FacilityCategory category={facility.facility_category} />
</Grid>
<Grid item={true} className={'fc-small-grid-item-spacing fc-display-hours'}>
<Typography variant={'body1'}>
{`Today: ${getDisplayHours()}`}
</Typography>
<Grid container={true} justify={'space-around'}>
<Grid item={true} className={'fc-extra-info'}>
<FacilityStatus facility={facility} />
</Grid>
<Grid item={true} className={'fc-extra-info'}>
<Typography variant={'caption'}>
<LocationOnIcon className={'fc-card-map-marker-icon'} />
</Typography>
<Typography title={buildingName} variant={'caption'} align={'center'}
className={'fc-two-line-ellipsis'}>
{buildingName}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid container={true} justify={'space-around'}>
<Grid item={true} className={'fc-extra-info'}>
<FacilityStatus facility={facility} />
</Grid>
<Grid item={true} className={'fc-extra-info'}>
<Typography variant={'caption'}>
<LocationOnIcon className={'fc-card-map-marker-icon'} />
</Typography>
<Typography title={buildingName} variant={'caption'} align={'center'}
className={'fc-two-line-ellipsis'}>
{buildingName}
</Typography>
</Grid>
</Grid>
</CardContent>
<FacilityDialog facility={facility} facilities={facilities} isOpen={this.isFacilitySelected()}
onClose={this.handleCardClick} />
</Card>
</CardContent>
</Card>
</NavLink>
);
}
}
export interface FacilityCardProps {
export interface FacilityCardProps extends RouteComponentProps<{slug: string}> {
facility: IFacility;
facilities: IFacility[];
favorites: string[];
selectedFacility: IFacility;
setSelectedFacility: (facility: IFacility) => void;
addFavoriteFacility: (slug: string) => void;
removeFavoriteFacility: (slug: string) => void;
setSidebarExpansion: (isOpen: boolean) => void;
}
export interface FacilityCardState {
......@@ -160,15 +148,12 @@ export interface FacilityCardState {
}
const mapStateToProps = (state: ApplicationState) => ({
favorites: state.ui.favorites,
selectedFacility: state.ui.selectedFacility
favorites: state.ui.favorites
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
setSelectedFacility: (facility: IFacility) => dispatch(setSelectedFacility(facility)),
addFavoriteFacility: (slug: string) => dispatch(addFavoriteFacility(slug)),
removeFavoriteFacility: (slug: string) => dispatch(removeFavoriteFacility(slug)),
setSidebarExpansion: (isOpen: boolean) => dispatch(setSidebarExpansion(isOpen))
removeFavoriteFacility: (slug: string) => dispatch(removeFavoriteFacility(slug))
});
export default connect(mapStateToProps, mapDispatchToProps)(FacilityCard);
\ No newline at end of file
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(FacilityCard));
\ No newline at end of file
......@@ -6,7 +6,7 @@ import { ApplicationState } from '../store';
import { Dispatch } from 'redux';
import { fetchFacilities } from '../store/facility/facility.actions';
import { fetchAlerts } from '../store/alert/alert.actions';
import { setSidebarExpansion, setSelectedFacility } from '../store/ui/ui.actions';
import { Route, withRouter, RouteComponentProps } from 'react-router-dom';
import CardContainer from '../components/CardContainer';
import AppBar from '../components/AppBar';
......@@ -23,11 +23,12 @@ class Layout extends React.Component<LayoutProps> {
}
render() {
const {isSidebarOpen, selectedFacility, facilities, searchTerm, campusRegion, setSidebarExpansion, setSelectedFacility} = this.props;
const {facilities, searchTerm, campusRegion, history} = this.props;
return (
<div className={'layout-root'}>
<AppBar />
<div className={'layout-container'}>
<div className={'layout-main-content'}>
<div className={'layout-card-container'}>
......@@ -36,26 +37,23 @@ class Layout extends React.Component<LayoutProps> {
</div>
</div>
<Sidebar facilities={facilities} facility={selectedFacility} isSidebarOpen={isSidebarOpen}
setSidebar={setSidebarExpansion} setSelectedFacility={setSelectedFacility}/>
<Route path="/facility/:slug" render={(props) => {
return <Sidebar facility={facilities.find(facility => facility.slug === props.match.params.slug)} facilities={facilities} history={history} />;
}} />
</div>
</div>
);
}
}
export interface LayoutProps {
export interface LayoutProps extends RouteComponentProps {
facilities: IFacility[];
alerts: IAlert[];
favorites: string[];
searchTerm: string;
campusRegion: CampusRegion;
selectedFacility: IFacility;
isSidebarOpen: boolean;
fetchFacilities: () => any;
fetchAlerts: () => any;
setSidebarExpansion: (isOpen: boolean) => any;
setSelectedFacility: (facility: IFacility) => any;
}
const mapStateToProps = (state: ApplicationState) => ({
......@@ -63,16 +61,12 @@ const mapStateToProps = (state: ApplicationState) => ({
alerts: state.alerts.alerts,
favorites: state.ui.favorites,
searchTerm: state.ui.search.searchTerm,
campusRegion: state.ui.search.campusRegion,
selectedFacility: state.ui.selectedFacility,
isSidebarOpen: state.ui.sidebar.isOpen
campusRegion: state.ui.search.campusRegion
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
fetchFacilities: () => dispatch(fetchFacilities()),
fetchAlerts: () => dispatch(fetchAlerts()),
setSidebarExpansion: (isOpen: boolean) => dispatch(setSidebarExpansion(isOpen)),
setSelectedFacility: (facility: IFacility) => dispatch(setSelectedFacility(facility))
fetchAlerts: () => dispatch(fetchAlerts())
});
export default connect(mapStateToProps, mapDispatchToProps)(Layout);
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Layout));
......@@ -2,8 +2,6 @@ import { IFacility, CampusRegion } from '../../models/facility.model';
import { Action } from 'redux';
export enum UiActionTypes {
SET_SIDEBAR_EXPANSION = 'SET_SIDEBAR_EXPANSION',
SET_SELECTED_FACILITY = 'SET_SELECTED_FACILITY',
SET_SEARCH_TERM = 'SET_SEARCH_TERM',
SET_SELECTED_CAMPUS_REGION = 'SET_SELECTED_CAMPUS_REGION',
ADD_FAVORITE_FACILITY = 'ADD_FAVORITE_FACILITY',
......@@ -11,16 +9,6 @@ export enum UiActionTypes {
SET_FAVORITE_FACILITIES = 'SET_FAVORITE_FACILITIES'
}
export interface SetSidebarExpansion extends Action<UiActionTypes> {
type: UiActionTypes.SET_SIDEBAR_EXPANSION;
isOpen: boolean;
}
export interface SetSelectedFacility extends Action<UiActionTypes> {
type: UiActionTypes.SET_SELECTED_FACILITY;
facility: IFacility;
}
export interface SetSearchTerm extends Action<UiActionTypes> {
type: UiActionTypes.SET_SEARCH_TERM;
searchTerm: string;
......@@ -47,8 +35,6 @@ export interface SetFavoriteFacilities extends Action<UiActionTypes> {
}
export type UiAction =
SetSidebarExpansion |
SetSelectedFacility |
SetSearchTerm |
SetSelectedCampusRegion |
AddFavoriteFacility |
......
import { SetSidebarExpansion, UiActionTypes, SetSelectedFacility, SetSelectedCampusRegion, SetSearchTerm, AddFavoriteFacility, RemoveFavoriteFacility, SetFavoriteFacilities } from './ui.action-types';
import { IFacility, CampusRegion } from '../../models/facility.model';
export const setSidebarExpansion = (isOpen: boolean): SetSidebarExpansion => ({
type: UiActionTypes.SET_SIDEBAR_EXPANSION,
isOpen: isOpen
});
export const setSelectedFacility = (facility: IFacility): SetSelectedFacility => ({
type: UiActionTypes.SET_SELECTED_FACILITY,
facility: facility
});
import { UiActionTypes, SetSelectedCampusRegion, SetSearchTerm, AddFavoriteFacility, RemoveFavoriteFacility, SetFavoriteFacilities } from './ui.action-types';
import { CampusRegion } from '../../models/facility.model';
export const setSearchTerm = (searchTerm: string): SetSearchTerm => ({
type: UiActionTypes.SET_SEARCH_TERM,
......
......@@ -7,38 +7,12 @@ export interface SearchBarState {
campusRegion: CampusRegion;
}
export interface SidebarState {
isOpen: boolean;
}
export interface UiState {
selectedFacility: IFacility | {};
sidebar: SidebarState;
search: SearchBarState;
favorites: string[];
}
const selectedFacilityReducer = (state: IFacility | {} = {}, action: UiAction): IFacility | {} => {
switch (action.type) {
case UiActionTypes.SET_SELECTED_FACILITY:
return action.facility || {};
default:
return state;
}
};
const sidebarReducer = (state: SidebarState = {isOpen: false}, action: UiAction): SidebarState => {
switch (action.type) {
case UiActionTypes.SET_SIDEBAR_EXPANSION:
return {
...state,
isOpen: action.isOpen
};
default:
return state;
}
};
const searchReducer = (state: SearchBarState = {searchTerm: '', campusRegion: CampusRegion.Fairfax}, action: UiAction): SearchBarState => {
switch (action.type) {
case UiActionTypes.SET_SEARCH_TERM:
......@@ -68,8 +42,6 @@ const favoritesReducer = (state: string[] = [], action: UiAction): string[] => {
};
export const uiReducer = combineReducers<UiState>({
selectedFacility: selectedFacilityReducer,
sidebar: sidebarReducer,
search: searchReducer,
favorites: favoritesReducer
});
\ No newline at end of file
......@@ -14,14 +14,6 @@
margin-bottom: 16px;
}
.sidebar-open {
transform: translateX(0px);
}
.sidebar-closed {
transform: translateX(400px);
}
.sidebar-root {
overflow: hidden;
display: block;
......@@ -47,18 +39,8 @@
display: none;
}
.card-container-offset-open {
.sidebar-container {
flex: 1 0 400px;
}
.card-container-offset-closed {
flex: 1 0 0;
}
.card-container-offset {
transition-delay: 100ms;
}
.sidebar-divider {
......@@ -116,7 +98,7 @@
display: none !important;
}
.card-container-offset {
.sidebar-container {
display: none !important;
}
}
\ No newline at end of file
......@@ -11,5 +11,5 @@ module.exports = {
appBuild: resolveApp('build'),
appSrc: resolveApp('src'),
appOutputJs: 'static/js/bundle.js',
publicPath: ''
publicPath: '/'
};
\ No newline at end of file
......@@ -38,6 +38,7 @@ module.exports = {
contentBase: paths.appBuild,
hot: true,
port: 3000,
compress: true
compress: true,
historyApiFallback: true
}
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment