From b68f521485ebd5907751ca865f7a88d52ba4ce21 Mon Sep 17 00:00:00 2001 From: tan95 Date: Tue, 13 Feb 2018 00:27:29 -0800 Subject: [PATCH 1/9] initial login implemented, with bugs for chrome. --- client/components/OpenIDAuth/callback.js | 22 +++++++++++ client/components/OpenIDAuth/login.js | 23 +++++++++++ client/components/OpenIDAuth/navUserButton.js | 24 ++++++++++++ .../OpenIDAuth/silent_renew/callback.js | 17 ++++++++ .../OpenIDAuth/silent_renew/index.js | 3 ++ .../OpenIDAuth/silent_renew/silent_renew.html | 1 + client/components/OpenIDAuth/user.js | 31 +++++++++++++++ client/containers/App/index.js | 2 + client/index.js | 9 ++++- client/reducers/index.js | 5 +++ client/routes/index.js | 19 +++++++++ client/store/index.js | 7 ++++ client/utils/api.js | 39 +++++++++++++++++++ client/utils/userManager.js | 17 ++++++++ package.json | 4 ++ 15 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 client/components/OpenIDAuth/callback.js create mode 100644 client/components/OpenIDAuth/login.js create mode 100644 client/components/OpenIDAuth/navUserButton.js create mode 100644 client/components/OpenIDAuth/silent_renew/callback.js create mode 100644 client/components/OpenIDAuth/silent_renew/index.js create mode 100644 client/components/OpenIDAuth/silent_renew/silent_renew.html create mode 100644 client/components/OpenIDAuth/user.js create mode 100644 client/routes/index.js create mode 100644 client/utils/api.js create mode 100644 client/utils/userManager.js diff --git a/client/components/OpenIDAuth/callback.js b/client/components/OpenIDAuth/callback.js new file mode 100644 index 0000000..525fb60 --- /dev/null +++ b/client/components/OpenIDAuth/callback.js @@ -0,0 +1,22 @@ +import React from "react"; +import { connect } from "react-redux"; +import { CallbackComponent } from "redux-oidc"; +import { push } from "react-router-redux"; +import userManager from "../../utils/userManager"; + +class CallbackPage extends React.Component { + render() { + // just redirect to '/' in both cases + return ( + this.props.dispatch(push("/"))} + errorCallback={() => this.props.dispatch(push("/"))} + > +
Redirecting...
+
+ ); + } +} + +export default connect()(CallbackPage); diff --git a/client/components/OpenIDAuth/login.js b/client/components/OpenIDAuth/login.js new file mode 100644 index 0000000..e2b2a53 --- /dev/null +++ b/client/components/OpenIDAuth/login.js @@ -0,0 +1,23 @@ +import React from "react"; +import userManager from "../../utils/userManager"; +import Button from 'react-md/lib/Buttons'; + +class Login extends React.Component { + onLoginButtonClick(event) { + event.preventDefault(); + userManager.signinRedirect(); + } + + render() { + return ( + + + + + + ); + } +}; \ No newline at end of file diff --git a/client/components/RoutingProfileManager/ProfileList.js b/client/components/RoutingProfileManager/ProfileList.js new file mode 100644 index 0000000..7d306fe --- /dev/null +++ b/client/components/RoutingProfileManager/ProfileList.js @@ -0,0 +1,115 @@ +/* eslint-disable react/no-array-index-key */ +import React, { PureComponent } from 'react'; +import { + Card, + CardText, + CardTitle, +} from 'react-md/lib/Cards'; +import { Button } from 'react-md/lib/Buttons'; +import { Collapse } from 'react-md/lib/Helpers'; +import { CircularProgress } from 'react-md/lib/Progress'; +import { loadUserProfiles } from '../../utils/api'; +import ProfileEntry from './ProfileEntry'; + +const ACCESSIBILITY_PROPS = { + 'aria-busy': true, + 'aria-describedby': 'fake-feed-loading-progress', +}; + +export default class ProfileList extends PureComponent { + static propTypes = {}; + + constructor (props) { + super(); + + this.state = { + notRefreshing: true, + contents: [], + }; + } + + componentWillMount () { + this.refreshContent(); + } + + refreshContent = () => { + this.setState({notRefreshing: false}); + + loadUserProfiles().then(result => { + if (result.error != null) { + this.setState({contents: null, notRefreshing: true}); + return; + } + const profiles = result.data; + this.setState({contents: [], notRefreshing: true}); + this.setState({contents: profiles, notRefreshing: true}); + }); + }; + + render () { + const {notRefreshing, contents} = this.state; + + let accessibilityProps; + if (!notRefreshing) { + accessibilityProps = ACCESSIBILITY_PROPS; + } + + let cards; + if (contents == null) { + cards = ( + + + Error loading profile. + + + ); + } else if (contents.length < 1) { + cards = ( + + + No Profile Found + + + ); + } else { + cards = contents.map(({ + profileID, + userID, + profileName, + inclineMin, + inclineMax, + inclineIdeal, + avoidCurbs, + avoidConstruction, + }) => ( + + )); + } + + const refresh = ; + return ( +
+ {refresh} + +
+ +
+
+
+ {cards} +
+
+ ); + } +} \ No newline at end of file diff --git a/client/components/RoutingProfileManager/index.js b/client/components/RoutingProfileManager/index.js new file mode 100644 index 0000000..e19fc07 --- /dev/null +++ b/client/components/RoutingProfileManager/index.js @@ -0,0 +1,55 @@ +/* WithScrollingContent.jsx */ +/* eslint-disable react/no-array-index-key */ +import React from 'react'; +import { connect } from 'react-redux'; +import { DialogContainer } from 'react-md/lib/Dialogs'; +import * as AppActions from '../../actions'; +import { bindActionCreators } from 'redux'; +import ProfileList from './ProfileList' + +function RoutingProfileManager (props) { + const { + actions, + visible, + } = props; + + const contentProps = {id: 'scrolling-content-dialog-content'}; + + return ( + {}} + actions={[ + { + label: 'Close', + primary: true, + onClick: actions.closeRoutingProfileManager, + }]} + width={800} + contentProps={contentProps} + > + + + ); +} + +function mapStateToProps (state) { + return { + visible: state.viewVisibility.showRoutingProfilePane, + preferences: state.userpreference, + }; +} + +function mapDispatchToProps (dispatch) { + return { + actions: bindActionCreators(AppActions, dispatch) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(RoutingProfileManager); \ No newline at end of file diff --git a/client/containers/App/index.js b/client/containers/App/index.js index cd7f7b8..f8b29b4 100644 --- a/client/containers/App/index.js +++ b/client/containers/App/index.js @@ -11,6 +11,8 @@ import Button from 'react-md/lib/Buttons'; import Toolbar from 'react-md/lib/Toolbars'; import UserMenu from '../../components/OpenIDAuth/'; +import UserPreferences from '../../components/Preferences' +import RoutingProfileManager from '../../components/RoutingProfileManager' import AccessMap from 'containers/AccessMap'; import FloatingButtons from 'containers/FloatingButtons'; @@ -109,6 +111,8 @@ class App extends Component { + + ); } diff --git a/client/reducers/defaults.js b/client/reducers/defaults.js index f0704e7..c5dca6f 100644 --- a/client/reducers/defaults.js +++ b/client/reducers/defaults.js @@ -53,3 +53,14 @@ export const defaultMode = 'UPHILL'; export const defaultBrowser = { mediaType: null, }; + +export const defaultUserPreferences = { + showUserSettingsPane: false, + enableTracking: true, + routingProfiles:[], +}; + +export const defaultViewVisbility = { + showUserSettingsPane: false, + showRoutingProfileManager: false, +}; \ No newline at end of file diff --git a/client/reducers/index.js b/client/reducers/index.js index 8701d0d..9a1e47d 100644 --- a/client/reducers/index.js +++ b/client/reducers/index.js @@ -13,6 +13,8 @@ import waypoints from './waypoints'; import { routerReducer } from 'react-router-redux'; import { reducer as oidcReducer } from 'redux-oidc'; +import userPreference from './user-preference'; +import viewVisibility from './view-visibility'; /** * Routing to be implemented @@ -30,4 +32,6 @@ export default combineReducers({ waypoints, routing: routerReducer, oidc: oidcReducer, + userpreference: userPreference, + viewVisibility: viewVisibility, }); diff --git a/client/reducers/user-preference.js b/client/reducers/user-preference.js new file mode 100644 index 0000000..0de28eb --- /dev/null +++ b/client/reducers/user-preference.js @@ -0,0 +1,19 @@ +import { + TOGGLE_TRACKING, +} from 'actions'; + +import { defaultUserPreferences as defaults } from './defaults'; +import { combineReducers } from 'redux'; + +function handleUserTracking(state = defaults.enableTracking, action) { + switch (action.type) { + case TOGGLE_TRACKING: + return !state; + default: + return state; + } +} + +export default combineReducers({ + enableTracking: handleUserTracking, +}); \ No newline at end of file diff --git a/client/reducers/view-visibility.js b/client/reducers/view-visibility.js new file mode 100644 index 0000000..06f06c3 --- /dev/null +++ b/client/reducers/view-visibility.js @@ -0,0 +1,35 @@ +import { + OPEN_USER_SETTINGS_PANE, + CLOSE_USER_SETTINGS_PANE, + OPEN_PROFILE_MANAGER_PANE, + CLOSE_PROFILE_MANAGER_PANE +} from 'actions'; +import { defaultViewVisbility as defaults } from './defaults'; +import { combineReducers } from 'redux'; + +function handleUserSettingsPane(state = defaults.showUserSettingsPane, action) { + switch (action.type) { + case OPEN_USER_SETTINGS_PANE: + return true; + case CLOSE_USER_SETTINGS_PANE: + return false; + default: + return state; + } +} + +function handleRoutingProfileManagerPane(state = defaults.showRoutingProfileManager, action) { + switch (action.type) { + case OPEN_PROFILE_MANAGER_PANE: + return true; + case CLOSE_PROFILE_MANAGER_PANE: + return false; + default: + return state; + } +} + +export default combineReducers({ + showUserSettingsPane: handleUserSettingsPane, + showRoutingProfilePane: handleRoutingProfileManagerPane +}); \ No newline at end of file diff --git a/client/store/index.js b/client/store/index.js index 1d9b417..6eecbbd 100644 --- a/client/store/index.js +++ b/client/store/index.js @@ -28,7 +28,7 @@ if (process.env.NODE_ENV === 'development') { }); const analyticsMiddleware = analytics(({ type, payload }, state) => { - if (state.userSettings.track || process.env.NODE_ENV === 'development') { + if (state.userpreference.enableTracking || process.env.NODE_ENV === 'development') { rakam.logEvent(type, { ...state.analytics, ...payload }); } }); diff --git a/client/utils/api.js b/client/utils/api.js index be107a6..4e91385 100644 --- a/client/utils/api.js +++ b/client/utils/api.js @@ -12,16 +12,38 @@ export function loadUserInfo() { }); } +export function loadUserProfiles() { + const url = + "http://localhost:4040/api/profiles"; + + return apiRequest(url); +} + +export function deleteUserProfile(profileID) { + const url = + `http://localhost:4040/api/profile?profileID=${profileID}`; + + return apiRequest(url, "DELETE"); +} + +export function updateUserProfile(profileID, newProfile) { + const url = + `http://localhost:4040/api/profile?profileID=${profileID}`; + + return apiRequest(url, "POST", newProfile); +} + // a request helper which reads the access_token from the redux state and passes it in its HTTP request -function apiRequest(url, method = "GET") { +function apiRequest(url, method = "GET", body = undefined) { const token = store.getState().oidc.user.access_token; const headers = new Headers(); - headers.append("Accept", "application/json"); + headers.append("Content-Type", "application/json"); headers.append("Authorization", `Bearer ${token}`); const options = { method, - headers + headers, + body: body ? JSON.stringify(body) : undefined, }; return fetch(url, options) From daffeebae867d2bc12b18c936f76e59ea32dd551 Mon Sep 17 00:00:00 2001 From: tan95 Date: Sat, 17 Feb 2018 12:31:14 -0800 Subject: [PATCH 9/9] initial implemented profile selector --- client/actions/index.js | 67 ++++++++++++++ .../RoutingProfileManager/ProfileEntry.js | 4 +- .../RoutingProfileManager/ProfileList.js | 68 ++++++++------ .../components/RoutingProfileManager/index.js | 14 ++- .../RoutingProfileSelector/index.js | 92 +++++++++++++++++++ client/containers/OmniCard/index.js | 3 + client/reducers/defaults.js | 2 + client/reducers/routing-profile.js | 7 ++ client/reducers/user-preference.js | 40 +++++++- client/utils/api.js | 7 ++ 10 files changed, 270 insertions(+), 34 deletions(-) create mode 100644 client/components/RoutingProfileSelector/index.js diff --git a/client/actions/index.js b/client/actions/index.js index 82b1558..c7d402f 100644 --- a/client/actions/index.js +++ b/client/actions/index.js @@ -70,8 +70,75 @@ export const OPEN_USER_SETTINGS_PANE = 'OPEN_USER_SETTINGS_PANE'; export const CLOSE_USER_SETTINGS_PANE = 'CLOSE_USER_SETTINGS_PANE'; export const OPEN_PROFILE_MANAGER_PANE = 'OPEN_PROFILE_MANAGER_PANE'; export const CLOSE_PROFILE_MANAGER_PANE = 'CLOSE_PROFILE_MANAGER_PANE'; +export const FETCHING_USER_ROUTING_PROFILE = 'FETCHING_USER_ROUTING_PROFILE'; +export const FINISHED_FETCHING_USER_ROUTING_PROFILE = 'FINISHED_FETCHING_USER_ROUTING_PROFILE'; +export const UPDATE_USER_ROUTING_PROFILE = 'UPDATE_USER_ROUTING_PROFILE'; +export const CHANGE_USER_ROUTING_PROFILE_SELECTION = 'CHANGE_USER_ROUTING_PROFILE_SELECTION'; // Action creators +export function changeUserRoutingProfileSelection (selectedIndex) { + return { + type: CHANGE_USER_ROUTING_PROFILE_SELECTION, + payload: selectedIndex, + meta: { + analytics: { + type: 'change-user-routing-profile-selection', + } + } + }; +} + +export function refreshUserRoutingProfiles() { + return (dispatch) => { + dispatch(isFetchingUserRoutingProfiles(true)); + const loadUserProfiles = require('../utils/api').loadUserProfiles; + loadUserProfiles().then(result => { + if (result.error != null) { + dispatch(updateUserRoutingProfiles(null)); + dispatch(isFetchingUserRoutingProfiles(false)); + return; + } + const profiles = result.data; + dispatch(updateUserRoutingProfiles([])); + dispatch(updateUserRoutingProfiles(profiles)); + dispatch(isFetchingUserRoutingProfiles(false)); + }); + }; +} + +export function isFetchingUserRoutingProfiles(isFetching) { + if (isFetching) { + return { + type: FETCHING_USER_ROUTING_PROFILE, + meta: { + analytics: { + type: 'fetching-user-routing-profile', + } + } + }; + } + return { + type: FINISHED_FETCHING_USER_ROUTING_PROFILE, + meta: { + analytics: { + type: 'finished-fetching-user-routing-profile', + } + } + }; +} + +export function updateUserRoutingProfiles(profiles) { + return { + type: UPDATE_USER_ROUTING_PROFILE, + payload: profiles, + meta: { + analytics: { + type: 'update-user-routing-profile', + } + } + }; +} + export function openRoutingProfileManager() { return { type: OPEN_PROFILE_MANAGER_PANE, diff --git a/client/components/RoutingProfileManager/ProfileEntry.js b/client/components/RoutingProfileManager/ProfileEntry.js index cd81f7f..c92fcb1 100644 --- a/client/components/RoutingProfileManager/ProfileEntry.js +++ b/client/components/RoutingProfileManager/ProfileEntry.js @@ -111,11 +111,11 @@ export default class ProfileEntry extends PureComponent { const curbrampToggle = ( this.setState({tempAvoidCurbs: !d})} + onChange={d => this.setState({tempAvoidCurbs: d})} /> ); return ( diff --git a/client/components/RoutingProfileManager/ProfileList.js b/client/components/RoutingProfileManager/ProfileList.js index 7d306fe..c268e02 100644 --- a/client/components/RoutingProfileManager/ProfileList.js +++ b/client/components/RoutingProfileManager/ProfileList.js @@ -10,6 +10,7 @@ import { Collapse } from 'react-md/lib/Helpers'; import { CircularProgress } from 'react-md/lib/Progress'; import { loadUserProfiles } from '../../utils/api'; import ProfileEntry from './ProfileEntry'; +import PropTypes from 'prop-types'; const ACCESSIBILITY_PROPS = { 'aria-busy': true, @@ -17,33 +18,46 @@ const ACCESSIBILITY_PROPS = { }; export default class ProfileList extends PureComponent { - static propTypes = {}; + static propTypes = { + refreshProfileHandler: PropTypes.func.isRequired, + refreshStatusIndicator: PropTypes.bool.isRequired, + profileArray: PropTypes.array.isRequired, + }; constructor (props) { super(); this.state = { notRefreshing: true, - contents: [], + contents: props.profileArray, + refreshProfileHandler: props.refreshProfileHandler, + refreshStatusIndicator: props.refreshStatusIndicator, }; } + componentWillReceiveProps (newProps) { + if (newProps.profileArray !== this.props.profileArray) { + this.setState({contents: newProps.profileArray}); + } + } + componentWillMount () { this.refreshContent(); } refreshContent = () => { - this.setState({notRefreshing: false}); - - loadUserProfiles().then(result => { - if (result.error != null) { - this.setState({contents: null, notRefreshing: true}); - return; - } - const profiles = result.data; - this.setState({contents: [], notRefreshing: true}); - this.setState({contents: profiles, notRefreshing: true}); - }); + // this.setState({notRefreshing: false}); + // + // loadUserProfiles().then(result => { + // if (result.error != null) { + // this.setState({contents: null, notRefreshing: true}); + // return; + // } + // const profiles = result.data; + // this.setState({contents: [], notRefreshing: true}); + // this.setState({contents: profiles, notRefreshing: true}); + // }); + this.state.refreshProfileHandler(); }; render () { @@ -82,26 +96,26 @@ export default class ProfileList extends PureComponent { avoidCurbs, avoidConstruction, }) => ( - - )); + + )); } const refresh = ; + disabled={this.state.refreshStatusIndicator}>Refresh; return (
{refresh} - +
diff --git a/client/components/RoutingProfileManager/index.js b/client/components/RoutingProfileManager/index.js index e19fc07..d5b428f 100644 --- a/client/components/RoutingProfileManager/index.js +++ b/client/components/RoutingProfileManager/index.js @@ -1,16 +1,17 @@ -/* WithScrollingContent.jsx */ /* eslint-disable react/no-array-index-key */ import React from 'react'; import { connect } from 'react-redux'; import { DialogContainer } from 'react-md/lib/Dialogs'; import * as AppActions from '../../actions'; import { bindActionCreators } from 'redux'; -import ProfileList from './ProfileList' +import ProfileList from './ProfileList'; function RoutingProfileManager (props) { const { actions, visible, + preferences, + userRoutingProfiles, } = props; const contentProps = {id: 'scrolling-content-dialog-content'}; @@ -31,7 +32,11 @@ function RoutingProfileManager (props) { width={800} contentProps={contentProps} > - + ); } @@ -39,13 +44,14 @@ function RoutingProfileManager (props) { function mapStateToProps (state) { return { visible: state.viewVisibility.showRoutingProfilePane, + userRoutingProfiles: state.userpreference.userRoutingProfiles, preferences: state.userpreference, }; } function mapDispatchToProps (dispatch) { return { - actions: bindActionCreators(AppActions, dispatch) + actions: bindActionCreators(AppActions, dispatch), }; } diff --git a/client/components/RoutingProfileSelector/index.js b/client/components/RoutingProfileSelector/index.js new file mode 100644 index 0000000..32c0254 --- /dev/null +++ b/client/components/RoutingProfileSelector/index.js @@ -0,0 +1,92 @@ +/* eslint-disable react/no-array-index-key */ +import React from 'react'; +import { connect } from 'react-redux'; +import * as AppActions from '../../actions'; +import { bindActionCreators } from 'redux'; + +import List from 'react-md/lib/Lists'; +import SelectField from 'react-md/lib/SelectFields'; +import Button from 'react-md/lib/Buttons'; +import { createUserProfile } from '../../utils/api'; + +export function RoutingProfileSelector (props) { + const { + actions, + userRoutingProfiles, + currentlySelectedProfileIndex, + user, + currentActivatedProfile + } = props; + + const profileItems = []; + for (let i = 0; i < userRoutingProfiles.length; i++) { + profileItems.push({ + label: String(userRoutingProfiles[i].profileName), + value: i, + }); + } + + if (!user) { + return (
); + } + + return ( +
+ { + actions.changeUserRoutingProfileSelection(idx); + actions.setProfile({ + profileName: userRoutingProfiles[idx].profileName, + inclineIdeal: -0.01, + inclineMax: userRoutingProfiles[idx].inclineMax, + inclineMin: userRoutingProfiles[idx].inclineMin, + requireCurbRamps: userRoutingProfiles[idx].avoidCurbs, + }); + }} + onClick={() => actions.refreshUserRoutingProfiles()} + /> + +
+ ); +} + +function mapStateToProps (state) { + return { + userRoutingProfiles: state.userpreference.userRoutingProfiles, + currentlySelectedProfileIndex: state.userpreference.currentlySelectedProfileIndex, + currentActivatedProfile: state.routingprofile, + user: state.oidc.user, + }; +} + +function mapDispatchToProps (dispatch) { + return { + actions: bindActionCreators(AppActions, dispatch), + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(RoutingProfileSelector); \ No newline at end of file diff --git a/client/containers/OmniCard/index.js b/client/containers/OmniCard/index.js index 4c1978c..fef771d 100644 --- a/client/containers/OmniCard/index.js +++ b/client/containers/OmniCard/index.js @@ -24,6 +24,8 @@ import CaneUserIcon from 'components/Icons/CaneUserIcon'; import PoweredWheelchairIcon from 'components/Icons/PoweredWheelchairIcon'; import WheelchairIcon from 'components/Icons/WheelchairIcon'; +import RoutingProfileSelector from 'components/RoutingProfileSelector'; + import './style.scss'; const OmniCard = (props) => { @@ -247,6 +249,7 @@ const OmniCard = (props) => { > settings + ); diff --git a/client/reducers/defaults.js b/client/reducers/defaults.js index c5dca6f..160471b 100644 --- a/client/reducers/defaults.js +++ b/client/reducers/defaults.js @@ -58,6 +58,8 @@ export const defaultUserPreferences = { showUserSettingsPane: false, enableTracking: true, routingProfiles:[], + currentlySelectedProfileIndex: -1, + fetchingUserRoutingProfiles: false, }; export const defaultViewVisbility = { diff --git a/client/reducers/routing-profile.js b/client/reducers/routing-profile.js index 4f0c58f..160c5d2 100644 --- a/client/reducers/routing-profile.js +++ b/client/reducers/routing-profile.js @@ -34,6 +34,8 @@ const handleInclineIdeal = (state = defaults.inclineIdeal, action) => { case 'cane': case 'custom': return -0.01 + default: + return action.payload.inclineIdeal; } case SET_INCLINE_IDEAL: return action.payload; @@ -55,6 +57,7 @@ const handleInclineMax = (state = defaults.inclineMax, action) => { case 'custom': return state; default: + return action.payload.inclineMax; } case SET_INCLINE_MAX: return action.payload; @@ -75,6 +78,8 @@ const handleInclineMin = (state = defaults.inclineMin, action) => { profiles.cane.inclineMin; case 'custom': return state; + default: + return action.payload.inclineMin; } case SET_INCLINE_MIN: return action.payload; @@ -95,6 +100,8 @@ const handleCurbRamps = (state = defaults.requireCurbRamps, action) => { return false case 'custom': return state; + default: + return action.payload.requireCurbRamps; } case TOGGLE_CURBRAMPS: return !state; diff --git a/client/reducers/user-preference.js b/client/reducers/user-preference.js index 0de28eb..5f36f46 100644 --- a/client/reducers/user-preference.js +++ b/client/reducers/user-preference.js @@ -1,11 +1,15 @@ import { TOGGLE_TRACKING, + UPDATE_USER_ROUTING_PROFILE, + FETCHING_USER_ROUTING_PROFILE, + FINISHED_FETCHING_USER_ROUTING_PROFILE, + CHANGE_USER_ROUTING_PROFILE_SELECTION, } from 'actions'; import { defaultUserPreferences as defaults } from './defaults'; import { combineReducers } from 'redux'; -function handleUserTracking(state = defaults.enableTracking, action) { +function handleUserTracking (state = defaults.enableTracking, action) { switch (action.type) { case TOGGLE_TRACKING: return !state; @@ -14,6 +18,40 @@ function handleUserTracking(state = defaults.enableTracking, action) { } } +function handleUpdateUserRoutingProfiles ( + state = defaults.routingProfiles, action) { + switch (action.type) { + case UPDATE_USER_ROUTING_PROFILE: + return action.payload; + default: + return state; + } +} + +function handleFetchingUserRoutingProfiles ( + state = defaults.fetchingUserRoutingProfiles, action) { + switch (action.type) { + case FETCHING_USER_ROUTING_PROFILE: + return true; + case FINISHED_FETCHING_USER_ROUTING_PROFILE: + return false; + default: + return state; + } +} + +function handleChangeUserRoutingProfileSelection ( + state = defaults.fetchingUserRoutingProfiles, action) { + switch (action.type) { + case CHANGE_USER_ROUTING_PROFILE_SELECTION: + return action.payload; + default: + return state; + } +} + export default combineReducers({ enableTracking: handleUserTracking, + userRoutingProfiles: handleUpdateUserRoutingProfiles, + fetchingUserRoutingProfiles: handleFetchingUserRoutingProfiles, }); \ No newline at end of file diff --git a/client/utils/api.js b/client/utils/api.js index 4e91385..381f9fa 100644 --- a/client/utils/api.js +++ b/client/utils/api.js @@ -30,6 +30,13 @@ export function updateUserProfile(profileID, newProfile) { const url = `http://localhost:4040/api/profile?profileID=${profileID}`; + return apiRequest(url, "PUT", newProfile); +} + +export function createUserProfile(newProfile) { + const url = + `http://localhost:4040/api/profile`; + return apiRequest(url, "POST", newProfile); }