Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
717 changes: 329 additions & 388 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/api/projectReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,13 @@ export function getProjectSummary(projectId) {
}
})
}

/**
* Gets signed URL for embeding the requested report.
* @param {*} projectId id of the project for which report is to be fecthed
* @param {*} reportName unique name of the report
*/
export function getProjectReportUrl(projectId, reportName) {
return axios.get(`${PROJECTS_API_URL}/v5/projects/${projectId}/reports/embed?reportName=${reportName}`)
.then(resp => resp.data)
}
13 changes: 13 additions & 0 deletions src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ export const LOAD_PROJECT_SUMMARY = 'LOAD_PROJECT_SUMMARY'
export const LOAD_PROJECT_SUMMARY_PENDING = 'LOAD_PROJECT_SUMMARY_PENDING'
export const LOAD_PROJECT_SUMMARY_SUCCESS = 'LOAD_PROJECT_SUMMARY_SUCCESS'
export const LOAD_PROJECT_SUMMARY_FAILURE = 'LOAD_PROJECT_SUMMARY_FAILURE'
export const REFRESH_LOOKER_SESSION = 'REFRESH_LOOKER_SESSION'

// Product attachments
export const ADD_PRODUCT_ATTACHMENT = 'ADD_PRODUCT_ATTACHMENT'
Expand Down Expand Up @@ -956,3 +957,15 @@ export const EVENT_TYPE = {
export const PROJECT_ASSETS_SHARED_WITH_ALL_MEMBERS = 'All Project Members'
export const PROJECT_ASSETS_SHARED_WITH_TOPCODER_MEMBERS = 'Only Topcoder Members'
export const PROJECT_ASSETS_SHARED_WITH_ADMIN = 'Only Admins'



/**
* REPORTS
*/
export const PROJECT_REPORTS = {
PROJECT_SUMMARY : 'summary',
TAAS_MEMBERS : 'taas_members',
}

export const REPORT_SESSION_LENGTH = 600
34 changes: 32 additions & 2 deletions src/projects/actions/projectReports.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
LOAD_PROJECT_SUMMARY
LOAD_PROJECT_SUMMARY,
REFRESH_LOOKER_SESSION,
} from '../../config/constants'
import {
getProjectSummary
getProjectSummary,
getProjectReportUrl,
} from '../../api/projectReports'

export function loadProjectSummary(projectId) {
Expand All @@ -14,3 +16,31 @@ export function loadProjectSummary(projectId) {
})
}
}

/**
* Redux action to start fetching the signed URL for embeding the given report
* @param {*} projectId id of the project
* @param {*} reportName unique name of the report
*/
export function loadProjectReportsUrls(projectId, reportName) {
return (dispatch) => {
return dispatch({
type: LOAD_PROJECT_SUMMARY,
payload: getProjectReportUrl(projectId, reportName),
meta: { projectId }
})
}
}

/**
* Redux action to refresh the looker session. It is aimed to just indicate that there is need
* of refreshing the token. It does not do any thing itself. It is upto the state listner to react.
*/
export function refreshLookerSession() {
return (dispatch) => {
return dispatch({
type: REFRESH_LOOKER_SESSION,
payload: { lookerSessionExpired: true }
})
}
}
83 changes: 58 additions & 25 deletions src/projects/detail/containers/ProjectSummaryReportContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,66 @@ import {
SCREEN_BREAKPOINT_MD,
PROJECT_FEED_TYPE_PRIMARY,
PROJECT_FEED_TYPE_MESSAGES,
PROJECT_REPORTS,
REPORT_SESSION_LENGTH,
} from '../../../config/constants'
import TwoColsLayout from '../../../components/TwoColsLayout'
import Sticky from '../../../components/Sticky'
import ProjectInfoContainer from './ProjectInfoContainer'
import PERMISSIONS from '../../../config/permissions'
import { checkPermission } from '../../../helpers/permissions'
import { loadProjectSummary } from '../../actions/projectReports'
import { loadProjectSummary, loadProjectReportsUrls, refreshLookerSession } from '../../actions/projectReports'
import spinnerWhileLoading from '../../../components/LoadingSpinner'
import ProjectSummaryReport from '../components/ProjectSummaryReport'

const EnhancedProjectSummaryReport = spinnerWhileLoading(props => {
return props.project && !props.isLoading && props.reportsProjectId === props.project.id
})(ProjectSummaryReport)
import './ProjectSummaryReportContainer.scss'

const LookerEmbedReport = (props) => {
return (<iframe width="100%" src={props.projectSummaryEmbedUrl} onLoad={props.onLoad} />)
}

const EnhancedLookerEmbedReport = spinnerWhileLoading(props => {
return !props.isLoading
})(LookerEmbedReport)

let timer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, it's safer to keep timer inside class like this.timer. Otherwise, if we have 2 components on one page, they would use the same timer and break each other.

fyi @vikasrohit

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I placed it intentionally outside because I do want to use the same time for all reporting components on the same page.
However, on second thought, I think we can move it to inside the class for better reading because I don't expect multiple copies of this component on the same page.


