diff --git a/src/api/projectMembers.js b/src/api/projectMembers.js index 8ceba034b..007f188b0 100644 --- a/src/api/projectMembers.js +++ b/src/api/projectMembers.js @@ -68,7 +68,7 @@ export function removeProjectMember(projectId, memberId) { } export function getProjectMembers(projectId) { - const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,photoURL,workingHourStart,workingHourEnd,timeZone' + const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,email,photoURL,workingHourStart,workingHourEnd,timeZone' const url = `${PROJECTS_API_URL}/v5/projects/${projectId}/members/?fields=` + encodeURIComponent(fields) return axios.get(url) @@ -78,7 +78,7 @@ export function getProjectMembers(projectId) { } export function getProjectMember(projectId, memberId) { - const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,photoURL,workingHourStart,workingHourEnd,timeZone' + const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,email,photoURL,workingHourStart,workingHourEnd,timeZone' const url = `${PROJECTS_API_URL}/v5/projects/${projectId}/members/${memberId}?fields=` + encodeURIComponent(fields) return axios.get(url) diff --git a/src/api/projects.js b/src/api/projects.js index 559b5d55d..63563c4a1 100644 --- a/src/api/projects.js +++ b/src/api/projects.js @@ -4,7 +4,7 @@ import { PROJECTS_API_URL, PROJECTS_LIST_PER_PAGE } from '../config/constants' export function getProjects(criteria, pageNum) { // add default params - const includeFields = ['id', 'name', 'description', 'members', 'status', 'type', 'actualPrice', 'estimatedPrice', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy', 'details', 'lastActivityAt', 'lastActivityUserId', 'version', 'templateId'] + const includeFields = ['id', 'name', 'description', 'members', 'invites', 'status', 'type', 'actualPrice', 'estimatedPrice', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy', 'details', 'lastActivityAt', 'lastActivityUserId', 'version', 'templateId'] const params = { fields: includeFields.join(','), sort: 'updatedAt+desc', // default sort value diff --git a/src/config/constants.js b/src/config/constants.js index d5e695f10..2c5611701 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -417,7 +417,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' +export const SET_LOOKER_SESSION_EXPIRED = 'SET_LOOKER_SESSION_EXPIRED' // Product attachments export const ADD_PRODUCT_ATTACHMENT = 'ADD_PRODUCT_ATTACHMENT' @@ -962,4 +962,7 @@ export const PROJECT_REPORTS = { TAAS_MEMBERS : 'taas_members', } -export const REPORT_SESSION_LENGTH = 1800 \ No newline at end of file +/** + * Report session length in seconds + */ +export const REPORT_SESSION_LENGTH = 30 * 60 // 30 minutes diff --git a/src/projects/actions/projectReports.js b/src/projects/actions/projectReports.js index 7c91a88b7..42cbdffee 100644 --- a/src/projects/actions/projectReports.js +++ b/src/projects/actions/projectReports.js @@ -1,6 +1,6 @@ import { LOAD_PROJECT_SUMMARY, - REFRESH_LOOKER_SESSION, + SET_LOOKER_SESSION_EXPIRED, } from '../../config/constants' import { getProjectSummary, @@ -33,14 +33,15 @@ export function loadProjectReportsUrls(projectId, reportName) { } /** - * 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. + * Redux action set the flag `lookerSessionExpired` + * + * @param {Boolean} isExpired true to indicate that looker session is expired */ -export function refreshLookerSession() { +export function setLookerSessionExpired(isExpired) { return (dispatch) => { return dispatch({ - type: REFRESH_LOOKER_SESSION, - payload: { lookerSessionExpired: true } + type: SET_LOOKER_SESSION_EXPIRED, + payload: { lookerSessionExpired: isExpired } }) } } \ No newline at end of file diff --git a/src/projects/detail/containers/ProjectSummaryReportContainer.jsx b/src/projects/detail/containers/ProjectSummaryReportContainer.jsx index 9533a0430..b28ae1e22 100644 --- a/src/projects/detail/containers/ProjectSummaryReportContainer.jsx +++ b/src/projects/detail/containers/ProjectSummaryReportContainer.jsx @@ -4,6 +4,7 @@ import PT from 'prop-types' import { connect } from 'react-redux' import { withRouter } from 'react-router-dom' import MediaQuery from 'react-responsive' +import Modal from 'react-modal' import { SCREEN_BREAKPOINT_MD, @@ -17,7 +18,7 @@ import Sticky from '../../../components/Sticky' import ProjectInfoContainer from './ProjectInfoContainer' import PERMISSIONS from '../../../config/permissions' import { checkPermission } from '../../../helpers/permissions' -import { loadProjectSummary, loadProjectReportsUrls, refreshLookerSession } from '../../actions/projectReports' +import { loadProjectReportsUrls, setLookerSessionExpired } from '../../actions/projectReports' import spinnerWhileLoading from '../../../components/LoadingSpinner' import './ProjectSummaryReportContainer.scss' @@ -34,34 +35,60 @@ class ProjectSummaryReportContainer extends React.Component { constructor(props) { super(props) + + this.state = { + iframeKey: 0, // we would use it to force iframe to reload + } this.timer = null this.setLookerSessionTimer = this.setLookerSessionTimer.bind(this) + this.reloadProjectReport = this.reloadProjectReport.bind(this) + } + + reloadProjectReport() { + this.props.loadProjectReportsUrls(_.get(this.props, 'project.id'), PROJECT_REPORTS.PROJECT_SUMMARY) + // don't have to set session expire timer here, it would be set of iframe load + } + + componentWillMount() { + this.reloadProjectReport() + // don't have to set session expire timer here, it would be set of iframe load + } + + componentWillUnmount() { + if (this.timer) { + clearTimeout(this.timer) + } } componentWillUpdate(nextProps) { const nextReportProjectId = _.get(nextProps, 'reportsProjectId') const nextProjectId = _.get(nextProps, 'project.id') - const lookerSessionExpired = !this.props.lookerSessionExpired && nextProps.lookerSessionExpired - if(lookerSessionExpired || (nextProjectId && nextReportProjectId !== nextProjectId)) { - nextProps.loadProjectReportsUrls(nextProjectId, PROJECT_REPORTS.PROJECT_SUMMARY) - this.setLookerSessionTimer() + + if (nextProjectId && nextReportProjectId !== nextProjectId) { + this.props.loadProjectReportsUrls(nextProjectId, PROJECT_REPORTS.PROJECT_SUMMARY) + // don't have to set session expire timer here, it would be set of iframe load + } + + // when we get a new URL for report, force iframe to reload, in case the URL stays the same + if (this.props.isLoading && !nextProps.isLoading) { + this.setState({ + iframeKey: this.state.iframeKey + 1 + }) } } setLookerSessionTimer() { console.log('Setting Looker Session Timer') + if (this.timer) { clearTimeout(this.timer) } - 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 + + // set timeout for raising alert to refresh the token when session expires this.timer = setTimeout(() => { - console.log('Calling refresh looker session action') - this.props.refreshLookerSession() - }, (timeoutDuration)) + console.log('Looker Session is expired by timer') + this.props.setLookerSessionExpired(true) + }, REPORT_SESSION_LENGTH * 1000) } render() { @@ -78,6 +105,7 @@ class ProjectSummaryReportContainer extends React.Component { isLoading, location, projectSummaryEmbedUrl, + lookerSessionExpired, } = this.props const leftArea = ( @@ -111,13 +139,30 @@ class ProjectSummaryReportContainer extends React.Component { - { - - } + +
+ Report sessions expired +
+ +
+ To keep the data up to date, please, hit "Refresh" button to reload the report. +
+ +
+ +
+
+
) @@ -154,9 +199,8 @@ const mapStateToProps = ({ projectState, projectTopics, phasesTopics, projectRep } const mapDispatchToProps = { - loadProjectSummary, loadProjectReportsUrls, - refreshLookerSession, + setLookerSessionExpired, } export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ProjectSummaryReportContainer)) diff --git a/src/projects/reducers/projectReports.js b/src/projects/reducers/projectReports.js index c669904b8..18961e2c0 100644 --- a/src/projects/reducers/projectReports.js +++ b/src/projects/reducers/projectReports.js @@ -2,7 +2,7 @@ import { LOAD_PROJECT_SUMMARY_PENDING, LOAD_PROJECT_SUMMARY_SUCCESS, LOAD_PROJECT_SUMMARY_FAILURE, - REFRESH_LOOKER_SESSION, + SET_LOOKER_SESSION_EXPIRED, } from '../../config/constants' const initialState = { @@ -22,7 +22,7 @@ export const projectReports = function (state=initialState, action) { return Object.assign({}, state, { isLoading: true, error: false, - projectId: action.meta.projectId + projectId: action.meta.projectId, }) case LOAD_PROJECT_SUMMARY_SUCCESS: @@ -41,14 +41,13 @@ 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: { + case SET_LOOKER_SESSION_EXPIRED: { return Object.assign({}, state, { - lookerSessionExpired: true + lookerSessionExpired: payload }) }