From 726c83cda1e4e3504cf9d28df8c6aa779d72ae88 Mon Sep 17 00:00:00 2001 From: Parth Shah Date: Mon, 6 Feb 2017 13:52:46 -0800 Subject: [PATCH 1/3] initial commit --- package.json | 1 + src/actions/loadUser.js | 51 ++++++++++++++++++- src/components/TopBar/TopBar.jsx | 14 +++-- src/config/constants.js | 31 ++++++----- src/config/store.js | 6 ++- src/helpers/index.js | 2 + src/index.jsx | 12 +++-- .../list/components/Projects/Projects.jsx | 1 - src/routes.jsx | 11 +++- yarn.lock | 10 ++-- 10 files changed, 109 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 0de5d657b..d4761d334 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "recompose": "^0.20.2", "redux": "^3.5.2", "redux-promise-middleware": "^4.0.0", + "redux-segment": "^1.6.1", "redux-thunk": "^2.1.0", "tc-accounts": "https://github.com/appirio-tech/accounts-app.git#dev", "tc-ui": "https://github.com/appirio-tech/tc-ui.git#feature/connectv2" diff --git a/src/actions/loadUser.js b/src/actions/loadUser.js index 2a1756404..94ad9faa9 100644 --- a/src/actions/loadUser.js +++ b/src/actions/loadUser.js @@ -1,8 +1,9 @@ import _ from 'lodash' // import { fetchJSON } from '../helpers' -import { ACCOUNTS_APP_CONNECTOR_URL, LOAD_USER_SUCCESS, LOAD_USER_FAILURE } from '../config/constants' +import { ACCOUNTS_APP_CONNECTOR_URL, LOAD_USER_SUCCESS, LOAD_USER_FAILURE, ROLE_ADMINISTRATOR, ROLE_CONNECT_COPILOT, ROLE_TOPCODER_USER, ROLE_CONNECT_MANAGER } from '../config/constants' import { getFreshToken, configureConnector, decodeToken } from 'tc-accounts' import { getUserProfile } from '../api/users' +import { EventTypes } from 'redux-segment' configureConnector({ connectorUrl: ACCOUNTS_APP_CONNECTOR_URL, @@ -42,7 +43,53 @@ export function loadUserSuccess(dispatch, token) { currentUser = _.assign(currentUser, profile) // keeping profile for backward compaitability currentUser.profile = profile - dispatch({ type: LOAD_USER_SUCCESS, user : currentUser }) + // determine user role + let userRole; + if (_.indexOf(currentUser.roles, ROLE_ADMINISTRATOR) > -1) { + userRole = ROLE_ADMINISTRATOR + } else if (_.indexOf(currentUser.roles, ROLE_CONNECT_MANAGER) > -1) { + userRole = ROLE_CONNECT_MANAGER + } else if (_.indexOf(currentUser.roles, ROLE_CONNECT_COPILOT) > -1) { + userRole = ROLE_CONNECT_COPILOT + } else { + userRole = ROLE_TOPCODER_USER + } + + + const analyticsEvents = [ + eventType: EventTypes.identify, + eventPayload: { + userId: currentUser.id, + traits: { + id: currentUser.id, + role: userRole, + username: currentUser.handle, + firstName: currentUser.firstName, + lastName: currentUser.lastName, + email: currentUser.email, + createdAt: currentUser.createdAt + } + } + ] + if (analytics) { + const anonymousId = analtyics.user().anonymousId() + if (anonymousId) { + analyticsEvents.push({ + eventType: EventType.alias, + eventPayload: { + userId: currentUser.id, + previousId: anonymousId + } + }) + } + } + dispatch({ + type: LOAD_USER_SUCCESS, + user : currentUser, + meta: { + analytics: analyticsEvents + } + }) }) .catch((err) => { // if we fail to load user's profile, still dispatch user load success diff --git a/src/components/TopBar/TopBar.jsx b/src/components/TopBar/TopBar.jsx index 3c73be10a..029531dbc 100644 --- a/src/components/TopBar/TopBar.jsx +++ b/src/components/TopBar/TopBar.jsx @@ -52,17 +52,25 @@ class TopBar extends Component { userHandle, userImage, userName, domain, criteria, onNewProjectIntent, applyFilters, isProjectDetails, project, isPowerUser, loginUrl, registerUrl, isFilterVisible } = this.props - const homePageUrl = window.location.protocol + '//' + window.location.hostname - const logoutLink = 'https://accounts.' + domain + '/#!/logout?retUrl=' + homePageUrl + const homePageUrl = `${window.location.protocol}//${window.location.host}/` + const logoutLink = `https://accounts.${domain}/#!/logout?retUrl=${homePageUrl}` const isLoggedIn = !!userHandle const logoTargetUrl = isLoggedIn ? '/projects' : '/' + const logoutClick = () => { + console.log('Before ID', analytics.user().anonymousId()) + analytics.reset() + console.log('After ID', analytics.user().anonymousId()) + window.location = logoutLink + + } + const userMenuItems = [ [ { label: 'Help', link: 'https://help.topcoder.com/hc/en-us', absolute: true, id: 0 } ], [ - { label: 'Log out', link: logoutLink, absolute: true, id: 0 } + { label: 'Log out', onClick: logoutClick, absolute: true, id: 0 } ] ] const logo = ( diff --git a/src/config/constants.js b/src/config/constants.js index 40bc0d1fa..e7aa812a5 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -147,6 +147,23 @@ export const THREAD_MESSAGES_PAGE_SIZE = 3 * Project status */ export const PROJECT_STATUS_DRAFT = 'draft' +export const PROJECT_STATUS_IN_REVIEW = 'in_review' +export const PROJECT_STATUS_REVIEWED = 'reviewed' +export const PROJECT_STATUS_ACTIVE = 'active' +export const PROJECT_STATUS_COMPLETED = 'completed' +export const PROJECT_STATUS_CANCELLED = 'cancelled' +export const PROJECT_STATUS_PAUSED = 'paused' + +export const PROJECT_STATUS = [ + {color: 'gray', name: 'Draft', value: PROJECT_STATUS_DRAFT }, + {color: 'gray', name: 'In Review', value: PROJECT_STATUS_IN_REVIEW }, + {color: 'gray', name: 'Reviewed', value: PROJECT_STATUS_REVIEWED }, + {color: 'green', name: 'Active', value: PROJECT_STATUS_ACTIVE }, + {color: 'black', name: 'Completed', value: PROJECT_STATUS_COMPLETED }, + {color: 'black', name: 'Cancelled', value: PROJECT_STATUS_CANCELLED }, + {color: 'red', name: 'Paused', value: PROJECT_STATUS_PAUSED } +] + /* * Project member role @@ -174,6 +191,7 @@ export const FILE_PICKER_API_KEY = process.env.FILE_PICKER_API_KEY || 'AzFINuQoq export const FILE_PICKER_SUBMISSION_CONTAINER_NAME = process.env.FILE_PICKER_SUBMISSION_CONTAINER_NAME || 'submission-staging-dev' export const PROJECT_ATTACHMENTS_FOLDER = process.env.PROJECT_ATTACHMENTS_FOLDER || 'PROJECT_ATTACHMENTS' +export const SEGMENT_KEY = process.env.SEGMENT_KEY /* * URLs */ @@ -189,19 +207,6 @@ export const SALESFORCE_PROJECT_LEAD_LINK = process.env.SALESFORCE_PROJECT_LEAD_ export const PROJECT_NAME_MAX_LENGTH = 255 -export const PROJECT_STATUS_COMPLETED = 'completed' -export const PROJECT_STATUS_CANCELLED = 'cancelled' - -export const PROJECT_STATUS = [ - {color: 'gray', name: 'Draft', value: 'draft'}, - {color: 'gray', name: 'In Review', value: 'in_review'}, - {color: 'gray', name: 'Reviewed', value: 'reviewed'}, - {color: 'green', name: 'Active', value: 'active'}, - {color: 'black', name: 'Completed', value: PROJECT_STATUS_COMPLETED }, - {color: 'black', name: 'Cancelled', value: PROJECT_STATUS_CANCELLED }, - {color: 'red', name: 'Paused', value: 'paused'} -] - export const PROJECT_FEED_TYPE_PRIMARY = 'PRIMARY' export const PROJECT_FEED_TYPE_MESSAGES = 'MESSAGES' diff --git a/src/config/store.js b/src/config/store.js index 2309f0ead..c60451451 100644 --- a/src/config/store.js +++ b/src/config/store.js @@ -2,12 +2,16 @@ import thunk from 'redux-thunk' import { createStore, applyMiddleware, compose } from 'redux' import reducers from '../reducers' import promiseMiddleware from 'redux-promise-middleware' +import { createTracker } from 'redux-segment' + +const tracker = createTracker() const middleware = [ promiseMiddleware({ promiseTypeSuffixes: ['PENDING', 'SUCCESS', 'FAILURE'] }), - thunk + thunk, + tracker ] if (process.env.ENV === 'DEV') { diff --git a/src/helpers/index.js b/src/helpers/index.js index 814e9b4d1..23bf75592 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -3,6 +3,8 @@ import fetch from 'isomorphic-fetch' import tcEmitter from './emitter' export const TCEmitter = tcEmitter +export const titleCase = (str) => _.startCase(_.camelCase(str)) + // Fetch helpers export function status(response) { diff --git a/src/index.jsx b/src/index.jsx index e68994bb3..ecf51024e 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -8,7 +8,7 @@ import Router from 'react-router/lib/Router' import store from './config/store' import routes from './routes' import { TCEmitter } from './helpers' -import { EVENT_ROUTE_CHANGE, HEAP_ANALYTICS_APP_ID } from './config/constants' +import { EVENT_ROUTE_CHANGE, HEAP_ANALYTICS_APP_ID, SEGMENT_KEY } from './config/constants' const mountNode = document.getElementById('root') const onRouteChange = () => { @@ -16,8 +16,14 @@ const onRouteChange = () => { } /* eslint-disable */ -window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c { + if (window.analytics) { + window.analytics.page() + } +}) + const LoginRedirect = withProps({ redirectTo: `${ACCOUNTS_APP_LOGIN_URL}?retUrl=${window.location.protocol}//${window.location.hostname}${window.location.port ? ':' + window.location.port : ''}` })(RedirectComponent) @@ -71,4 +78,4 @@ export default ( }/> }/> -) \ No newline at end of file +) diff --git a/yarn.lock b/yarn.lock index 3cbd220d9..6eeffb10c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3767,7 +3767,7 @@ minimatch@~0.2.11: lru-cache "2" sigmund "~1.0.0" -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -3775,10 +3775,6 @@ minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - mkdirp@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" @@ -5047,6 +5043,10 @@ redux-router@^1.0.0-beta3: dependencies: deep-equal "^1.0.1" +redux-segment@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/redux-segment/-/redux-segment-1.6.1.tgz#813d0734ae7b5de14e863202afd20c24de79022e" + redux-thunk@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-0.1.0.tgz#8e347606808b35bf8a927df4616f6fed101826e5" From d300d7ec5b7c4d260fe370effcef26bd37f1da62 Mon Sep 17 00:00:00 2001 From: Parth Shah Date: Mon, 6 Feb 2017 13:58:39 -0800 Subject: [PATCH 2/3] fixing lint issues and minor fixes --- src/actions/loadUser.js | 13 ++++++------- src/components/TopBar/TopBar.jsx | 4 +--- src/index.jsx | 2 +- src/routes.jsx | 2 +- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/actions/loadUser.js b/src/actions/loadUser.js index 94ad9faa9..ac82b1562 100644 --- a/src/actions/loadUser.js +++ b/src/actions/loadUser.js @@ -1,5 +1,4 @@ import _ from 'lodash' -// import { fetchJSON } from '../helpers' import { ACCOUNTS_APP_CONNECTOR_URL, LOAD_USER_SUCCESS, LOAD_USER_FAILURE, ROLE_ADMINISTRATOR, ROLE_CONNECT_COPILOT, ROLE_TOPCODER_USER, ROLE_CONNECT_MANAGER } from '../config/constants' import { getFreshToken, configureConnector, decodeToken } from 'tc-accounts' import { getUserProfile } from '../api/users' @@ -44,7 +43,7 @@ export function loadUserSuccess(dispatch, token) { // keeping profile for backward compaitability currentUser.profile = profile // determine user role - let userRole; + let userRole if (_.indexOf(currentUser.roles, ROLE_ADMINISTRATOR) > -1) { userRole = ROLE_ADMINISTRATOR } else if (_.indexOf(currentUser.roles, ROLE_CONNECT_MANAGER) > -1) { @@ -56,7 +55,7 @@ export function loadUserSuccess(dispatch, token) { } - const analyticsEvents = [ + const analyticsEvents = [{ eventType: EventTypes.identify, eventPayload: { userId: currentUser.id, @@ -70,12 +69,12 @@ export function loadUserSuccess(dispatch, token) { createdAt: currentUser.createdAt } } - ] - if (analytics) { - const anonymousId = analtyics.user().anonymousId() + }] + if (window.analytics) { + const anonymousId = window.analtyics.user().anonymousId() if (anonymousId) { analyticsEvents.push({ - eventType: EventType.alias, + eventType: EventTypes.alias, eventPayload: { userId: currentUser.id, previousId: anonymousId diff --git a/src/components/TopBar/TopBar.jsx b/src/components/TopBar/TopBar.jsx index 029531dbc..addc48fd6 100644 --- a/src/components/TopBar/TopBar.jsx +++ b/src/components/TopBar/TopBar.jsx @@ -58,9 +58,7 @@ class TopBar extends Component { const logoTargetUrl = isLoggedIn ? '/projects' : '/' const logoutClick = () => { - console.log('Before ID', analytics.user().anonymousId()) - analytics.reset() - console.log('After ID', analytics.user().anonymousId()) + window.analytics.reset() window.location = logoutLink } diff --git a/src/index.jsx b/src/index.jsx index ecf51024e..7a6aaf284 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -8,7 +8,7 @@ import Router from 'react-router/lib/Router' import store from './config/store' import routes from './routes' import { TCEmitter } from './helpers' -import { EVENT_ROUTE_CHANGE, HEAP_ANALYTICS_APP_ID, SEGMENT_KEY } from './config/constants' +import { EVENT_ROUTE_CHANGE, SEGMENT_KEY } from './config/constants' const mountNode = document.getElementById('root') const onRouteChange = () => { diff --git a/src/routes.jsx b/src/routes.jsx index f65fedbdc..477358c6c 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -14,7 +14,7 @@ import { getFreshToken } from 'tc-accounts' // import reportsListRoutes from './reports/routes.jsx' // Tracking -browserHistory.listen( location => { +browserHistory.listen( () => { if (window.analytics) { window.analytics.page() } From 40254fdb30b49470a1ab762901b9d47d03b8c33b Mon Sep 17 00:00:00 2001 From: Parth Shah Date: Mon, 6 Feb 2017 14:34:51 -0800 Subject: [PATCH 3/3] changing segment key name --- src/config/constants.js | 2 +- src/index.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config/constants.js b/src/config/constants.js index e7aa812a5..38fe34f9b 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -191,7 +191,7 @@ export const FILE_PICKER_API_KEY = process.env.FILE_PICKER_API_KEY || 'AzFINuQoq export const FILE_PICKER_SUBMISSION_CONTAINER_NAME = process.env.FILE_PICKER_SUBMISSION_CONTAINER_NAME || 'submission-staging-dev' export const PROJECT_ATTACHMENTS_FOLDER = process.env.PROJECT_ATTACHMENTS_FOLDER || 'PROJECT_ATTACHMENTS' -export const SEGMENT_KEY = process.env.SEGMENT_KEY +export const SEGMENT_KEY = process.env.CONNECT_SEGMENT_KEY /* * URLs */ diff --git a/src/index.jsx b/src/index.jsx index 7a6aaf284..6d15e05ad 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -5,6 +5,7 @@ import { Provider } from 'react-redux' import browserHistory from 'react-router/lib/browserHistory' import Router from 'react-router/lib/Router' +import _ from 'lodash' import store from './config/store' import routes from './routes' import { TCEmitter } from './helpers' @@ -16,7 +17,7 @@ const onRouteChange = () => { } /* eslint-disable */ -if (SEGMENT_KEY) { +if (!_.isEmpty(SEGMENT_KEY)) { !!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t