class ProjectSummaryReportContainer extends React.Component {

constructor(props) {
super(props)
this.setLookerSessionTimer = this.setLookerSessionTimer.bind(this)
}

componentWillUpdate(nextProps) {
const nextReportProjectId = _.get(nextProps, 'projectReports.projectId')
const nextReportProjectId = _.get(nextProps, 'reportsProjectId')
const nextProjectId = _.get(nextProps, 'project.id')
if(nextProps.project && nextReportProjectId !== nextProjectId) {
nextProps.loadProjectSummary(nextProps.project.id)
const lookerSessionExpired = !this.props.lookerSessionExpired && nextProps.lookerSessionExpired
if(lookerSessionExpired || (nextProjectId && nextReportProjectId !== nextProjectId)) {
nextProps.loadProjectReportsUrls(nextProjectId, PROJECT_REPORTS.PROJECT_SUMMARY)
this.setLookerSessionTimer()
}
}

setLookerSessionTimer() {
console.log('Setting Looker Session Timer')
if (timer) {
clearTimeout(timer)
}
const thisRef = this
let timeoutDuration = 60*1000
if (REPORT_SESSION_LENGTH > 2*60) {
timeoutDuration = REPORT_SESSION_LENGTH*1000 - 2*60*1000
}
// set timeout for raising alert to refresh the token 2 minutes before the session expire
timer = setTimeout(() => {
console.log('Calling refresh looker session action')
thisRef.props.refreshLookerSession()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hint: this inside arrow functions () => {} always point to the outer this, so we don't have to use thisRef and can use this instead, it would work.

@vikasrohit fyi

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always found the this in side the native javascript methods (like setTimeout), ambiguous. But as it is working for this case as you suggested and I double checked, I am removing the need of this Ref.

}, (timeoutDuration))
}

render() {
const {
project,
projectReports,
isSuperUser,
isManageUser,
currentMemberRole,
Expand All @@ -45,11 +77,10 @@ class ProjectSummaryReportContainer extends React.Component {
phases,
productsTimelines,
phasesTopics,
isProcessing,
isLoading,
location,
reportsProjectId,
projectSummaryEmbedUrl,
} = this.props
const projectSummary = _.get(projectReports, 'projectSummary')

const leftArea = (
<ProjectInfoContainer
Expand All @@ -64,7 +95,7 @@ class ProjectSummaryReportContainer extends React.Component {
productsTimelines={productsTimelines}
phasesTopics={phasesTopics}
onChannelClick={this.onChannelClick}
isProjectProcessing={isProcessing}
isProjectProcessing={isLoading}
/>
)

Expand All @@ -82,29 +113,28 @@ class ProjectSummaryReportContainer extends React.Component {
</MediaQuery>
</TwoColsLayout.Sidebar>
<TwoColsLayout.Content>
<EnhancedProjectSummaryReport
projectSummary={projectSummary}
isLoading={isProcessing}
reportsProjectId={reportsProjectId}
project={project}
/>
{
<EnhancedLookerEmbedReport
isLoading={isLoading}
projectSummaryEmbedUrl={projectSummaryEmbedUrl}
onLoad={this.setLookerSessionTimer}
/>
}
</TwoColsLayout.Content>

</TwoColsLayout>
)
}
}

ProjectSummaryReportContainer.propTypes = {
currentMemberRole: PT.string.isRequired,
isProcessing: PT.bool.isRequired,
isLoading: PT.bool.isRequired,
isSuperUser: PT.bool.isRequired,
isManageUser: PT.bool.isRequired,
project: PT.object.isRequired,
projectReports: PT.object.isRequired,
phases: PT.array.isRequired,
productsTimelines: PT.object.isRequired,
reportsProjectId: PT.number.isRequired,
reportsProjectId: PT.number,
}

const mapStateToProps = ({ projectState, projectTopics, phasesTopics, projectReports }) => {
Expand All @@ -118,14 +148,17 @@ const mapStateToProps = ({ projectState, projectTopics, phasesTopics, projectRep
phases: projectState.phases,
feeds: allFeed,
phasesTopics,
projectReports,
isProcessing: projectReports.isLoading,
isLoading: projectReports.isLoading,
reportsProjectId: projectReports.projectId,
lookerSessionExpired: projectReports.lookerSessionExpired,
projectSummaryEmbedUrl: projectReports.projectSummaryEmbedUrl,
}
}

const mapDispatchToProps = {
loadProjectSummary,
loadProjectReportsUrls,
refreshLookerSession,
}

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ProjectSummaryReportContainer))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
iframe {
height: calc(100vh - 60px);
}
17 changes: 14 additions & 3 deletions src/projects/reducers/projectReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import {
LOAD_PROJECT_SUMMARY_PENDING,
LOAD_PROJECT_SUMMARY_SUCCESS,
LOAD_PROJECT_SUMMARY_FAILURE,
REFRESH_LOOKER_SESSION,
} from '../../config/constants'

const initialState = {
isLoading: false,
error: false,
projectId: null,
projectSummary: null
projectSummary: null,
projectSummaryEmbedUrl: null,
lookerSessionExpired: false,
}

export const projectReports = function (state=initialState, action) {
Expand All @@ -23,11 +26,13 @@ export const projectReports = function (state=initialState, action) {
})

case LOAD_PROJECT_SUMMARY_SUCCESS:
if(payload.projectId === state.projectId) {
if(action.meta.projectId === state.projectId) {
return Object.assign({}, state, {
isLoading: false,
error: false,
projectSummary: payload
projectSummaryEmbedUrl: payload,
lookerSessionExpired: false,
// projectSummary: payload
})
} else {
return state
Expand All @@ -36,10 +41,16 @@ export const projectReports = function (state=initialState, action) {
case LOAD_PROJECT_SUMMARY_FAILURE: {
return Object.assign({}, state, {
isLoading: false,
lookerSessionExpired: false,
error: payload
})
}

case REFRESH_LOOKER_SESSION: {
return Object.assign({}, state, {
lookerSessionExpired: true
})
}

default:
return state
Expand Down