Skip to content

Instantly share code, notes, and snippets.

@marr
Created February 17, 2016 19:18
Show Gist options
  • Save marr/ca8e6ec07aa51bc1e39d to your computer and use it in GitHub Desktop.
Save marr/ca8e6ec07aa51bc1e39d to your computer and use it in GitHub Desktop.
diff --git c/app/app.js w/app/app.js
index 171ab05..1dfe5f5 100644
--- c/app/app.js
+++ w/app/app.js
@@ -8,8 +8,6 @@ import { routeActions, syncHistory } from 'react-router-redux'
const reduxRouterMiddleware = syncHistory(browserHistory)
// const reduxRouterMiddleware = syncHistory(hashHistory)
-import 'babel-polyfill'
-
/*
* Create the store with two middlewares :
* 1. redux-thunk : Allow us to asynchronous things in the actions
diff --git c/app/components/SiteHeader.js w/app/components/SiteHeader.js
index 8af5b86..fd00b84 100644
--- c/app/components/SiteHeader.js
+++ w/app/components/SiteHeader.js
@@ -15,10 +15,6 @@ const SiteHeader = ({ userId }) => {
<Link to='/tickets' activeClassName="active">Submit Ticket</Link>
<Link to='/feedback' activeClassName="active">Feedback</Link>
</nav>
- <div className="concierge-header">
- Welcome, Dave!
- <Link to="/logout">Log out</Link>
- </div>
</header>
)
}
diff --git c/app/components/UserResult.js w/app/components/UserResult.js
index 3c0091a..9b9137d 100644
--- c/app/components/UserResult.js
+++ w/app/components/UserResult.js
@@ -13,7 +13,7 @@ const UserResult = ({
<td>{last_name}</td>
<td>{email}</td>
<td>{phone_number}</td>
- <td><button className="secondary" onClick={onClick}>View</button></td>
+ <td><button onClick={onClick}>View</button></td>
</tr>
)
diff --git c/app/containers/HomePage/actions.js w/app/containers/HomePage/actions.js
index f623375..48fde52 100644
--- c/app/containers/HomePage/actions.js
+++ w/app/containers/HomePage/actions.js
@@ -1,6 +1,10 @@
import { FIND_USERS, FOUND_USERS } from './constants'
-import { secureFetch } from 'utils/http'
-import { ACCOUNT_ENDPOINT } from 'utils/api'
+const { secureFetch } = require('utils/http')
+import {
+ ACCOUNT_ENDPOINT,
+ // ANALYTICS_ENDPOINT,
+ // STATUS_ENDPOINT
+} from 'utils/api'
export function asyncFindUsers(search) {
if (!search) {
@@ -10,7 +14,7 @@ export function asyncFindUsers(search) {
// TODO: Use a real search endpoint. This one only finds by email
return (dispatch) => {
dispatch(findUsers())
- return secureFetch(ACCOUNT_ENDPOINT)
+ return secureFetch(ACCOUNT_ENDPOINT + `&country=US&email=${search}`)
.then(json => dispatch(foundUsers(json.data)))
.catch(err => dispatch(findUsersError(err)))
}
diff --git c/app/containers/HomePage/tests/actions.test.js w/app/containers/HomePage/tests/actions.test.js
index a4da6b0..9b3dbe1 100644
--- c/app/containers/HomePage/tests/actions.test.js
+++ w/app/containers/HomePage/tests/actions.test.js
@@ -3,7 +3,11 @@ import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import { asyncFindUsers } from '../actions'
import { FIND_USERS, FOUND_USERS } from '../constants'
-import { ACCOUNT_ENDPOINT } from 'utils/api'
+import {
+ ACCOUNT_ENDPOINT,
+ // ANALYTICS_ENDPOINT,
+ // STATUS_ENDPOINT
+} from 'utils/api'
import { getHostname, getPath } from 'utils/http'
import nock from 'nock'
@@ -13,7 +17,7 @@ test('should find concierge user', assert => {
const email = '[email protected]'
nock(getHostname(ACCOUNT_ENDPOINT))
- .get(getPath(ACCOUNT_ENDPOINT))
+ .get(getPath(ACCOUNT_ENDPOINT) + `&country=US&email=${email}`)
.reply(200, { data: { email } })
const expectedActions = [
diff --git c/app/containers/UserDetail/actions.js w/app/containers/UserDetail/actions.js
index cebd51c..b39970e 100644
--- c/app/containers/UserDetail/actions.js
+++ w/app/containers/UserDetail/actions.js
@@ -1,32 +1,29 @@
/* eslint-disable no-use-before-define */
-import {
- CHANGE_USER,
- CHANGED_USER,
- LOAD_USER,
- LOADED_USER,
- RECEIVED_ACCOUNT,
- RECEIVED_CENTRE,
- RECEIVED_STATUS
-} from './constants'
+import { CHANGE_USER, CHANGED_USER, LOAD_USER, LOADED_USER } from './constants'
import {
ACCOUNT_ENDPOINT,
- CENTRE_ENDPOINT,
+ ANALYTICS_ENDPOINT,
STATUS_ENDPOINT
} from 'utils/api'
-import { secureFetch, looseFetch } from 'utils/http'
+import { secureFetch } from 'utils/http'
function fetchAccount(id, country = 'US') {
- return secureFetch(`${ACCOUNT_ENDPOINT}`)
- // return secureFetch(`${ACCOUNT_ENDPOINT}&person_id=${id}&country=${country}`)
+ return secureFetch(`${ACCOUNT_ENDPOINT}&person_id=${id}&country=${country}`)
}
function fetchAccountStatus(email, country = 'US') {
return secureFetch(`${STATUS_ENDPOINT}&email=${email}&country=${country}`)
}
-function fetchCentre(centre_id) {
- const url = `${CENTRE_ENDPOINT(centre_id)}`
- return looseFetch(url)
+function fetchAnalytics(id) {
+ return secureFetch(`${ANALYTICS_ENDPOINT}&person_id=${id}`)
+}
+
+function getCentre(centre_id) {
+ const centres = {
+ "sanfrancisco": { country: "US", name: "Westfield San Francisco"}
+ }
+ return centres[centre_id]
}
export function asyncChangeUser(user) {
@@ -42,59 +39,36 @@ export function asyncChangeUser(user) {
}
}
-function receivedAccount(personId, json) {
- return {
- type: RECEIVED_ACCOUNT,
- payload: {
- personId,
- account: json.data
- }
- }
-}
-
-function receivedStatus(personId, json) {
- return {
- type: RECEIVED_STATUS,
- payload: {
- personId,
- status: json.data
- }
- }
-}
-
-function receivedCentre(personId, json) {
- return {
- type: RECEIVED_CENTRE,
- payload: {
- personId,
- centre: json.data
- }
- }
-}
-
-export function loadUser(personId) {
- return async (dispatch, getState) => {
- dispatch({ type: LOAD_USER, payload: { personId } })
- try {
- // Load account api
- const accountResponse = await fetchAccount(personId)
- dispatch(receivedAccount(personId, accountResponse))
- // Load status api
- const accountStatusResponse = await fetchAccountStatus(
- accountResponse.data.email
- )
- dispatch(receivedStatus(personId, accountStatusResponse))
- // Load centre api
- // const centreResponse = await fetchCentre(
- // accountResponse.data.primary_centre_id
- // )
- // dispatch(receivedCentre(id, centreResponse))
- // Loaded user!!
- const user = getState().user[personId]
- dispatch(loadedUser(user))
- } catch (e) {
- console.error(e)
- }
+export function asyncLoadUser(id) {
+ return (dispatch) => {
+ dispatch(loadUser())
+ return fetchAccount(id)
+ .then(account => {
+ const centre = getCentre(account.data.primary_centre_id)
+ Object.freeze(account)
+ return {
+ ...account,
+ data: {
+ ...account.data,
+ primary_centre: centre.name,
+ primary_country: centre.country
+ }
+ }
+ })
+ .then(account => {
+ return fetchAccountStatus(account.data.email)
+ .then(json => {
+ return {
+ ...account,
+ data: {
+ ...account.data,
+ status: json.data
+ }
+ }
+ })
+ })
+ .then(json => dispatch(loadedUser(json)))
+ .catch(error => dispatch(loadUserError(error)))
}
}
@@ -110,6 +84,10 @@ function changeUserError(error) {
return { type: CHANGE_USER, payload: error, error: true }
}
+function loadUser() {
+ return { type: LOAD_USER };
+}
+
function loadedUser(payload) {
return { type: LOADED_USER, payload }
}
diff --git c/app/containers/UserDetail/constants.js w/app/containers/UserDetail/constants.js
index c156b9b..27298b7 100644
--- c/app/containers/UserDetail/constants.js
+++ w/app/containers/UserDetail/constants.js
@@ -2,6 +2,3 @@ export const CHANGE_USER = 'CHANGE_USER'
export const CHANGED_USER = 'CHANGED_USER'
export const LOAD_USER = 'LOAD_USER'
export const LOADED_USER = 'LOADED_USER'
-export const RECEIVED_ACCOUNT = 'RECEIVED_ACCOUNT'
-export const RECEIVED_CENTRE = 'RECEIVED_CENTRE'
-export const RECEIVED_STATUS = 'RECEIVED_STATUS'
diff --git c/app/containers/UserDetail/index.js w/app/containers/UserDetail/index.js
index 69b65d8..a468f84 100644
--- c/app/containers/UserDetail/index.js
+++ w/app/containers/UserDetail/index.js
@@ -1,4 +1,4 @@
-import { asyncChangeUser, loadUser } from './actions'
+import { asyncChangeUser, asyncLoadUser } from './actions'
import React from 'react'
import { connect } from 'react-redux'
import { Link, IndexLink } from 'react-router'
@@ -52,7 +52,6 @@ class UserDetail extends React.Component {
render() {
const { isLoading } = this.props
- // isLoading is undefined here
return (isLoading) ? (
<div className='user-detail-loading loading'>
Loading...
@@ -63,12 +62,11 @@ class UserDetail extends React.Component {
// react-redux stuff
function mapStateToProps(state, ownProps) {
- const userId = ownProps.params.id
return {
location: state.routing.location,
- user: state.user[userId],
- userId,
- isLoading: state.user[userId] && state.user[userId].isLoading
+ user: state.user.data,
+ userId: ownProps.params.id,
+ isLoading: state.user.isLoading
}
}
@@ -76,7 +74,7 @@ function mapDispatchToProps(dispatch) {
return {
changeRoute: url => dispatch(routeActions.push(url)),
changeUser: user => dispatch(asyncChangeUser(user)), // edit customer
- loadUser: id => dispatch(loadUser(id)), // load customer
+ loadUser: id => dispatch(asyncLoadUser(id)), // load customer
}
}
diff --git c/app/containers/UserDetail/reducer.js w/app/containers/UserDetail/reducer.js
index e457684..4582b2c 100644
--- c/app/containers/UserDetail/reducer.js
+++ w/app/containers/UserDetail/reducer.js
@@ -1,59 +1,40 @@
-import {
- CHANGE_USER,
- CHANGED_USER,
- LOAD_USER,
- LOADED_USER,
- RECEIVED_CENTRE,
- RECEIVED_STATUS,
- RECEIVED_ACCOUNT
-} from './constants';
+import { CHANGE_USER, CHANGED_USER, LOAD_USER, LOADED_USER } from './constants';
-function guest(state = {
- isLoading: true,
- personId: "",
- account: {},
- status: ""
-}, action) {
- switch (action.type) {
- case LOADED_USER:
- case CHANGED_USER:
- return {
- ...state,
- isLoading: false
- }
- case LOAD_USER:
- case CHANGE_USER:
- return {
- ...state,
- isLoading: true
- }
- default:
- return state
- }
+const initialState = {
+ data: {
+ first_name: '',
+ last_name: '',
+ email: '',
+ phone_number: '',
+ person_id: ''
+ },
+ isLoading: false,
+ userId: null
}
-function guestPartial(state = {}, action) {
+function user(state = {}, action) {
return {
- ...state,
- ...action.payload
+ data: state.data,
+ userId: state.data.person_id
}
}
-function userReducer(state = {}, action) {
+function userReducer(state = initialState, action) {
+ Object.freeze(state); // Don't mutate state directly, always use assign()!
switch (action.type) {
case LOADED_USER:
case CHANGED_USER:
- case LOAD_USER:
- case CHANGE_USER:
+ const loadedUser = user(action.payload, action)
return {
...state,
- [action.payload.personId]: guest(state[action.payload.personId], action)
+ ...loadedUser,
+ isLoading: false
}
- case RECEIVED_CENTRE:
- case RECEIVED_STATUS:
- case RECEIVED_ACCOUNT:
+ case LOAD_USER:
+ case CHANGE_USER:
return {
- [action.payload.personId]: guestPartial(state[action.payload.personId], action)
+ ...state,
+ isLoading: true
}
default:
return state;
diff --git c/app/containers/UserDetail/tests/actions.test.js w/app/containers/UserDetail/tests/actions.test.js
index 6e212d2..edecd73 100644
--- c/app/containers/UserDetail/tests/actions.test.js
+++ w/app/containers/UserDetail/tests/actions.test.js
@@ -29,14 +29,10 @@ test('should get full guest', assert => {
"sms_marketing_opt_in": false,
"updated_at": "2016-02-05T00:43:42.852Z"
}
- const testCentreData = {
- "country": "US",
- "name": "Westfield San Francisco"
- }
// Mock account response
nock(getHostname(ACCOUNT_ENDPOINT))
- .get(getPath(ACCOUNT_ENDPOINT))
+ .get(getPath(ACCOUNT_ENDPOINT) + `&person_id=${testId}`)
.reply(200, {
"data": testUserData,
"errors": {},
@@ -47,18 +43,6 @@ test('should get full guest', assert => {
})
// Mock status response
- nock(getHostname(CENTRE_ENDPOINT))
- .get(getPath(CENTRE_ENDPOINT) + `&centre_id=sanfrancisco`)
- .reply(200, {
- "data": testCentreData,
- "errors": {},
- "meta": {
- "api_version": "1",
- "deprecation_information": {}
- }
- })
-
- // Mock status response
nock(getHostname(STATUS_ENDPOINT))
.get(getPath(STATUS_ENDPOINT) + `&[email protected]&country=US`)
.reply(200, {
@@ -91,13 +75,13 @@ test('should get full guest', assert => {
assert.end()
})
- store.dispatch(loadUser('WF-foo'))
+ store.dispatch(asyncLoadUser('WF-foo'))
})
test('should update customer', assert => {
const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)
- const person_id = 'WF-xyz'
+ const person_id = 'WF-6e27acf99d37448a8a3c3448333ab06c'
const state = {
person_id,
diff --git c/app/containers/UserDetail/tests/reducer.test.js w/app/containers/UserDetail/tests/reducer.test.js
index a2b197e..5fb1fda 100644
--- c/app/containers/UserDetail/tests/reducer.test.js
+++ w/app/containers/UserDetail/tests/reducer.test.js
@@ -2,18 +2,25 @@ import { test } from 'tap'
import reducer from '../reducer'
import * as types from '../constants'
-test('undefined state returns empty guest account', assert => {
+test('undefined state returns empty user', assert => {
assert.deepEqual(
reducer(undefined, {}), {
- account: {},
- isLoading: false
+ data: {
+ first_name: '',
+ last_name: '',
+ phone_number: '',
+ email: '',
+ person_id: ''
+ },
+ isLoading: false,
+ userId: null
}, 'preserves initial state')
assert.end()
})
test('change user reducer handles CHANGED_USER action', assert => {
assert.deepEqual(
- reducer({ person_id: 'testid'}, {
+ reducer({ userId: 'testid'}, {
type: types.CHANGED_USER,
payload: {
email: '[email protected]',
@@ -22,13 +29,14 @@ test('change user reducer handles CHANGED_USER action', assert => {
phone_number: ''
}
}), {
- account: {
+ data: {
email: '[email protected]',
first_name: 'Dave',
last_name: 'West',
phone_number: '',
},
isLoading: false,
+ userId: 'testid'
}, 'returns a user object')
// assert.deepEqual(
diff --git c/app/styles/_base.scss w/app/styles/_base.scss
index 0743bee..5b16f72 100644
--- c/app/styles/_base.scss
+++ w/app/styles/_base.scss
@@ -1,3 +1,60 @@
+// Nav styles
+header {
+ .masthead {
+ background: black url(../images/westfield.svg) no-repeat 125px 13px;
+ font-size: 13px;
+ height: 72px;
+ letter-spacing: .7pt;
+ line-height: 102px;
+ padding-left: 125px;
+ }
+ nav {
+ background-color: #29292B;
+ height: 66px;
+ line-height: 66px;
+ padding-left: 125px;
+ }
+ &, a {
+ color: white;
+ font-family: 'Flama Condensed';
+ font-size: 17px;
+ font-weight: 300;
+ text-transform: uppercase;
+ text-decoration: none;
+ }
+ nav a {
+ margin: 0 45px 0 -2px; // pull left to align with masthead
+ &.active {
+ border-bottom: 4px solid #DA291C;
+ padding: 0 2px 2px;
+ }
+ }
+}
+
+// Header styles
+h1, h2, h3 {
+ margin: 0;
+ padding: 0;
+}
+
+h2, h3 {
+ border-bottom: 1.5pt solid rgba(148, 148, 148, 0.3);
+ font-family: 'Flama Condensed';
+ font-weight: 300;
+ text-transform: uppercase;
+}
+
+h2 {
+ font-size: 38px;
+ padding: 0 0 20px;
+}
+
+h3 {
+ font-size: 22px;
+ padding: 0 0 8px;
+}
+
+// Body styles
body {
background-color: #F7F7F7;
color: #212121;
@@ -16,14 +73,16 @@ body {
}
// Button styles
-button {
+button.primary {
cursor: pointer;
+ padding: 6px 51px;
border-radius: 2px;
border: 1px solid transparent;
text-transform: uppercase;
color: white;
background-color: #000;
font-family: 'Flama Condensed';
+ font-size: 19px;
outline: none;
&:hover {
background-color: #434343;
@@ -31,35 +90,4 @@ button {
&:active {
background-color: #d80000;
}
- &.primary {
- font-size: 19px;
- padding: 6px 51px;
- }
-}
-
-button.secondary {
- font-size: 12px;
- padding: 3px 10px;
-}
-
-h1, h2, h3 {
- margin: 0;
- padding: 0;
-}
-
-h2, h3 {
- border-bottom: 1.5pt solid rgba(148, 148, 148, 0.3);
- font-family: 'Flama Condensed';
- font-weight: 300;
- text-transform: uppercase;
-}
-
-h2 {
- font-size: 38px;
- padding: 0 0 20px;
-}
-
-h3 {
- font-size: 22px;
- padding: 0 0 8px;
}
diff --git c/app/styles/_header.scss w/app/styles/_header.scss
deleted file mode 100644
index ac21a75..0000000
--- c/app/styles/_header.scss
+++ /dev/null
@@ -1,55 +0,0 @@
-header {
- .masthead {
- background: black url(../images/westfield.svg) no-repeat 125px 13px;
- font-size: 13px;
- height: 72px;
- letter-spacing: .7pt;
- line-height: 102px;
- padding-left: 125px;
- position: relative;
- }
- nav {
- background-color: #29292B;
- height: 66px;
- line-height: 66px;
- padding-left: 125px;
- }
- &, a {
- color: white;
- font-family: 'Flama Condensed';
- font-size: 17px;
- font-weight: 300;
- text-transform: uppercase;
- text-decoration: none;
- }
- nav a {
- margin: 0 45px 0 0;
- &.active {
- border-bottom: 4px solid #DA291C;
- padding: 0 2px 2px;
- &:first-of-type {
- margin-left: -2px;
- }
- }
- }
-}
-
-.concierge-header {
- border-left: 1px solid #4b4b4b;
- color: #fff;
- font-weight: 200;
- height: 45px;
- letter-spacing: 0.2pt;
- padding: 2px 0 0 31px;
- position: absolute;
- left: 1186px;
- text-transform: none;
- top:81px;
- a {
- display: block;
- font-size: 13px;
- text-transform: none;
- font-family: 'Flama-book';
- padding-top: 5px;
- }
-}
diff --git c/app/styles/core.scss w/app/styles/core.scss
index bf741c6..f5f70f8 100644
--- c/app/styles/core.scss
+++ w/app/styles/core.scss
@@ -1,5 +1,4 @@
@import 'base';
-@import 'header';
// Some best-practice CSS that's useful for most apps
// Just remove them if they're not what you want
diff --git c/app/utils/api.js w/app/utils/api.js
index 1b9f29d..0f23153 100644
--- c/app/utils/api.js
+++ w/app/utils/api.js
@@ -1,17 +1,11 @@
const API_KEY = __API_KEY__
-const API_ROOT_SECURE = 'https://secure.uat.westfield.io/v1'
-const API_ROOT = 'https://api.uat.westfield.io/v1'
+const API_ROOT = 'https://secure.uat.westfield.io/v1'
import URI from 'urijs'
-function getResourceURL(resource, extras) {
- return new URI(`${API_ROOT}/${[resource, ...extras].join('/')}?api_key=${API_KEY}`)
+function getResourceURL(resource) {
+ return new URI(`${API_ROOT}/${resource}?api_key=${API_KEY}`)
}
-function getSecureResourceURL(resource) {
- return new URI(`${API_ROOT_SECURE}/${resource}?api_key=${API_KEY}`)
-}
-
-export const ACCOUNT_ENDPOINT = getSecureResourceURL('account')
-export const STATUS_ENDPOINT = getSecureResourceURL('account/status')
-export const ANALYTICS_ENDPOINT = getSecureResourceURL('analytics')
-export const CENTRE_ENDPOINT = id => getResourceURL('centres', [id])
+export const ACCOUNT_ENDPOINT = getResourceURL('account')
+export const STATUS_ENDPOINT = getResourceURL('account/status')
+export const ANALYTICS_ENDPOINT = getResourceURL('analytics')
diff --git c/app/utils/http.js w/app/utils/http.js
index 1de6aaf..987766f 100644
--- c/app/utils/http.js
+++ w/app/utils/http.js
@@ -28,13 +28,8 @@ function parseJSON(response) {
return response.json()
}
-export function looseFetch(url) {
- // TODO: strip out Authorization header
- return secureFetch(url, { cache: false, mode: 'no-cors' })
-}
-
export function secureFetch(url, options = { cache: false }) {
- return fetch(url, { headers, ...options })
+ return fetch(url, { ...options, headers })
.then(checkStatus)
.then(parseJSON)
}
diff --git c/package.json w/package.json
index da50237..18cdccc 100644
--- c/package.json
+++ w/package.json
@@ -46,7 +46,6 @@
"webpack-dev-server": "^1.14.1"
},
"dependencies": {
- "babel-polyfill": "^6.5.0",
"es6-promise": "^3.0.2",
"exports-loader": "^0.6.2",
"imports-loader": "^0.6.5",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment