diff --git a/server.js b/server.js index 9b7653a5..7aa29253 100644 --- a/server.js +++ b/server.js @@ -16,6 +16,16 @@ function check () { return true } app.use(healthCheck.middleware([check])) +app.use((req, res, next) => { + res.header('Referrer-Policy', 'strict-origin-when-cross-origin'); + res.header('Permissions-Policy', 'geolocation=(), microphone=(), camera=()'); + res.header('X-Content-Type-Options', 'nosniff'); + res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); + res.header('Cache-control', 'public, max-age=0'); + res.header('Pragma', 'no-cache'); + + next(); +}); // app.use(requireHTTPS) // removed because app servers don't handle https // app.use(express.static(__dirname)) app.use(express.static(path.join(__dirname, 'build'))) diff --git a/src/components/ChallengesComponent/ChallengeList/ChallengeList.module.scss b/src/components/ChallengesComponent/ChallengeList/ChallengeList.module.scss index f891e5fa..428e1229 100644 --- a/src/components/ChallengesComponent/ChallengeList/ChallengeList.module.scss +++ b/src/components/ChallengesComponent/ChallengeList/ChallengeList.module.scss @@ -20,6 +20,20 @@ padding-bottom: 10px; } } + + .title { + font-weight: bold; + } + .error { + font-weight: bold; + color: #BE405E; + } + .active { + color: #008000; + } + .inactive { + color: #BE405E; + } } .header { diff --git a/src/components/ChallengesComponent/ChallengeList/index.js b/src/components/ChallengesComponent/ChallengeList/index.js index 88634a4a..65329c5e 100644 --- a/src/components/ChallengesComponent/ChallengeList/index.js +++ b/src/components/ChallengesComponent/ChallengeList/index.js @@ -125,6 +125,10 @@ class ChallengeList extends Component { partiallyUpdateChallengeDetails, deleteChallenge, isBillingAccountExpired, + billingStartDate, + billingEndDate, + isBillingAccountLoadingFailed, + isBillingAccountLoading, selfService } = this.props if (warnMessage) { @@ -172,14 +176,29 @@ class ChallengeList extends Component { return (
- this.updateSearchParam(e.target.value, status)} - value={searchText} - /> + {!isBillingAccountLoading && !isBillingAccountLoadingFailed && !isBillingAccountExpired && ( +
+ Billing Account: {status}   Start Date: {billingStartDate}   End Date: {billingEndDate} +
+ )} + {!isBillingAccountLoading && !isBillingAccountLoadingFailed && isBillingAccountExpired && ( +
+ Billing Account: INACTIVE   Start Date: {billingStartDate}   End Date: {billingEndDate} +
+ )} + {!isBillingAccountLoading && isBillingAccountLoadingFailed && ( +
Billing Account failed to load
+ )} +
+ this.updateSearchParam(e.target.value, status)} + value={searchText} + /> +
{activeProject && ( { @@ -78,6 +82,10 @@ const ChallengesComponent = ({ partiallyUpdateChallengeDetails={partiallyUpdateChallengeDetails} deleteChallenge={deleteChallenge} isBillingAccountExpired={isBillingAccountExpired} + billingStartDate={billingStartDate} + billingEndDate={billingEndDate} + isBillingAccountLoadingFailed={isBillingAccountLoadingFailed} + isBillingAccountLoading={isBillingAccountLoading} selfService={selfService} auth={auth} /> @@ -106,6 +114,10 @@ ChallengesComponent.propTypes = { partiallyUpdateChallengeDetails: PropTypes.func.isRequired, deleteChallenge: PropTypes.func.isRequired, isBillingAccountExpired: PropTypes.bool, + billingStartDate: PropTypes.string, + billingEndDate: PropTypes.string, + isBillingAccountLoadingFailed: PropTypes.bool, + isBillingAccountLoading: PropTypes.bool, selfService: PropTypes.bool, auth: PropTypes.object.isRequired } diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index 60046ab1..6a12f204 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -34,6 +34,7 @@ class Challenges extends Component { resetSidebarActiveParams() } else if (projectId || selfService) { if (projectId) { + window.localStorage.setItem('projectLoading', 'true') this.props.loadProject(projectId) } this.reloadChallenges(this.props) @@ -51,9 +52,12 @@ class Challenges extends Component { if (activeProjectId !== challengeProjectId || selfService) { const isAdmin = checkAdmin(this.props.auth.token) this.props.loadChallengesByPage(1, projectId ? parseInt(projectId) : -1, CHALLENGE_STATUS.ACTIVE, '', selfService, isAdmin ? null : this.props.auth.user.handle) - if (!selfService && (!reduxProjectInfo || `${reduxProjectInfo.id}` !== projectId) + const projectLoading = window.localStorage.getItem('projectLoading') !== null + if (!selfService && (!reduxProjectInfo || `${reduxProjectInfo.id}` !== projectId) && !projectLoading ) { loadProject(projectId) + } else { + window.localStorage.removeItem('projectLoading') } } } @@ -87,6 +91,10 @@ class Challenges extends Component { partiallyUpdateChallengeDetails, deleteChallenge, isBillingAccountExpired, + billingStartDate, + billingEndDate, + isBillingAccountLoadingFailed, + isBillingAccountLoading, selfService, auth } = this.props @@ -155,6 +163,10 @@ class Challenges extends Component { partiallyUpdateChallengeDetails={partiallyUpdateChallengeDetails} deleteChallenge={deleteChallenge} isBillingAccountExpired={isBillingAccountExpired} + billingStartDate={billingStartDate} + billingEndDate={billingEndDate} + isBillingAccountLoadingFailed={isBillingAccountLoadingFailed} + isBillingAccountLoading={isBillingAccountLoading} selfService={selfService} auth={auth} /> @@ -186,6 +198,10 @@ Challenges.propTypes = { partiallyUpdateChallengeDetails: PropTypes.func.isRequired, deleteChallenge: PropTypes.func.isRequired, isBillingAccountExpired: PropTypes.bool, + billingStartDate: PropTypes.string, + billingEndDate: PropTypes.string, + isBillingAccountLoadingFailed: PropTypes.bool, + isBillingAccountLoading: PropTypes.bool, selfService: PropTypes.bool, auth: PropTypes.object.isRequired } @@ -197,6 +213,10 @@ const mapStateToProps = ({ challenges, sidebar, projects, auth }) => ({ projects: sidebar.projects, projectDetail: projects.projectDetail, isBillingAccountExpired: projects.isBillingAccountExpired, + billingStartDate: projects.billingStartDate, + billingEndDate: projects.billingEndDate, + isBillingAccountLoadingFailed: projects.isBillingAccountLoadingFailed, + isBillingAccountLoading: projects.isBillingAccountLoading, auth: auth }) diff --git a/src/reducers/projects.js b/src/reducers/projects.js index 464ef784..bc3d04e5 100644 --- a/src/reducers/projects.js +++ b/src/reducers/projects.js @@ -13,12 +13,33 @@ import { LOAD_PROJECT_PHASES_PENDING, LOAD_PROJECT_PHASES_SUCCESS } from '../config/constants' +import moment from 'moment-timezone' + +/** + * checks if billing is expired or not + * @param {boolean} active if billing account is active or not + * @param {string} endDate the end date + * @returns if billing expired or not + */ +const checkBillingExpired = (active, endDate) => { + if (active) { + if (moment().isBefore(endDate)) { + return false + } + return true + } + return true +} +const dateFormat = 'MMM DD, YYYY' const initialState = { isLoading: false, projectDetail: {}, isBillingAccountExpired: false, isBillingAccountLoading: false, + isBillingAccountLoadingFailed: false, + billingStartDate: null, + billingEndDate: null, isPhasesLoading: false, phases: [] } @@ -42,19 +63,27 @@ export default function (state = initialState, action) { return { ...state, isBillingAccountLoading: true, - isBillingAccountExpired: false + isBillingAccountExpired: false, + billingStartDate: '', + billingEndDate: '' } case LOAD_PROJECT_BILLING_ACCOUNT_SUCCESS: return { ...state, isBillingAccountLoading: false, - isBillingAccountExpired: !action.payload.active + isBillingAccountExpired: checkBillingExpired(action.payload.active, action.payload.endDate), + billingStartDate: moment(action.payload.startDate).format(dateFormat), + billingEndDate: moment(action.payload.endDate).format(dateFormat), + isBillingAccountLoadingFailed: false } case LOAD_PROJECT_BILLING_ACCOUNT_FAILURE: return { ...state, isBillingAccountLoading: false, - isBillingAccountExpired: false + isBillingAccountExpired: false, + billingStartDate: '', + billingEndDate: '', + isBillingAccountLoadingFailed: true } case LOAD_PROJECT_PHASES_PENDING: return {