diff --git a/package.json b/package.json index 5bf2fb11d729fd87bdf3c60050364bf1682ba334..67f057fc3fd55c5eb7d2324cb4bb78299b2b1205 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "react-highcharts": "^12.0.0", "react-json-tree": "^0.10.9", "react-leaflet": "^1.4.1", + "react-modal": "^3.8.1", "react-placeholder": "^1.0.6", "react-redux": "^4.4.5", "react-router": "^3.0.0", diff --git a/src/components/Blocnote/BudgetSimulator/BugetTable.js b/src/components/Blocnote/BudgetSimulator/BugetTable.js index 85f575ee3e8ea6938fd21bdf1332df0dc07fdb45..c44e3a752ce98c35cca1cc4bafe65a6c2bb9ead5 100644 --- a/src/components/Blocnote/BudgetSimulator/BugetTable.js +++ b/src/components/Blocnote/BudgetSimulator/BugetTable.js @@ -22,6 +22,7 @@ class BudgetTable extends Component { backgroundColor: '#EEEEEE', color: '#000000', fontWeight: 'bold', + padding: '15px', }; const header = [...['Savings'], ...this.props.savingPotentialList] .map((columnName) => { @@ -67,9 +68,9 @@ class BudgetTable extends Component { - {header} diff --git a/src/components/Blocnote/FinancialInputs/Bills.js b/src/components/Blocnote/FinancialInputs/Bills.js index e37b1d6a64dd71c6b6f0b1461172af17f762f6cd..aec9c3647ee18e10d826c7bbea9bee1651901028 100644 --- a/src/components/Blocnote/FinancialInputs/Bills.js +++ b/src/components/Blocnote/FinancialInputs/Bills.js @@ -15,11 +15,14 @@ const Bills = (props) => { electric: false, }; + let index = 0; if (props.data !== null) { - console.log(props.data); // eslint-disable-line - Object.keys(props.data).forEach(billName => { + Object.keys(props.data).forEach((billName) => { BillsTables.push( -
+
{
); billsExist[billName] = true; + index += 1; }); } - Object.entries(billsExist).forEach(billExist => { + Object.entries(billsExist).forEach((billExist) => { if (billExist[1] === false) { BillsTables.push( -
+
); + index += 1; } }); diff --git a/src/components/Blocnote/FinancialInputs/BillsOverview.js b/src/components/Blocnote/FinancialInputs/BillsOverview.js index 1fa5a241447d45ee2e4b46e74ee22d213c9c226b..7d3fb7a3ed159873612c707e610210dc7a506db9 100644 --- a/src/components/Blocnote/FinancialInputs/BillsOverview.js +++ b/src/components/Blocnote/FinancialInputs/BillsOverview.js @@ -16,6 +16,23 @@ class BillsOverview extends Component { super(props); this.toggleEstimation = this.toggleEstimation.bind(this); this.changeEstimation = this.changeEstimation.bind(this); + this.state = { + estimationDropdownOpen: false, + estimationDropDownValue: Object.keys(this.props.data).length !== 0 ? + this.props.data.estimation_algorithm : 'Select Estimation', + estimationOptions: [ + { id: 'RoughEstimation', key: 'RoughEstimation', name: 'Rough Estimation' }, + { id: 'FancyEstimation', key: 'FancyEstimation', name: 'Fancy Estimation' }, + ], + loading: props.loading, + action: null, + postBills: this.processBillsData(), + messageContent: null, + messageStyle: 'default', + }; + } + + processBillsData = () => { const data = {}; if (Object.keys(this.props.data).length !== 0) { ['electric', 'water', 'gas', 'oil'].forEach((billName) => { @@ -25,17 +42,7 @@ class BillsOverview extends Component { }); }); } - - this.state = { - estimationDropdownOpen: false, - estimationDropDownValue: 'Select Estimation', - estimationOptions: [ - { id: 'RoughEstimation', key: 'RoughEstimation', name: 'Rough Estimation' }, - { id: 'Fancy Estimation', key: 'FancyEstimation', name: 'Fancy Estimation' }, - ], - action: null, - postBills: data, - }; + return data; } toggleEstimation() { @@ -45,13 +52,17 @@ class BillsOverview extends Component { } changeEstimation(e) { - this.setState({ estimationDropDownValue: e.currentTarget.textContent }); + this.setState({ + estimationDropDownValue: e.currentTarget.textContent, + messageContent: null, + messageStyle: 'default', + }); } handleCreateBillsOverview = () => { this.props.createBillsOverview( this.props.buildingId, - this.state.postBills, + this.processBillsData(), ); this.setState({ action: 'created' }); } @@ -63,11 +74,16 @@ class BillsOverview extends Component { if (data['Estimation Model'] === 'Select Estimation') { alert('Please select an estimation model'); } else { - this.props.updateBillsOverview( - this.props.buildingId, - data, - ); - this.setState({ action: 'updated' }); + this.setState({ loading: true }, () => { + this.props.updateBillsOverview( + this.props.buildingId, + data, + ); + this.setState({ + loading: false, + action: 'updated', + }); + }); } } @@ -166,24 +182,35 @@ class BillsOverview extends Component { ); } - let messageStyle = {}; - let messageContent = null; + this.state.messageStyle = 'default'; + this.state.messageContent = null; - if (this.props.loading) { - messageContent = 'Processing ...'; - messageStyle = this.props.defaultMessageStyle; + if (this.props.loading === true) { + this.state.messageContent = 'Processing ...'; } if (this.props.error && typeof this.props.error === 'object') { - messageContent = this.props.error.response.statusText; - messageStyle = this.props.errorMessageStyle; + if (this.state.estimationDropDownValue !== 'Rough Estimation') { + this.state.messageContent = 'Only Rough Estimation available at this point'; + } + // } else { + // messageContent = this.props.error.response.statusText; + // } + this.state.messageStyle = 'error'; } if (!this.props.error && !this.props.loading - && this.props.data !== null - && this.state.action !== null) { - messageContent = this.state.action.charAt(0).toUpperCase() + this.state.action.slice(1); - messageStyle = this.props.successMessageStyle; + && this.props.data !== null) { + if (this.state.action === 'updated') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Projected successfully.'; + } else if (this.state.action === 'created') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Saved.'; + } else { + this.state.messageStyle = 'default'; + this.state.messageContent = null; + } } return ( @@ -208,8 +235,8 @@ class BillsOverview extends Component {
@@ -243,6 +270,7 @@ class BillsOverview extends Component { BillsOverview.propTypes = { data: PropTypes.shape({ + estimation_algorithm: PropTypes.string, electric: PropTypes.objectOf, electric_user: PropTypes.string, gas: PropTypes.objectOf, @@ -253,21 +281,6 @@ BillsOverview.propTypes = { water_user: PropTypes.string, total_annual_charge: PropTypes.objectOf, }), - successMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - errorMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - defaultMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), headerStyle: PropTypes.shape({ textAlign: PropTypes.string, paddingLeft: PropTypes.string, diff --git a/src/components/Blocnote/FinancialInputs/BillsRow.js b/src/components/Blocnote/FinancialInputs/BillsRow.js index d0a59b29e8a5c58a2b29b8bad9063f31f0613cbb..a1571eca514888ebb9d0e74541aa3cdf04abfe1b 100644 --- a/src/components/Blocnote/FinancialInputs/BillsRow.js +++ b/src/components/Blocnote/FinancialInputs/BillsRow.js @@ -22,10 +22,10 @@ const BillsRow = (props) => { }; BillsRow.propTypes = { - dateFrom: PropTypes.number, - dateTo: PropTypes.number, - usage: PropTypes.func, - charge: PropTypes.func, + dateFrom: PropTypes.string, + dateTo: PropTypes.string, + usage: PropTypes.number, + charge: PropTypes.string, }; export default BillsRow; diff --git a/src/components/Blocnote/FinancialInputs/BillsSummary.js b/src/components/Blocnote/FinancialInputs/BillsSummary.js index 05b6b1ba4b27932cc471cb0f2937ae149cc85e0c..d4e717b58fef680138c064fa6267b30e4b693ea8 100644 --- a/src/components/Blocnote/FinancialInputs/BillsSummary.js +++ b/src/components/Blocnote/FinancialInputs/BillsSummary.js @@ -18,20 +18,20 @@ class BillsSummary extends Component { const BillsSummaryTables = []; if (this.state.billsSummary !== null) { - console.log(this.state.billsSummary); // eslint-disable-line - Object.keys(this.state.billsSummary).forEach(billName => { + Object.keys(this.state.billsSummary).forEach((billName, index) => { BillsSummaryTables.push( -
+
); @@ -82,21 +82,6 @@ BillsSummary.propTypes = { }) ), }), - successMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - errorMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - defaultMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), headerStyle: PropTypes.shape({ textAlign: PropTypes.string, paddingLeft: PropTypes.string, diff --git a/src/components/Blocnote/FinancialInputs/BillsSummaryRow.js b/src/components/Blocnote/FinancialInputs/BillsSummaryRow.js index b7479d8d08cdc67d2e2549de1d34c484f9d587c7..5c765e5790fe2d8429f74f59612d06b933e93bc3 100644 --- a/src/components/Blocnote/FinancialInputs/BillsSummaryRow.js +++ b/src/components/Blocnote/FinancialInputs/BillsSummaryRow.js @@ -12,6 +12,7 @@ class BillsSummaryRow extends Component { constructor(props) { super(props); this.handleOnChange = this.handleOnChange.bind(this); + this.handleCreateBillsSummary = this.handleCreateBillsSummary.bind(this); this.handleUpdateBillsSummary = this.handleUpdateBillsSummary.bind(this); this.handleDeleteBillsSummary = this.handleDeleteBillsSummary.bind(this); this.state = { @@ -21,9 +22,34 @@ class BillsSummaryRow extends Component { }; } + validateInputs() { + const emptyFields = []; + if (this.state.year === null || isNaN(this.state.year) || this.state.year === '') { + emptyFields.push('Year'); + } + if (this.state.charge === null || isNaN(this.state.charge) || this.state.charge === '') { + emptyFields.push('Charge'); + } + if (emptyFields.length > 0) { + alert(`Please fill in ${emptyFields.join(', ')} field(s)`); + return false; + } + return true; + } + + handleCreateBillsSummary() { + if (this.validateInputs() === true) { + this.props.createBillsSummary({ + year: this.state.year, + charge: this.state.charge, + }); + } + } + handleUpdateBillsSummary() { - console.log(this.state); // eslint-disable-line - this.props.updateBillsSummary(this.state); + if (this.validateInputs() === true) { + this.props.updateBillsSummary(this.state); + } } handleDeleteBillsSummary() { @@ -31,10 +57,36 @@ class BillsSummaryRow extends Component { } handleOnChange = (event) => { - this.setState({ [event.target.name]: event.target.value }); + this.setState({ [event.target.name]: event.target.value }, + () => { + this.props.handleOnChange(this.state); + }, + ); } render() { + let actionButton = null; + + if (this.props.id === 0) { + actionButton = ( + + ); + } else { + actionButton = ( + + ); + } + return (
); } - let messageStyle = {}; - let messageContent = null; + this.state.messageStyle = 'default'; + this.state.messageContent = null; - if (this.props.loading) { - messageContent = 'Processing ...'; - messageStyle = this.props.defaultMessageStyle; + if (this.props.loading === true) { + this.state.messageContent = 'Processing ...'; } if (this.props.error && typeof this.props.error === 'object') { - messageContent = this.props.error.response.statusText; - messageStyle = this.props.errorMessageStyle; + this.state.messageStyle = 'error'; + this.state.messageContent = this.props.error.response.statusText; } if (!this.props.error && !this.props.loading && this.props.billData !== null && this.state.action !== null) { - messageContent = this.state.action.charAt(0).toUpperCase() + this.state.action.slice(1); - messageStyle = this.props.successMessageStyle; + this.state.messageStyle = 'success'; + this.state.messageContent = 'Saved!'; } const billName = this.props.billName.charAt(0).toUpperCase() + this.props.billName.slice(1); @@ -173,14 +203,14 @@ class BillsSummaryTable extends Component { }} > {' '}{' '} + style={this.state.messageStyle} + content={this.state.messageContent} + />   @@ -204,21 +234,6 @@ BillsSummaryTable.propTypes = { id: PropTypes.number, }) ), - successMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - errorMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - defaultMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), createBillsSummary: PropTypes.func, updateBillsSummary: PropTypes.func, deleteBillsSummary: PropTypes.func, diff --git a/src/components/Blocnote/FinancialInputs/BillsTable.js b/src/components/Blocnote/FinancialInputs/BillsTable.js index 1a2f32a48ae1e855e6f919e87431b1d37e7dde6d..b099048edf3a875261961b1756c14dd63fe64aec 100644 --- a/src/components/Blocnote/FinancialInputs/BillsTable.js +++ b/src/components/Blocnote/FinancialInputs/BillsTable.js @@ -10,7 +10,7 @@ import BillsRow from './BillsRow'; class BillsTable extends Component { constructor(props) { - global.expandNum = 15; + global.expandNum = 5; super(props); this.state = { billsData: (this.props.billsData.length > global.expandNum) ? @@ -60,7 +60,6 @@ class BillsTable extends Component { const headerStyle = { textAlign: 'center', fontWeight: 'bold', - // marginBottom: '15px', padding: '15px', borderBottom: '1px solid #999999', background: '#EEEEEE', @@ -92,10 +91,11 @@ class BillsTable extends Component { `Showing ${this.state.billsData.length} of ${this.props.billsData.length}` : null; if (this.state.billsData.length !== 0) { - rows = this.state.billsData.map((bill) => { + rows = this.state.billsData.map((bill, index) => { // charge = charge.toLocaleString(); return ( - + + ); } if (this.state.showCollapseButton) { collapseButton = ( - ); } diff --git a/src/components/Blocnote/FinancialInputs/CashBalance.js b/src/components/Blocnote/FinancialInputs/CashBalance.js index 2434ee26ee983d862725b63139c1a221ef1ec619..62c02d1ab69bda107c51eb29b0a60840c11677f6 100644 --- a/src/components/Blocnote/FinancialInputs/CashBalance.js +++ b/src/components/Blocnote/FinancialInputs/CashBalance.js @@ -19,7 +19,10 @@ class CashBalance extends Component { balance_amount: null, statement_date: null, is_from_balance_sheet: false, + loading: props.loading, action: null, + messageContent: null, + messageStyle: 'default', }; } @@ -158,24 +161,23 @@ class CashBalance extends Component { ); } - let messageStyle = {}; - let messageContent = null; + this.state.messageStyle = 'default'; + this.state.messageContent = null; - if (this.props.loading) { - messageContent = 'Updating ...'; - messageStyle = this.props.defaultMessageStyle; + if (this.props.loading === true) { + this.state.messageContent = 'Updating ...'; } if (this.props.error && typeof this.props.error === 'object') { - messageContent = this.props.error.response.statusText; - messageStyle = this.props.errorMessageStyle; + this.state.messageStyle = 'error'; + this.state.messageContent = this.props.error.response.statusText; } if (!this.props.error && !this.props.loading && this.props.data !== null - && this.state.action) { - messageContent = 'Saved!'; - messageStyle = this.props.successMessageStyle; + && this.state.action === 'updated') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Saved!'; } return ( @@ -198,9 +200,9 @@ class CashBalance extends Component {
   + style={this.state.messageStyle} + content={this.state.messageContent} + /> {' '}{' '} - - - +
+
+ + + +    + + + +
+
@@ -373,16 +372,6 @@ FinanceOverview.propTypes = { anticipatedCommissioningDate: PropTypes.string, anticipatedConstructionPeriod: PropTypes.number, baseURL: PropTypes.string, - successMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - errorMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), headerStyle: PropTypes.shape({ textAlign: PropTypes.string, paddingLeft: PropTypes.string, diff --git a/src/components/Blocnote/FinancialInputs/IncomeStatements.js b/src/components/Blocnote/FinancialInputs/IncomeStatements.js index 04a58767ca3f7f2698952f7758954ef21a2ea507..9b3e0c7338b4e74a6f87e00e574a41baeedc13ad 100644 --- a/src/components/Blocnote/FinancialInputs/IncomeStatements.js +++ b/src/components/Blocnote/FinancialInputs/IncomeStatements.js @@ -22,35 +22,39 @@ class IncomeStatements extends Component { this.handleOnChange = this.handleOnChange.bind(this); // this.handleOnChangeNew = this.handleOnChangeNew.bind(this); + const GRDropdownId = props.data.growth_rate !== null ? props.data.growth_rate : -99; + this.growthRateOptions = [ + { id: '-2', key: '-2', name: `CAGR=${props.data.cagr}%` }, + { id: '-1', key: '-1', name: 'Average' }, + { id: '0', key: '0', name: '0%' }, + { id: '1', key: '1', name: '1%' }, + { id: '2', key: '2', name: '2%' }, + { id: '3', key: '3', name: '3%' }, + { id: '4', key: '4', name: '4%' }, + { id: '5', key: '5', name: '5%' }, + { id: '6', key: '6', name: '6%' }, + { id: '7', key: '7', name: '7%' }, + { id: '8', key: '8', name: '8%' }, + { id: '9', key: '9', name: '9%' }, + { id: '10', key: '10', name: '10%' }, + { id: '11', key: '11', name: '11%' }, + { id: '12', key: '12', name: '12%' }, + { id: '13', key: '13', name: '13%' }, + { id: '14', key: '14', name: '14%' }, + { id: '15', key: '15', name: '15%' }, + ]; + const obj = { GRDropdownOpen: false, - GRDropdownValue: 'Select Growth Rate', - selectedDropdownId: 0, - growthRateOptions: [ - { id: '-2', key: '-2', name: 'CAGR=1.58%' }, - { id: '-1', key: '-1', name: 'Average' }, - { id: '0', key: '0', name: '0%' }, - { id: '1', key: '1', name: '1%' }, - { id: '2', key: '2', name: '2%' }, - { id: '3', key: '3', name: '3%' }, - { id: '4', key: '4', name: '4%' }, - { id: '5', key: '5', name: '5%' }, - { id: '6', key: '6', name: '6%' }, - { id: '7', key: '7', name: '7%' }, - { id: '8', key: '8', name: '8%' }, - { id: '9', key: '9', name: '9%' }, - { id: '10', key: '10', name: '10%' }, - { id: '11', key: '11', name: '11%' }, - { id: '12', key: '12', name: '12%' }, - { id: '13', key: '13', name: '13%' }, - { id: '14', key: '14', name: '14%' }, - { id: '15', key: '15', name: '15%' }, - ], + GRDropdownId, + growthRateOptions: this.growthRateOptions, incomeStatements: props.data, incomeStatementsNew: null, error: false, - loading: false, + loading: props.loading, action: null, + messageContent: null, + messageStyle: 'default', }; const histYears = {}; @@ -75,7 +79,6 @@ class IncomeStatements extends Component { } this.state = Object.assign(obj, histYears, incomeStatementsNew); - console.log(this.state); // eslint-disable-line } initIncomeStatement = () => { @@ -127,8 +130,7 @@ class IncomeStatements extends Component { } changeGrowthRate(e) { - this.setState({ GRDropdownValue: e.currentTarget.textContent }); - this.setState({ selectedDropdownId: e.currentTarget.id }); + this.setState({ GRDropdownId: e.currentTarget.id }); } buildHeader = () => { @@ -222,8 +224,9 @@ class IncomeStatements extends Component { hist.push(obj); }); const growthRate = {}; - growthRate['growth-rate'] = String(this.state.selectedDropdownId); + growthRate['growth-rate'] = String(this.state.GRDropdownId); hist.push(growthRate); + this.props.updateIncomeStatements( this.props.buildingId, hist, @@ -263,7 +266,7 @@ class IncomeStatements extends Component { return newStatement; }); - if (this.state.GRDropdownValue === 'Select Growth Rate') { + if (this.state.GRDropdownId === -99) { emptyMessages.push('Growth Rate'); } @@ -336,14 +339,17 @@ class IncomeStatements extends Component { if (this.state.incomeStatements.hist !== null && this.state.incomeStatements.future !== null) { header = this.buildHeader(); - const histYears = Object.keys(this.state.incomeStatements.hist).sort(); + const incomeStatements = this.state.incomeStatements; + const histYears = Object.keys(incomeStatements.hist).sort(); rows = columnKeys.map((columnKey, id) => { const cells = [ ...[columnNames[columnKey]], ...histYears.map((histYear) => { - let cellValue = this.state.incomeStatements.hist[histYear][columnKey]; - cellValue = Math.round(cellValue * 100) / 100; + // let cellValue = incomeStatements.hist[histYear][columnKey]; + // cellValue = Math.round(cellValue * 100) / 100; if (['utility_expense', 'revenue', 'non_utility_expense'].includes(columnKey)) { + let cellValue = this.state.incomeStatements.hist[histYear][columnKey]; + cellValue = Math.round(cellValue * 100) / 100; return ( @@ -361,18 +367,20 @@ class IncomeStatements extends Component { ); } + let cellValue = this.props.data.hist[histYear][columnKey]; + cellValue = Math.round(cellValue * 100) / 100; cellValue = cellValue.toLocaleString(); cellValue = `$${cellValue}`; return cellValue; }), - ...[this.state.incomeStatements.future[columnKey]].map((amount) => { + ...[this.props.data.future[columnKey]].map((amount) => { let cellValue = amount; cellValue = Math.round(cellValue * 100) / 100; cellValue = cellValue.toLocaleString(); cellValue = `$${cellValue}`; return cellValue; }), - ...Object.values([this.state.incomeStatements.average[columnKey]]).map((amount) => { + ...Object.values([this.props.data.average[columnKey]]).map((amount) => { let cellValue = amount; cellValue = Math.round(cellValue * 100) / 100; cellValue = cellValue.toLocaleString(); @@ -472,51 +480,61 @@ class IncomeStatements extends Component { ); } - let messageStyle = {}; - let messageContent = null; + this.state.messageStyle = 'default'; + this.state.messageContent = null; - if (this.state.loading) { - messageContent = 'Processing ...'; - messageStyle = this.props.defaultMessageStyle; + if (this.props.loading === true) { + this.state.messageContent = 'Updating ...'; } - if (this.state.error && typeof this.state.error === 'object') { - messageContent = this.state.error.response.statusText; - messageStyle = this.props.errorMessageStyle; + if (this.props.error && typeof this.props.error === 'object') { + this.state.messageStyle = 'error'; + this.state.messageContent = this.props.error.response.statusText; } - if (!this.state.error && !this.state.loading + if (!this.props.error && !this.props.loading && this.state.incomeStatements !== null - && this.state.action !== null) { - messageContent = this.state.action.charAt(0).toUpperCase() + this.state.action.slice(1); - messageStyle = this.props.successMessageStyle; + && this.state.action === 'updated') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Saved and reloaded.'; } + let GRDropdownValue = 'Select Growth Rate'; + this.growthRateOptions.forEach(option => { + if (option.id === String(parseInt(parseFloat(this.state.GRDropdownId).toFixed(2) * 100, 10)) + || option.id === this.state.GRDropdownId + || parseInt(option.id, 10) === this.state.GRDropdownId) { + GRDropdownValue = option.name; + } + }); + return (

Income Statements (in $, End of Year)

-
+
- {this.state.GRDropdownValue} + {GRDropdownValue} {growthRateOptions}
-
+
   + style={this.state.messageStyle} + content={this.state.messageContent} + /> +
+
{calculateSaveButton}
@@ -587,22 +605,9 @@ IncomeStatements.propTypes = { non_utility_operating_expense: PropTypes.string, })), }), - successMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - errorMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - defaultMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), updateIncomeStatements: PropTypes.func, + error: PropTypes.bool, + loading: PropTypes.bool, }; export default IncomeStatements; diff --git a/src/components/Blocnote/FinancialInputs/LoanOptions.js b/src/components/Blocnote/FinancialInputs/LoanOptions.js index 3b40ddb19f9aad2f9fa98024e33a417ff2269cf8..a01e9c54c1e8f6756b2d81a5b3a59cb861a0cf10 100644 --- a/src/components/Blocnote/FinancialInputs/LoanOptions.js +++ b/src/components/Blocnote/FinancialInputs/LoanOptions.js @@ -17,6 +17,7 @@ class LoanOptions extends Component { this.deleteRow = this.deleteRow.bind(this); this.addRow = this.addRow.bind(this); this.updateRow = this.updateRow.bind(this); + this.handleUpdateLoanOptions = this.handleUpdateLoanOptions.bind(this); this.state = { loanOptions: props.data, lenders: props.lenders, @@ -27,7 +28,10 @@ class LoanOptions extends Component { interest_rate: null, duration: null, max_loan_amount: null, + loading: props.loading, action: null, + messageContent: null, + messageStyle: 'default', }; } @@ -35,7 +39,6 @@ class LoanOptions extends Component { const emptyFields = []; if (this.state.loanOptions.length > 0) { Object.values(this.state.loanOptions).forEach(loanOption => { - console.log(loanOption); // eslint-disable-line if (loanOption.lender === null && !emptyFields.includes('Lender')) { emptyFields.push('Lender'); @@ -67,7 +70,6 @@ class LoanOptions extends Component { alert('Sorry, you need at least one loan option!'); } else if (confirm('Are you sure to delete this loan option?') === true) { const loanOptionsCopy = this.state.loanOptions.filter((loanOption, index) => { - console.log(loanOption); // eslint-disable-line return index !== id; }); this.setState({ loanOptions: loanOptionsCopy }, () => { @@ -86,7 +88,6 @@ class LoanOptions extends Component { duration: this.state.duration, max_loan_amount: this.state.max_loan_amount, }; - console.log(this.state.loanOptions); // eslint-disable-line const loanOptions = this.state.loanOptions; loanOptions.push(loanOption); this.setState({ loanOptions }, () => { @@ -107,11 +108,9 @@ class LoanOptions extends Component { updateRow = (row) => { const loanOptions = this.state.loanOptions.map((loanOption, id) => { - console.log(id); // eslint-disable-line - console.log(row.id); // eslint-disable-line if (id === row.id) { const tmp = { id }; - tmp.lender = row.lender; + tmp.lender = row.lenderDropDownValue; tmp.interest_rate = row.interest_rate; tmp.duration = row.duration; tmp.max_loan_amount = row.max_loan_amount; @@ -125,22 +124,40 @@ class LoanOptions extends Component { } handleUpdateLoanOptions = () => { - if (this.validateInputs() === true) { - const formData = {}; - formData['noi-dscr'] = parseFloat(this.state.noi).toFixed(2); - formData['cash-dscr'] = parseFloat(this.state.cash).toFixed(2); - formData.instance = this.state.loanOptions; - this.props.updateLoanOptions( - this.props.buildingId, - formData, - ); - this.setState({ - loading: false, - action: 'updated', + if (isNaN(this.state.noi) === true) { + alert('Please fill in NOI DSCR'); + } else if (isNaN(this.state.cash) === true) { + alert('Please fill in Cash DSCR'); + } else if (this.validateInputs() === true) { + this.setState({ loading: true }, () => { + const formData = {}; + formData['noi-dscr'] = parseFloat(this.state.noi).toFixed(2); + formData['cash-dscr'] = parseFloat(this.state.cash).toFixed(2); + formData.instance = this.state.loanOptions.map(loanOption => { + return { + lender: loanOption.lender, + interest_rate: loanOption.interest_rate, + duration: loanOption.duration, + max_loan_amount: loanOption.max_loan_amount, + }; + }); + formData.lenders = this.state.lenders; + this.props.updateLoanOptions( + this.props.buildingId, + formData, + ); + this.setState({ + loading: false, + action: 'updated', + }); }); } } + handleOnChange = (event) => { + this.setState({ [event.target.name]: event.target.value }); + } + render() { const header = [ 'Loan Options', @@ -171,6 +188,7 @@ class LoanOptions extends Component { duration={loanOption.duration} maxLoanAmount={parseFloat(loanOption.max_loan_amount).toFixed(2)} onDelEvent={this.deleteRow} + onChangeEvent={this.updateRow} /> ); }); @@ -192,24 +210,23 @@ class LoanOptions extends Component { ); } - let messageStyle = {}; - let messageContent = null; + this.state.messageStyle = 'default'; + this.state.messageContent = null; - if (this.props.loading) { - messageContent = 'Updating ...'; - messageStyle = this.props.defaultMessageStyle; + if (this.props.loading === true) { + this.state.messageContent = 'Updating ...'; } if (this.props.error && typeof this.props.error === 'object') { - messageContent = this.props.error.response.statusText; - messageStyle = this.props.errorMessageStyle; + this.state.messageContent = this.props.error.response.statusText; + this.state.messageStyle = 'error'; } if (!this.props.error && !this.props.loading && this.props.data !== null - && this.state.updated) { - messageContent = 'Saved!'; - messageStyle = this.props.successMessageStyle; + && this.state.action === 'updated') { + this.state.messageContent = 'Saved!'; + this.state.messageStyle = 'success'; } return ( @@ -270,9 +287,9 @@ class LoanOptions extends Component {
   + style={this.state.messageStyle} + content={this.state.messageContent} + /> {' '}{' '}
+ + + ); } return (
+ {this.props.tableName} - +
@@ -70,10 +122,8 @@ class BillsSummaryRow extends Component { - {' '} + {actionButton} + {' '}
+ There is no cost estimation. +
- +
+ Cost Estimation +
+ - + {rows} - - - + - +
- Cost Estimation -
Item Estimated CostOptionOption
); @@ -160,7 +170,7 @@ class CostEstimation extends Component { } CostEstimation.propTypes = { - blockStyle: PropTypes.string, + blockStyle: PropTypes.objectOf, // headerStyle: PropTypes.string, data: PropTypes.arrayOf, updateCostEstimation: PropTypes.func, diff --git a/src/components/Blocnote/PreliminaryFinance/DownPayment.js b/src/components/Blocnote/PreliminaryFinance/DownPayment.js index 5443c29f3fe15ac3de0ee33c7683cc28e83e6755..83180ca6642344cffd844e08910b6e5b61171fdf 100644 --- a/src/components/Blocnote/PreliminaryFinance/DownPayment.js +++ b/src/components/Blocnote/PreliminaryFinance/DownPayment.js @@ -5,7 +5,6 @@ import PropTypes from 'prop-types'; class DownPayment extends Component { constructor(props) { super(props); - this.state = { successMessages: [], error: false, @@ -28,9 +27,9 @@ class DownPayment extends Component { } DownPayment.propTypes = { - blockStyle: PropTypes.string, - headerStyle: PropTypes.string, - paymentAmount: PropTypes.string, + blockStyle: PropTypes.objectOf, + headerStyle: PropTypes.objectOf, + paymentAmount: PropTypes.number, }; export default DownPayment; diff --git a/src/components/Blocnote/PreliminaryFinance/InputScenario.js b/src/components/Blocnote/PreliminaryFinance/InputScenario.js index a31f7c5a5cc520d6587bbb0c507f7ea1587a8c68..396a746e4cfa7a6b081d6905a379343026e104c5 100644 --- a/src/components/Blocnote/PreliminaryFinance/InputScenario.js +++ b/src/components/Blocnote/PreliminaryFinance/InputScenario.js @@ -18,14 +18,23 @@ class InputScenario extends Component { savings: props.savings, loading: false, action: null, + messageContent: null, + messageStyle: 'default', }; } handleUpdateScenario = () => { const data = { scenario: this.state.scenarioName, - cost: this.state.costs, - savings: Object.values(this.state.savings), + cost: this.state.costs + .filter(costItem => + costItem.item !== '' && costItem.item !== null && costItem.cost !== null && costItem.cost !== 'null') + .map(costItem => { + return Object.assign({}, costItem, { cost: String(costItem.cost) }); + }), + savings: Object.values(this.state.savings).map(saving => { + return Object.assign({}, saving, { estimated_savings: String(saving.estimated_savings) }); + }), }; console.log(data); // eslint-disable-line this.props.updateScenario( @@ -52,24 +61,21 @@ class InputScenario extends Component { } render() { - let messageStyle = {}; - let messageContent = null; - if (this.props.loading) { - messageContent = 'Processing ...'; - messageStyle = this.props.defaultMessageStyle; + this.state.messageContent = 'Processing ...'; + this.state.messageStyle = 'default'; } if (this.props.error && typeof this.props.error === 'object') { - messageContent = this.props.error.response.statusText; - messageStyle = this.props.errorMessageStyle; + this.state.messageContent = this.props.error.response.statusText; + this.state.messageStyle = 'error'; } if (!this.props.error && !this.props.loading && this.props.data !== null && this.state.action !== null) { - messageContent = this.state.action.charAt(0).toUpperCase() + this.state.action.slice(1); - messageStyle = this.props.successMessageStyle; + this.state.messageContent = 'Saved and reloaded'; + this.state.messageStyle = 'success'; } return ( @@ -90,9 +96,9 @@ class InputScenario extends Component {
{' '}{' '} + style={this.state.messageStyle} + content={this.state.messageContent} + />   
+ + + + + + + + + + + + + + +
+ Number of Units + + +
+ Vacant Units (Pre-retrofit) + + +
+ Vacant Units (Post-retrofit) + + +
+ + ); + } +} + +Occupants.propTypes = { + userId: PropTypes.string, + totalUnits: PropTypes.number, + preVacantUnits: PropTypes.number, + postVacantUnits: PropTypes.number, + updateOccupants: PropTypes.func, + buildingId: PropTypes.number, + occupantsLog: PropTypes.arrayOf, + data: PropTypes.objectOf, + error: PropTypes.bool, + loading: PropTypes.bool, +}; + +Occupants.defaultProps = { + totalUnits: 0, + preVacantUnits: 0, + postVacantUnits: 0, +}; + +export default Occupants; diff --git a/src/components/Performance/ResultMessage.js b/src/components/Performance/ResultMessage.js new file mode 100644 index 0000000000000000000000000000000000000000..7a784917de5e27fb65180a0e5129496716069f3a --- /dev/null +++ b/src/components/Performance/ResultMessage.js @@ -0,0 +1,49 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +const successStyle = { + color: '#336633', + padding: '8px 25px 8px 25px', + fontWeight: 'bold', +}; +const errorStyle = { + color: 'red', + padding: '10px 25px 10px 25px', + fontWeight: 'bold', +}; +const defaultStyle = { + color: 'black', + padding: '10px 25px 10px 25px', + fontWeight: 'bold', +}; + +const ResultMessage = (props) => { + if (props.style === 'success') { + return ( + + ✓  {props.content} + + ); + } else if (props.style === 'error') { + return ( + + {/* ×  {props.content} */} +   {props.content} + + ); + } + return ( + + {props.content} + + ); + +}; + +ResultMessage.propTypes = { + style: PropTypes.string, + content: PropTypes.string, +}; + +export default ResultMessage; diff --git a/src/components/Performance/RetrofitRow.js b/src/components/Performance/RetrofitRow.js new file mode 100644 index 0000000000000000000000000000000000000000..cc0804b63ecdf22ddb9eb9968283836348dd6b69 --- /dev/null +++ b/src/components/Performance/RetrofitRow.js @@ -0,0 +1,329 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { + Input, + Button, + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem, +} from 'reactstrap'; + + +class RetrofitRow extends Component { + constructor(props) { + super(props); + this.handleOnChange = this.handleOnChange.bind(this); + this.toggleRetrofit = this.toggleRetrofit.bind(this); + this.togglePrimaryUtility = this.togglePrimaryUtility.bind(this); + this.toggleSecondUtility = this.toggleSecondUtility.bind(this); + this.changeRetrofit = this.changeRetrofit.bind(this); + this.changePrimaryUtility = this.changePrimaryUtility.bind(this); + this.changeSecondUtility = this.changeSecondUtility.bind(this); + this.handleCreateRetrofit = this.handleCreateRetrofit.bind(this); + this.handleUpdateRetrofit = this.handleUpdateRetrofit.bind(this); + this.handleDeleteRetrofit = this.handleDeleteRetrofit.bind(this); + this.state = { + id: props.id, + retrofit_id: props.retrofitId, + install_start_date: props.installStartDate, + install_end_date: props.installEndDate, + primary_utility: props.primaryUtility, + second_utility: props.secondUtility, + notes: props.notes, + retrofitDropdownOpen: false, + retrofitDropDownValue: this.getRetrofitName(), + retrofitMeta: props.retrofitMeta !== undefined ? + props.retrofitMeta.map((retrofit, id) => { + return { id, key: retrofit.id, name: retrofit.retrofit_name }; + }) : [], + primaryUtilityDropdownOpen: false, + secondUtilityDropdownOpen: false, + utilityNames: props.utilityNames !== undefined ? props.utilityNames.map((utilityName, id) => { + return { id, key: id, name: utilityName }; + }) : [], + }; + } + + getRetrofitName = () => { + let retrofitName = null; + this.props.retrofitMeta.forEach((retrofit) => { + if (retrofit.id === this.props.retrofitId) { + retrofitName = retrofit.retrofit_name; + } + }); + return retrofitName; + }; + + updateRetrofit = () => { + const retrofit = { + id: this.props.id, + retrofit_id: this.state.retrofit_id, + install_start_date: this.state.install_start_date, + install_end_date: this.state.install_end_date, + primary_utility: this.state.primary_utility, + second_utility: this.state.second_utility, + notes: this.state.notes, + }; + this.props.onChangeEvent(retrofit); + }; + + toggleRetrofit() { + this.setState({ + retrofitDropdownOpen: !this.state.retrofitDropdownOpen, + }); + } + + togglePrimaryUtility() { + this.setState({ + primaryUtilityDropdownOpen: !this.state.primaryUtilityDropdownOpen, + }); + } + + toggleSecondUtility() { + this.setState({ + secondUtilityDropdownOpen: !this.state.secondUtilityDropdownOpen, + }); + } + + changeRetrofit(e) { + this.setState({ + retrofit_id: e.currentTarget.id, + retrofitDropDownValue: e.currentTarget.textContent, + }); + } + + changePrimaryUtility(e) { + this.setState({ primary_utility: e.currentTarget.textContent }, () => { + this.updateRetrofit(); + }); + } + + changeSecondUtility(e) { + this.setState({ second_utility: e.currentTarget.textContent }, () => { + this.updateRetrofit(); + }); + } + + handleOnChange = (event) => { + this.setState({ [event.target.name]: event.target.value }, () => { + this.updateRetrofit(); + }); + } + + handleCreateRetrofit() { + if (this.validateInputs() === true) { + this.props.createRetrofit({ + retrofit_id: this.state.retrofit_id, + install_start_date: this.state.install_start_date, + install_end_date: this.state.install_end_date, + primary_utility: this.state.primary_utility, + second_utility: this.state.second_utility, + notes: this.state.notes, + }); + } + } + + handleUpdateRetrofit() { + if (this.validateInputs() === true) { + this.props.updateRetrofit({ + id: this.props.id, + retrofit_id: this.state.retrofit_id, + install_start_date: this.state.install_start_date, + install_end_date: this.state.install_end_date, + primary_utility: this.state.primary_utility, + second_utility: this.state.second_utility, + notes: this.state.notes, + }); + } + } + + handleDeleteRetrofit() { + this.props.deleteRetrofit(this.props.id); + } + + validateInputs() { + const emptyFields = []; + if (this.state.install_start_date === null || this.state.install_start_date === '') { + emptyFields.push('Install start date'); + } + if (this.state.install_end_date === null || this.state.install_end_date === '') { + emptyFields.push('Install end date'); + } + if (emptyFields.length > 0) { + alert(`Please fill in ${emptyFields.join(', ')} field(s)`); + return false; + } + return true; + } + + render() { + const style = { textAlign: 'left' }; + const retrofitMeta = this.state.retrofitMeta.map(e => { + return ( + + {e.name} + + ); + }); + const primaryUtilityNames = this.state.utilityNames.map(e => { + return ( + + {e.name} + + ); + }); + const secondUtilityNames = this.state.utilityNames.map(e => { + return ( + + {e.name} + + ); + }); + const dropdownStyle = { + marginLeft: '0px', + }; + + let actionButton = null; + + if (this.props.id === 0) { + actionButton = ( + + ); + } else { + actionButton = ( + + ); + } + return ( + + + + + {this.state.retrofitDropDownValue} + + + {retrofitMeta} + + + + + + + + + + + + + {this.state.primary_utility} + + + {primaryUtilityNames} + + + + + + + {this.state.second_utility} + + + {secondUtilityNames} + + + + + + + + {actionButton} + {' '} + + + + ); + } +} + +RetrofitRow.propTypes = { + id: PropTypes.number, + retrofitId: PropTypes.number, + // retrofitName: PropTypes.string, + installStartDate: PropTypes.string, + installEndDate: PropTypes.string, + primaryUtility: PropTypes.string, + secondUtility: PropTypes.string, + notes: PropTypes.string, + utilityNames: PropTypes.arrayOf, + retrofitMeta: PropTypes.arrayOf, + createRetrofit: PropTypes.func, + updateRetrofit: PropTypes.func, + deleteRetrofit: PropTypes.func, + onChangeEvent: PropTypes.func, +}; + +export default RetrofitRow; diff --git a/src/components/Performance/Retrofits.js b/src/components/Performance/Retrofits.js new file mode 100644 index 0000000000000000000000000000000000000000..c1a3c05eb41edba0c235ba388ff3f88a66cf5470 --- /dev/null +++ b/src/components/Performance/Retrofits.js @@ -0,0 +1,294 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { + Button, + Table, +} from 'reactstrap'; +import RetrofitRow from './RetrofitRow'; +import ResultMessage from './ResultMessage'; + + +class Retrofits extends Component { + constructor(props) { + super(props); + this.state = { + utilityNames: props.utilityNames, + retrofits: props.retrofits, + retrofitsLog: props.retrofitsLog, + loading: props.loading, + action: null, + messageContent: null, + messageStyle: 'default', + }; + } + + validateInputs = () => { + const emptyFields = []; + if (this.state.retrofits !== null && this.state.retrofits.length > 0) { + Object.values(this.state.retrofits).forEach(retrofit => { + if (retrofit.retrofit_name === null && + !emptyFields.includes('Retrofit Name')) { + emptyFields.push('Retrofit Name'); + } + if (retrofit.install_start_date === '' && + !emptyFields.includes('Installation Start Date')) { + emptyFields.push('Installation Start Date'); + } + if (retrofit.install_end_date === '' && + !emptyFields.includes('Installation End Date')) { + emptyFields.push('Installation End Date'); + } + if (retrofit.primary_utility === null && + !emptyFields.includes('Primary Influenced Utility')) { + emptyFields.push('Primary Influenced Utility'); + } + if (retrofit.second_utility === null && + !emptyFields.includes('Second Influenced Utility')) { + emptyFields.push('Second Influenced Utility'); + } + }); + } + + if (emptyFields.length > 0) { + alert(`Please fill in ${emptyFields.join(', ')} fields`); + return false; + } + return true; + } + + addRow = () => { + if (this.validateInputs() === true) { + const retrofits = this.state.retrofits; + retrofits.push({ + id: 0, + retrofit_id: 1, + install_start_date: '', + install_end_date: '', + primary_utility: 'Natural Gas', + second_utility: 'Water', + notes: '', + }); + this.setState({ retrofits }, () => { + console.log(this.state.retrofits, 'New retrofit added!'); // eslint-disable-line + }); + } + } + + handleOnChange = (row) => { + const retrofits = this.state.retrofits.map((retrofit) => { + if (retrofit.id === row.id) { + const tmp = { id: retrofit.id, building_id: retrofit.building_id }; + tmp.retrofit_id = row.retrofit_id; + tmp.install_start_date = row.install_start_date; + tmp.install_end_date = row.install_end_date; + tmp.primary_utility = row.primary_utility; + tmp.second_utility = row.second_utility; + tmp.notes = row.notes; + return tmp; + } + return retrofit; + }); + this.setState({ retrofits }, () => { + console.log(retrofits); // eslint-disable-line + }); + } + + createRetrofit = (retrofit) => { + this.props.createRetrofit( + this.props.buildingId, + retrofit, + ); + console.log('saga done!'); // eslint-disable-line + this.setState({ action: 'created' }); + } + + updateRetrofit = (retrofit) => { + this.props.updateRetrofit( + this.props.buildingId, + retrofit, + ); + this.setState({ action: 'updated' }); + } + + deleteRetrofit = id => { + if (confirm('Are you sure to delete this retrofit?') === true) { + if (id === 0) { + this.setState({ + retrofits: this.state.retrofits.filter(retrofit => retrofit.id !== 0), + }); + } else { + this.props.deleteRetrofit( + this.props.buildingId, + { id }, + ); + + this.setState({ + retrofits: this.state.retrofits.filter(retrofit => retrofit.id !== id), + action: 'deleted', + }); + } + } + } + + render() { + const header = [ + 'Retrofit Name', + 'Installation Start Date', + 'Installation End Date', + 'Primary Influenced Utility', + 'Second Influenced Utility', + 'Notes', + ' ', + ].map((title, key) => { + return ( + + {title} + + ); + }); + + let retrofits = []; + let historyButton = null; + this.state.retrofits = this.props.retrofits; + if (this.state.retrofits !== null && Object.keys(this.state.retrofits).length !== 0) { + retrofits = this.state.retrofits.map((retrofit) => { + return ( + + ); + }); + } else { + retrofits = ( + + + Currently no retrofits. + + + ); + } + + if (this.state.retrofitsLog !== null && Object.keys(this.state.retrofitsLog).length !== 0) { + historyButton = ( + + ); + } + + this.state.messageStyle = 'default'; + this.state.messageContent = null; + + if (this.props.loading === true) { + this.state.messageContent = 'Updating ...'; + } + + if (this.props.error && typeof this.props.error === 'object') { + this.state.messageStyle = 'error'; + this.state.messageContent = this.props.error.response.statusText; + } + + if (!this.props.error && !this.props.loading + && this.props.data !== null) { + if (this.state.action === 'created') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Created!'; + } else if (this.state.action === 'updated') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Saved!'; + } else if (this.state.action === 'deleted') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Deleted!'; + } + } + + return ( +
+
+
+

+ My Retrofits +

+
+
+ {' '}{' '} +   + {historyButton} +
+
+
+
+
+
+ + + {header} + + + {retrofits} + +
+
+
+
+
+
+ ); + } +} + +Retrofits.propTypes = { + blockStyle: PropTypes.shape({ + marginBottom: PropTypes.string, + }), + headerStyle: PropTypes.shape({ + textAlign: PropTypes.string, + marginBottom: PropTypes.string, + }), + retrofitMeta: PropTypes.arrayOf, + utilityNames: PropTypes.arrayOf, + retrofits: PropTypes.arrayOf, + retrofitsLog: PropTypes.arrayOf, + data: PropTypes.arrayOf( + PropTypes.shape({ + duration: PropTypes.string, + interest_rate: PropTypes.string, + lender: PropTypes.string, + max_loan_amount: PropTypes.string, + }), + ), + buildingId: PropTypes.number, + createRetrofit: PropTypes.func, + updateRetrofit: PropTypes.func, + deleteRetrofit: PropTypes.func, + error: PropTypes.bool, + loading: PropTypes.bool, +}; + +export default Retrofits; diff --git a/src/components/Performance/ThermalComfort.js b/src/components/Performance/ThermalComfort.js new file mode 100644 index 0000000000000000000000000000000000000000..0023ce00c17230d4450a081dda599c3c6257209c --- /dev/null +++ b/src/components/Performance/ThermalComfort.js @@ -0,0 +1,352 @@ +import React, { Component } from 'react'; +import Modal from 'react-modal'; +import PropTypes from 'prop-types'; +import { + Table, + Input, + InputGroup, + InputGroupAddon, + Button, +} from 'reactstrap'; +import './styles.css'; +import ResultMessage from './ResultMessage'; + + +const customStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + minWidth: '750px', + }, +}; + +Modal.setAppElement('body'); + +class ThermalComfort extends Component { + constructor(props) { + super(props); + this.state = { + preHeatingSetPoint: props.preHeatingSetPoint, + postHeatingSetPoint: props.postHeatingSetPoint, + preCoolingSetPoint: props.preCoolingSetPoint, + postCoolingSetPoint: props.postCoolingSetPoint, + modalIsOpen: false, + action: null, + thermalComfortLog: (this.props.thermalComfortLog.length > global.expandNum) ? + this.props.thermalComfortLog.slice(0, global.expandNum) : this.props.thermalComfortLog, + showNextButton: (this.props.thermalComfortLog.length > global.expandNum) === true, + currentLogPos: 0, + showlastButton: false, + }; + this.openModal = this.openModal.bind(this); + this.closeModal = this.closeModal.bind(this); + } + nextRecords = () => { + this.setState({ + currentLogPos: this.state.currentLogPos + global.expandNum, + }, () => { + this.setState({ + thermalComfortLog: + this.props.thermalComfortLog.slice(this.state.currentLogPos, + this.state.currentLogPos + global.expandNum), + showLastButton: true, + }); + if (this.state.currentLogPos + global.expandNum >= this.props.thermalComfortLog.length) { + this.setState({ + showNextButton: false, + }); + } + }); + } + lastRecords = () => { + this.setState({ + thermalComfortLog: this.props.thermalComfortLog.slice( + this.state.currentLogPos - global.expandNum, + this.state.currentLogPos + ), + currentLogPos: this.state.currentLogPos - global.expandNum, + showNextButton: true, + }, () => { + if (this.state.currentLogPos === 0) { + this.setState({ + showLastButton: false, + }); + } + }); + } + openModal() { + this.setState({ modalIsOpen: true }); + } + closeModal() { + this.setState({ modalIsOpen: false }); + } + handleOnChange = (event) => { + this.setState({ [event.target.name]: event.target.value }); + } + handleUpdateThermalComfort = () => { + if (confirm('Are you sure to update?') === true) { + this.props.updateThermalComfort( + this.props.buildingId, + { + building_id: this.props.buildingId, + pre_heating_set_point: this.state.preHeatingSetPoint, + post_heating_set_point: this.state.postHeatingSetPoint, + pre_cooling_set_point: this.state.preCoolingSetPoint, + post_cooling_set_point: this.state.postCoolingSetPoint, + user_id: this.props.userId, + } + ); + this.setState({ action: 'updated' }); + } + } + + render() { + const rows = this.state.thermalComfortLog.map((thermalComfort) => { + const delta = Object.entries(thermalComfort.delta) + .filter(entry => entry[0] !== 'id' && entry[0] !== 'building_id') + .map((entry, index) => { + return ( +
+ {entry[0]} : {entry[1]} +
+ ); + }); + + let updatedTime = new Date(thermalComfort.updated_time); + updatedTime = `${updatedTime.toLocaleDateString('en-US')} ${updatedTime.toLocaleTimeString('en-US')}`; + + return ( + + {thermalComfort.id} + {delta} + {thermalComfort.user_id} + {updatedTime} + + ); + }); + + let nextButton = null; + let lastButton = null; + const buttonStyle = { + textAlign: 'center', + marginBottom: '10px', + marginLeft: '0px', + cursor: 'pointer', + }; + + if (this.state.showNextButton) { + nextButton = ( + + ); + } + if (this.state.showLastButton) { + lastButton = ( + + ); + } + + this.state.messageStyle = 'default'; + this.state.messageContent = null; + + if (this.props.loading === true) { + this.state.messageContent = 'Updating ...'; + } + + if (this.props.error && typeof this.props.error === 'object') { + this.state.messageStyle = 'error'; + this.state.messageContent = this.props.error.response.statusText; + } + + if (!this.props.error && !this.props.loading + && this.props.data !== null + && this.state.action === 'updated') { + this.state.messageStyle = 'success'; + this.state.messageContent = 'Saved!'; + } + + return ( +
+
+
+

+ Thermal Comfort +

+
+
+ {' '}{' '} + {' '} + + {' '} +
+
+ +
+
+

Change History

+
+
+ +
+
+
+ + + + + + + + + {rows} + +
IDChangesUser IDUpdated Time
+
+
+ {lastButton} +
+
+ {nextButton} +
+
+
+
+ + + + + + + + + + + + + + + + + +
+ Heating Set Point (Pre-retrofit) + + + + + F + + +
+ Heating Set point (Post-retrofit) + + + + + F + + +
+ Cooling Set Point (Pre-retrofit) + + + + + F + + +
+ Cooling Set Point (Post-retrofit) + + + + + F + + +
+
+ ); + } +} + +ThermalComfort.propTypes = { + userId: PropTypes.string, + preHeatingSetPoint: PropTypes.number, + postHeatingSetPoint: PropTypes.number, + preCoolingSetPoint: PropTypes.number, + postCoolingSetPoint: PropTypes.number, + updateThermalComfort: PropTypes.func, + buildingId: PropTypes.number, + thermalComfortLog: PropTypes.arrayOf, + data: PropTypes.objectOf, + error: PropTypes.bool, + loading: PropTypes.bool, +}; + +export default ThermalComfort; diff --git a/src/components/Performance/styles.css b/src/components/Performance/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..97b24ba89e6cd6e314e27e3acda999bfaa847e83 --- /dev/null +++ b/src/components/Performance/styles.css @@ -0,0 +1,13 @@ +.test { + display: block !important; +} + +.header { + margin-bottom: 25px; + padding-left: 10px; +} + +.rightAlign { + text-align: right; + padding-right: 28px !important; +} diff --git a/src/components/SideBarDetail/index.js b/src/components/SideBarDetail/index.js index d648702f89cab73b7a23fc7594e63f2a4f295a32..ee470758d7464e74b85c4a0253989926a26256fe 100644 --- a/src/components/SideBarDetail/index.js +++ b/src/components/SideBarDetail/index.js @@ -12,6 +12,7 @@ import { envelopeSVG, reportsSVG, blocnoteSVG, + performanceSVG, iotSVG, } from './sidebarSVGs'; import userPropType from '../../containers/User/propTypes'; @@ -210,6 +211,12 @@ export default function SideBarDetail({ building, children, contacts, user }) { Reports +
  • + + {performanceSVG} + Performance + +
  • {blocnoteSVG} diff --git a/src/components/SideBarDetail/sidebarSVGs.js b/src/components/SideBarDetail/sidebarSVGs.js index cb06b34a109aa69e955c3e5eb123469c2e99d522..35a6ce3d09c9b317eaa3b99196616d1aa60d4017 100644 --- a/src/components/SideBarDetail/sidebarSVGs.js +++ b/src/components/SideBarDetail/sidebarSVGs.js @@ -88,3 +88,11 @@ export const iotSVG = ( ); + +export const performanceSVG = ( + + + + + +); diff --git "a/src/containers/Blocnote/FinancialInputs/\bIncomeStatements.js" "b/src/containers/Blocnote/FinancialInputs/\bIncomeStatements.js" deleted file mode 100644 index e8db590725766d50e245303c520eeace7b26ae79..0000000000000000000000000000000000000000 --- "a/src/containers/Blocnote/FinancialInputs/\bIncomeStatements.js" +++ /dev/null @@ -1,605 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { - Dropdown, - DropdownToggle, - DropdownMenu, - DropdownItem, - Button, - Input, - InputGroup, - InputGroupAddon, - Table, -} from 'reactstrap'; -import ResultMessage from './../../../components/Blocnote/FinancialInputs/ResultMessage'; - - -class IncomeStatements extends Component { - constructor(props) { - super(props); - this.toggleGrowthRate = this.toggleGrowthRate.bind(this); - this.changeGrowthRate = this.changeGrowthRate.bind(this); - this.handleOnChange = this.handleOnChange.bind(this); - // this.handleOnChangeNew = this.handleOnChangeNew.bind(this); - - const obj = { - GRDropdownOpen: false, - GRDropdownValue: 'Select Growth Rate', - selectedDropdownId: 0, - growthRateOptions: [ - { id: '-2', key: '-2', name: 'CAGR=1.58%' }, - { id: '-1', key: '-1', name: 'Average' }, - { id: '0', key: '0', name: '0%' }, - { id: '1', key: '1', name: '1%' }, - { id: '2', key: '2', name: '2%' }, - { id: '3', key: '3', name: '3%' }, - { id: '4', key: '4', name: '4%' }, - { id: '5', key: '5', name: '5%' }, - { id: '6', key: '6', name: '6%' }, - { id: '7', key: '7', name: '7%' }, - { id: '8', key: '8', name: '8%' }, - { id: '9', key: '9', name: '9%' }, - { id: '10', key: '10', name: '10%' }, - { id: '11', key: '11', name: '11%' }, - { id: '12', key: '12', name: '12%' }, - { id: '13', key: '13', name: '13%' }, - { id: '14', key: '14', name: '14%' }, - { id: '15', key: '15', name: '15%' }, - ], - incomeStatements: props.data, - incomeStatementsNew: null, - error: false, - loading: false, - action: null, - }; - - const histYears = {}; - const incomeStatementsNew = { incomeStatementsNew: [] }; - if (props.data.hist !== null) { - Object.keys(props.data.hist).forEach((year, id) => { - const histYear = `Year-${id + 1}`; - histYears[histYear] = parseInt(year, 10); - }); - } else { - [0, 1, 2].forEach(id => { - const histYear = `Year-${id + 1}`; - histYears[histYear] = null; - }); - - const growthRate = {}; - growthRate['growth-rate'] = props.data.cagr; - incomeStatementsNew.incomeStatementsNew.push(this.initIncomeStatement()); - incomeStatementsNew.incomeStatementsNew.push(this.initIncomeStatement()); - incomeStatementsNew.incomeStatementsNew.push(this.initIncomeStatement()); - incomeStatementsNew.incomeStatementsNew.push(growthRate); - } - - this.state = Object.assign(obj, histYears, incomeStatementsNew); - console.log(this.state); // eslint-disable-line - } - - initIncomeStatement = () => { - const statement = { - year: null, - revenue: null, - }; - statement['utility-expense'] = null; - statement['non-utility-operating-expense'] = null; - return statement; - } - - handleOnChange = (event) => { - const year = event.target.id; - const targetName = event.target.name; - const copyIS = this.state.incomeStatements; - Object.entries(copyIS.hist).forEach((incomeStatement) => { - if (incomeStatement[0] === year) { - copyIS.hist[year][targetName] = parseFloat(event.target.value); - } - }); - - this.setState({ incomeStatements: copyIS }, () => { - console.log(this.state.incomeStatements); // eslint-disable-line - }); - } - - handleOnChangeNew = (event) => { - const id = event.target.id; - const targetName = event.target.name.includes('Year') ? 'year' : event.target.name; - const targetValue = event.target.value; - const copyISNew = this.state.incomeStatementsNew.map((item, key) => { - if (parseInt(key, 10) === parseInt(id, 10)) { - const tmp = item; - tmp[targetName] = targetValue; - return tmp; - } - return item; - }); - this.setState({ incomeStatementsNew: copyISNew }, () => { - console.log(this.state.incomeStatementsNew); // eslint-disable-line - }); - } - - toggleGrowthRate() { - this.setState({ - GRDropdownOpen: !this.state.GRDropdownOpen, - }); - } - - changeGrowthRate(e) { - this.setState({ GRDropdownValue: e.currentTarget.textContent }); - this.setState({ selectedDropdownId: e.currentTarget.id }); - } - - buildHeader = () => { - const headers = []; - const year = 'year'; - headers.push( - - Year - - ); - - Object.entries(this.state).forEach((entry, key) => { - if (entry[0].includes('Year')) { - headers.push( - - - - ); - } - }); - const futureYear = this.state.incomeStatements.future.year; - headers.push( - - {futureYear} - - ); - const average = 'average'; - headers.push( - - Average - - ); - return headers; - } - - buildEmptyHeader = () => { - const headers = []; - const year = 'year'; - headers.push( - - Year - - ); - let id = 0; - Object.entries(this.state).forEach((entry, key) => { - if (entry[0].includes('Year')) { - headers.push( - - - - ); - id += 1; - } - }); - headers.push( - - {' '}Next Year{' '} - - ); - headers.push( - - {' '}Average{' '} - - ); - return headers; - } - - handleUpdateIncomeStatements = () => { - this.setState({ loading: true }); - const hist = []; - Object.entries(this.state.incomeStatements.hist).forEach(incomeStatement => { - const obj = { - year: String(incomeStatement[0]), - revenue: String(incomeStatement[1].revenue), - }; - obj['non-utility-operating-expense'] = String(incomeStatement[1].non_utility_expense); - obj['utility-expense'] = String(incomeStatement[1].utility_expense); - hist.push(obj); - }); - const growthRate = {}; - growthRate['growth-rate'] = String(this.state.selectedDropdownId); - hist.push(growthRate); - this.props.updateIncomeStatements( - this.props.buildingId, - hist, - ); - this.setState({ - loading: false, - action: 'updated', - }); - } - - handleCreateIncomeStatements = () => { - const emptyMessages = []; - const incomeStatementsNew = Object.values(this.state.incomeStatementsNew.slice(0, 3)) - .map(statement => { - if (statement.year === null && - !emptyMessages.includes('Year')) { - emptyMessages.push('Year'); - } - if (statement.revenue === null && - !emptyMessages.includes('Revenue')) { - emptyMessages.push('Revenue'); - } - if (statement['utility-expense'] === null && - !emptyMessages.includes('Utility Expense')) { - emptyMessages.push('Utility Expense'); - } - if (statement['non-utility-operating-expense'] === null && - !emptyMessages.includes('Non-Utility Operating Expense')) { - emptyMessages.push('Non-Utility Operating Expense'); - } - - const newStatement = {}; - newStatement.year = String(statement.year); - newStatement.revenue = String(statement.revenue); - newStatement['utility-expense'] = String(statement['utility-expense']); - newStatement['non-utility-operating-expense'] = String(statement['non-utility-operating-expense']); - return newStatement; - }); - - if (emptyMessages.length !== 0) { - const msg = `Please fill in ${emptyMessages.join(', ')} fields.`; - alert(msg); - } else { - const growthRate = {}; - growthRate['growth-rate'] = String(this.state.incomeStatementsNew[3]['growth-rate']); - incomeStatementsNew.push(growthRate); - this.setState({ loading: true }); - this.props.updateIncomeStatements( - this.props.buildingId, - incomeStatementsNew, - ); - this.setState({ - loading: false, - action: 'updated', - }); - } - } - - render() { - let header = []; - let rows = []; - let calculateSaveButton = null; - - const columnKeys = [ - 'revenue', - 'utility_expense', - 'energy_opex', - 'electric_opex', - 'gas_opex', - 'oil_opex', - 'water_opex', - 'other_utility', - 'non_utility_expense', - 'net_non_energy_opex', - 'total_opex', - 'noi', - ]; - - const columnNames = { - revenue: 'Revenue', - utility_expense: 'Utility Expense', - energy_opex: 'Energy Expense', - electric_opex: 'Electric Bill', - gas_opex: 'Gas Bill', - oil_opex: 'Oil Bill', - water_opex: 'Water Bill', - other_utility: 'Other Utility Expense', - non_utility_expense: 'Non-Utility Operating Expense', - net_non_energy_opex: 'Net Non-Energy Expense', - total_opex: 'Total Expense', - noi: 'Net Operating Income', - }; - - const growthRateOptions = this.state.growthRateOptions.map(e => { - return ( - - {e.name} - - ); - }); - - if (this.state.incomeStatements.hist !== null && - this.state.incomeStatements.future !== null) { - header = this.buildHeader(); - const histYears = Object.keys(this.state.incomeStatements.hist).sort(); - rows = columnKeys.map((columnKey, id) => { - const cells = [ - ...[columnNames[columnKey]], - ...histYears.map((histYear) => { - let cellValue = this.state.incomeStatements.hist[histYear][columnKey]; - cellValue = Math.round(cellValue * 100) / 100; - if (['utility_expense', 'revenue', 'non_utility_expense'].includes(columnKey)) { - return ( - - - $ - - - - ); - } - cellValue = cellValue.toLocaleString(); - cellValue = `$${cellValue}`; - return cellValue; - }), - ...[this.state.incomeStatements.future[columnKey]].map((amount) => { - let cellValue = amount; - cellValue = Math.round(cellValue * 100) / 100; - cellValue = cellValue.toLocaleString(); - cellValue = `$${cellValue}`; - return cellValue; - }), - ...Object.values([this.state.incomeStatements.average[columnKey]]).map((amount) => { - let cellValue = amount; - cellValue = Math.round(cellValue * 100) / 100; - cellValue = cellValue.toLocaleString(); - cellValue = `$${cellValue}`; - return cellValue; - }), - ]; - - const cellsData = Object.values(cells).map((celldata, key) => { - return ( - - - {celldata} - - - ); - }); - - return ( - - {cellsData} - - ); - }); - - calculateSaveButton = ( - - ); - } else { - header = this.buildEmptyHeader(); - const ids = Object.keys(this.state.incomeStatementsNew).slice(0, 3).sort(); - console.log(this.state.incomeStatementsNew); // eslint-disable-line - rows = columnKeys.map((columnKey, id) => { - const cells = [ - ...[columnNames[columnKey]], - ...ids.map((index) => { - const keyMapping = { - revenue: 'revenue', - utility_expense: 'utility-expense', - non_utility_expense: 'non-utility-operating-expense', - }; - - if (['revenue', 'utility_expense', 'non_utility_expense'].includes(columnKey)) { - const newColumnKey = keyMapping[columnKey]; - return ( - - - $ - - - - ); - } - return null; - }), - ...['', ''], - ]; - - const cellsData = Object.values(cells).map((celldata, key) => { - return ( - - - {celldata} - - - ); - }); - - return ( - - {cellsData} - - ); - }); - - calculateSaveButton = ( - - ); - } - - let messageStyle = {}; - let messageContent = null; - - if (this.state.loading) { - messageContent = 'Processing ...'; - messageStyle = this.props.defaultMessageStyle; - } - - if (this.state.error && typeof this.state.error === 'object') { - messageContent = this.state.error.response.statusText; - messageStyle = this.props.errorMessageStyle; - } - - if (!this.state.error && !this.state.loading - && this.state.incomeStatements !== null - && this.state.action !== null) { - messageContent = this.state.action.charAt(0).toUpperCase() + this.state.action.slice(1); - messageStyle = this.props.successMessageStyle; - } - - return ( -
    -

    - Income Statements (in $, End of Year) -

    -
    -
    - - - {this.state.GRDropdownValue} - - - {growthRateOptions} - - -
    -
    -    - {calculateSaveButton} -
    -
    -
    -
    - - - {header} - - - {rows} - -
    -
    -
    -
    - ); - } -} - -IncomeStatements.propTypes = { - blockStyle: PropTypes.shape({ - marginBottom: PropTypes.string, - }), - headerStyle: PropTypes.shape({ - textAlign: PropTypes.string, - marginBottom: PropTypes.string, - }), - buildingId: PropTypes.number, - data: PropTypes.shape({ - average: PropTypes.shape({ - electric_opex: PropTypes.number, - energy_opex: PropTypes.number, - gas_opex: PropTypes.number, - net_non_energy_opex: PropTypes.number, - noi: PropTypes.number, - non_utility_expense: PropTypes.number, - oil_opex: PropTypes.number, - other_utility: PropTypes.number, - revenue: PropTypes.number, - total_opex: PropTypes.number, - utility_expense: PropTypes.number, - water_opex: PropTypes.number, - year: null, - }), - cagr: PropTypes.number, - future: PropTypes.shape({ - electric_opex: PropTypes.number, - energy_opex: PropTypes.number, - gas_opex: PropTypes.number, - net_non_energy_opex: PropTypes.number, - noi: PropTypes.number, - non_utility_expense: PropTypes.number, - oil_opex: PropTypes.number, - other_utility: PropTypes.number, - revenue: PropTypes.number, - total_opex: PropTypes.number, - utility_expense: PropTypes.number, - water_opex: PropTypes.number, - year: null, - }), - growth_rate: PropTypes.number, - hist: PropTypes.objectOf, - result: PropTypes.arrayOf(PropTypes.shape({ - year: PropTypes.string, - revenue: PropTypes.string, - utility_expense: PropTypes.string, - non_utility_operating_expense: PropTypes.string, - })), - }), - successMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - errorMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - defaultMessageStyle: PropTypes.shape({ - color: PropTypes.string, - paddingLeft: PropTypes.string, - fontWeight: PropTypes.string, - }), - updateIncomeStatements: PropTypes.func, -}; - -export default IncomeStatements; diff --git a/src/containers/Blocnote/FinancialInputs/index.js b/src/containers/Blocnote/FinancialInputs/index.js index 398817c2d1fb860dda60538a64ae43bf48ac1fc0..2d3005e6d769696bd943ce087c5e103bc11ec089 100644 --- a/src/containers/Blocnote/FinancialInputs/index.js +++ b/src/containers/Blocnote/FinancialInputs/index.js @@ -90,22 +90,7 @@ class FinancialInputs extends Component { marginBottom: '25px', paddingLeft: '10px', }; - const successMessageStyle = { - color: 'green', - paddingLeft: '25px', - fontWeight: 'bold', - }; - const errorMessageStyle = { - color: 'red', - paddingLeft: '25px', - fontWeight: 'bold', - }; - const defaultMessageStyle = { - color: 'black', - paddingLeft: '25px', - fontWeight: 'bold', - }; - + console.log(this.props); // eslint-disable-line const { blocnote } = this.props; const { fianceOverview, bills, billsOverview, billsSummary, cashBalance, @@ -124,12 +109,11 @@ class FinancialInputs extends Component { liabilities.data !== null && customerPreference.data !== null) { const foData = this.processFinanceOverview(fianceOverview.data); - const financeOverviewExist = fianceOverview.data.instance.financing_overview_data !== undefined; let cashBalanceData = []; let loanOptionsData = []; - let billsSummaryData = { + const billsSummaryData = { gas: [], oil: [], water: [], @@ -153,7 +137,9 @@ class FinancialInputs extends Component { } }); } else { - billsSummaryData = billsSummary.data.result.user_bill; + Object.keys(billsSummary.data.result.user_bill).forEach((billName) => { + billsSummaryData[billName] = billsSummary.data.result.user_bill[billName]; + }); } if (Object.keys(customerPreference.data).length !== 0) { customerPreferenceData = customerPreference.data.instance; @@ -164,8 +150,6 @@ class FinancialInputs extends Component { { + userBill[billName] = billData; + if (billName === action.instance.utility_type) { + const bill = userBill[billName].pop(); + bill.id = action.result.id; + bill.year = action.instance.year; + bill.charge = action.instance.charge; + userBill[billName].push(bill); + } + }); return { ...state, billsSummary: { data: { - result: - [ - ...state.billsSummary.data, - { - id: action.result.id, - utility_type: action.instance.utility_type, - year: action.instance.year, - charge: action.instance.charge, - }, - ], - + result: { + user_bill: userBill, + }, }, loading: false, error: false, }, }; + } case FinancialInputs.CREATE_BILLS_SUMMARY_FAILED: return { @@ -288,15 +292,24 @@ export default function (state = blocnoteInitialState, action) { }, }; - case FinancialInputs.DELETE_BILLS_SUMMARY_SUCCEEDED: + case FinancialInputs.DELETE_BILLS_SUMMARY_SUCCEEDED: { + const userBill = {}; + Object.entries(state.billsSummary.data.result.user_bill).forEach(([billName, billData]) => { + userBill[billName] = billData.filter(bill => bill.id !== action.instance.id); + }); return { ...state, billsSummary: { - ...state.billsSummary, + data: { + result: { + user_bill: userBill, + }, + }, loading: false, error: false, }, }; + } case FinancialInputs.DELETE_BILLS_SUMMARY_FAILED: return { @@ -491,16 +504,18 @@ export default function (state = blocnoteInitialState, action) { }, }; - case FinancialInputs.UPDATE_BILLS_OVERVIEW_SUCCEEDED: + case FinancialInputs.UPDATE_BILLS_OVERVIEW_SUCCEEDED: { return { ...state, billsOverview: { - ...state.billsOverview, + data: { + instance: action.instance, + }, loading: false, error: false, }, }; - + } case FinancialInputs.UPDATE_BILLS_OVERVIEW_FAILED: return { ...state, billsOverview: { @@ -520,16 +535,25 @@ export default function (state = blocnoteInitialState, action) { }, }; - case FinancialInputs.UPDATE_INCOME_STATEMENTS_SUCCEEDED: + case FinancialInputs.UPDATE_INCOME_STATEMENTS_SUCCEEDED: { + const dataCopy = { + cagr: action.instance.CAGR, + average: action.instance.average, + future: action.instance.future, + hist: action.instance.instance, + growth_rate: Number.parseFloat(action.instance.growth_rate) / 100, + }; return { ...state, incomeStatement: { - data: { instance: action.instance }, + data: { + instance: dataCopy, + }, loading: false, error: false, }, }; - + } case FinancialInputs.UPDATE_INCOME_STATEMENTS_FAILED: return { ...state, incomeStatement: { @@ -553,7 +577,11 @@ export default function (state = blocnoteInitialState, action) { return { ...state, cashBalance: { - data: { instance: action.instance }, + data: { + instance: { + result: action.instance, + }, + }, loading: false, error: false, }, @@ -607,16 +635,24 @@ export default function (state = blocnoteInitialState, action) { }, }; - case FinancialInputs.UPDATE_LOAN_OPTIONS_SUCCEEDED: + case FinancialInputs.UPDATE_LOAN_OPTIONS_SUCCEEDED: { + const finalData = {}; + const instance = action.instance; + finalData.load = true; + finalData.present = true; + finalData.lenders = instance.lenders; + finalData.cash = instance['cash-dscr']; + finalData.noi = instance['noi-dscr']; + finalData.status = instance.instance; return { ...state, loanOptions: { - data: { instance: action.instance }, + data: finalData, loading: false, error: false, }, }; - + } case FinancialInputs.UPDATE_LOAN_OPTIONS_FAILED: return { ...state, loanOptions: { diff --git a/src/containers/Blocnote/sagas.js b/src/containers/Blocnote/sagas.js index 59a9aacb507e874919d97978ae1909353867eaa7..7cdd5b7db56061a7af7a53291bad0889f3c892cc 100644 --- a/src/containers/Blocnote/sagas.js +++ b/src/containers/Blocnote/sagas.js @@ -196,7 +196,7 @@ function* updateBillsOverview(action) { ); if (!res.err) { - yield put(updateBillsOverviewSucceeded(action.payload)); + yield put(updateBillsOverviewSucceeded(res.instance)); } else { yield put(updateBillsOverviewFailed(res.err)); } @@ -215,7 +215,8 @@ function* updateIncomeStatements(action) { ); if (!res.err) { - yield put(updateIncomeStatementsSucceeded(action.payload)); + res.growth_rate = action.payload.slice(-1)[0]['growth-rate']; + yield put(updateIncomeStatementsSucceeded(res)); } else { yield put(updateIncomeStatementsFailed(res.err)); } @@ -315,7 +316,7 @@ function* updateScenario(action) { ); if (!res.err) { - yield put(updateScenarioSucceeded(action.payload)); + yield put(updateScenarioSucceeded(res)); } else { yield put(updateScenarioFailed(res.err)); } diff --git a/src/containers/Performance/actions.js b/src/containers/Performance/actions.js new file mode 100644 index 0000000000000000000000000000000000000000..5098bf13e17d6b765c288301725609de5ee25e6f --- /dev/null +++ b/src/containers/Performance/actions.js @@ -0,0 +1,94 @@ +import { + OCCUPANTS_REQUESTED, + OCCUPANTS_SUCCEEDED, + OCCUPANTS_FAILED, + OCCUPANTS_LOG_REQUESTED, + OCCUPANTS_LOG_SUCCEEDED, + OCCUPANTS_LOG_FAILED, + THERMAL_COMFORT_REQUESTED, + THERMAL_COMFORT_SUCCEEDED, + THERMAL_COMFORT_FAILED, + THERMAL_COMFORT_LOG_REQUESTED, + THERMAL_COMFORT_LOG_SUCCEEDED, + THERMAL_COMFORT_LOG_FAILED, + RETROFITS_REQUESTED, + RETROFITS_SUCCEEDED, + RETROFITS_FAILED, + RETROFITS_LOG_REQUESTED, + RETROFITS_LOG_SUCCEEDED, + RETROFITS_LOG_FAILED, + RETROFIT_META_REQUESTED, + RETROFIT_META_SUCCEEDED, + RETROFIT_META_FAILED, + UPDATE_OCCUPANTS_REQUESTED, + UPDATE_OCCUPANTS_SUCCEEDED, + UPDATE_OCCUPANTS_FAILED, + UPDATE_THERMAL_COMFORT_REQUESTED, + UPDATE_THERMAL_COMFORT_SUCCEEDED, + UPDATE_THERMAL_COMFORT_FAILED, + UPDATE_MYRETROFIT_REQUESTED, + UPDATE_MYRETROFIT_SUCCEEDED, + UPDATE_MYRETROFIT_FAILED, + CREATE_OCCUPANTS_LOG_REQUESTED, + CREATE_OCCUPANTS_LOG_SUCCEEDED, + CREATE_OCCUPANTS_LOG_FAILED, + CREATE_THERMAL_COMFORT_LOG_REQUESTED, + CREATE_THERMAL_COMFORT_LOG_SUCCEEDED, + CREATE_THERMAL_COMFORT_LOG_FAILED, + CREATE_MYRETROFIT_REQUESTED, + CREATE_MYRETROFIT_SUCCEEDED, + CREATE_MYRETROFIT_FAILED, + DELETE_MYRETROFIT_REQUESTED, + DELETE_MYRETROFIT_SUCCEEDED, + DELETE_MYRETROFIT_FAILED, +} from './constants'; +import { makeActionCreator } from '../../utils/reduxHelpers'; + +export const loadOccupants = makeActionCreator(OCCUPANTS_REQUESTED, 'buildingId'); +export const occupantsLoaded = makeActionCreator(OCCUPANTS_SUCCEEDED, 'instance'); +export const occupantsFailed = makeActionCreator(OCCUPANTS_FAILED, 'error'); +export const loadOccupantsLog = makeActionCreator(OCCUPANTS_LOG_REQUESTED, 'buildingId'); +export const occupantsLogLoaded = makeActionCreator(OCCUPANTS_LOG_SUCCEEDED, 'instance'); +export const occupantsLogFailed = makeActionCreator(OCCUPANTS_LOG_FAILED, 'error'); + +export const loadThermalComfort = makeActionCreator(THERMAL_COMFORT_REQUESTED, 'buildingId'); +export const thermalComfortLoaded = makeActionCreator(THERMAL_COMFORT_SUCCEEDED, 'instance'); +export const thermalComfortFailed = makeActionCreator(THERMAL_COMFORT_FAILED, 'error'); +export const loadThermalComfortLog = makeActionCreator(THERMAL_COMFORT_LOG_REQUESTED, 'buildingId'); +export const thermalComfortLogLoaded = makeActionCreator(THERMAL_COMFORT_LOG_SUCCEEDED, 'instance'); +export const thermalComfortLogFailed = makeActionCreator(THERMAL_COMFORT_LOG_FAILED, 'error'); + +export const loadRetrofits = makeActionCreator(RETROFITS_REQUESTED, 'buildingId'); +export const retrofitsLoaded = makeActionCreator(RETROFITS_SUCCEEDED, 'instance'); +export const retrofitsFailed = makeActionCreator(RETROFITS_FAILED, 'error'); +export const loadRetrofitsLog = makeActionCreator(RETROFITS_LOG_REQUESTED, 'buildingId'); +export const retrofitsLogLoaded = makeActionCreator(RETROFITS_LOG_SUCCEEDED, 'instance'); +export const retrofitsLogFailed = makeActionCreator(RETROFITS_LOG_FAILED, 'error'); +export const loadRetrofitMeta = makeActionCreator(RETROFIT_META_REQUESTED, 'buildingId'); +export const retrofitMetaLoaded = makeActionCreator(RETROFIT_META_SUCCEEDED, 'instance'); +export const retrofitMetaFailed = makeActionCreator(RETROFIT_META_FAILED, 'error'); + +export const updateOccupants = makeActionCreator(UPDATE_OCCUPANTS_REQUESTED, 'buildingId', 'payload'); +export const updateOccupantsSucceeded = makeActionCreator(UPDATE_OCCUPANTS_SUCCEEDED, 'payload', 'instance'); +export const updateOccupantsFailed = makeActionCreator(UPDATE_OCCUPANTS_FAILED, 'error'); +export const updateThermalComfort = makeActionCreator(UPDATE_THERMAL_COMFORT_REQUESTED, 'buildingId', 'payload'); +export const updateThermalComfortSucceeded = makeActionCreator(UPDATE_THERMAL_COMFORT_SUCCEEDED, 'instance'); +export const updateThermalComfortFailed = makeActionCreator(UPDATE_THERMAL_COMFORT_FAILED, 'error'); + +export const updateMyRetrofit = makeActionCreator(UPDATE_MYRETROFIT_REQUESTED, 'buildingId', 'payload'); +export const updateMyRetrofitSucceeded = makeActionCreator(UPDATE_MYRETROFIT_SUCCEEDED, 'payload', 'instance'); +export const updateMyRetrofitFailed = makeActionCreator(UPDATE_MYRETROFIT_FAILED, 'error'); + +export const createOccupantsLog = makeActionCreator(CREATE_OCCUPANTS_LOG_REQUESTED, 'buildingId', 'payload'); +export const createOccupantsLogSucceeded = makeActionCreator(CREATE_OCCUPANTS_LOG_SUCCEEDED, 'instance'); +export const createOccupantsLogFailed = makeActionCreator(CREATE_OCCUPANTS_LOG_FAILED, 'error'); +export const createThermalComfortLog = makeActionCreator(CREATE_THERMAL_COMFORT_LOG_REQUESTED, 'buildingId', 'payload'); +export const createThermalComfortLogSucceeded = makeActionCreator(CREATE_THERMAL_COMFORT_LOG_SUCCEEDED, 'instance'); +export const createThermalComfortLogFailed = makeActionCreator(CREATE_THERMAL_COMFORT_LOG_FAILED, 'error'); +export const createMyRetrofit = makeActionCreator(CREATE_MYRETROFIT_REQUESTED, 'buildingId', 'payload'); +export const createMyRetrofitSucceeded = makeActionCreator(CREATE_MYRETROFIT_SUCCEEDED, 'instance'); +export const createMyRetrofitFailed = makeActionCreator(CREATE_MYRETROFIT_FAILED, 'error'); + +export const deleteMyRetrofit = makeActionCreator(DELETE_MYRETROFIT_REQUESTED, 'buildingId', 'payload'); +export const deleteMyRetrofitSucceeded = makeActionCreator(DELETE_MYRETROFIT_SUCCEEDED, 'instance'); +export const deleteMyRetrofitFailed = makeActionCreator(DELETE_MYRETROFIT_FAILED, 'error'); diff --git a/src/containers/Performance/constants.js b/src/containers/Performance/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..8ec48d2d019037541eb408c8863b0eef45e44523 --- /dev/null +++ b/src/containers/Performance/constants.js @@ -0,0 +1,47 @@ +export const OCCUPANTS_REQUESTED = 'OCCUPANTS_REQUESTED'; +export const OCCUPANTS_SUCCEEDED = 'OCCUPANTS_SUCCEEDED'; +export const OCCUPANTS_FAILED = 'OCCUPANTS_FAILED'; +export const OCCUPANTS_LOG_REQUESTED = 'OCCUPANTS_LOG_REQUESTED'; +export const OCCUPANTS_LOG_SUCCEEDED = 'OCCUPANTS_LOG_SUCCEEDED'; +export const OCCUPANTS_LOG_FAILED = 'OCCUPANTS_LOG_FAILED'; + +export const THERMAL_COMFORT_REQUESTED = 'THERMAL_COMFORT_REQUESTED'; +export const THERMAL_COMFORT_SUCCEEDED = 'THERMAL_COMFORT_SUCCEEDED'; +export const THERMAL_COMFORT_FAILED = 'THERMAL_COMFORT_FAILED'; +export const THERMAL_COMFORT_LOG_REQUESTED = 'THERMAL_COMFORT_LOG_REQUESTED'; +export const THERMAL_COMFORT_LOG_SUCCEEDED = 'THERMAL_COMFORT_LOG_SUCCEEDED'; +export const THERMAL_COMFORT_LOG_FAILED = 'THERMAL_COMFORT_LOG_FAILED'; + +export const RETROFITS_REQUESTED = 'RETROFITS_REQUESTED'; +export const RETROFITS_SUCCEEDED = 'RETROFITS_SUCCEEDED'; +export const RETROFITS_FAILED = 'RETROFITS_FAILED'; +export const RETROFITS_LOG_REQUESTED = 'RETROFITS_LOG_REQUESTED'; +export const RETROFITS_LOG_SUCCEEDED = 'RETROFITS_LOG_SUCCEEDED'; +export const RETROFITS_LOG_FAILED = 'RETROFITS_LOG_FAILED'; +export const RETROFIT_META_REQUESTED = 'RETROFIT_META_REQUESTED'; +export const RETROFIT_META_SUCCEEDED = 'RETROFIT_META_SUCCEEDED'; +export const RETROFIT_META_FAILED = 'RETROFIT_META_FAILED'; + +export const UPDATE_OCCUPANTS_REQUESTED = 'UPDATE_OCCUPANTS_REQUESTED'; +export const UPDATE_OCCUPANTS_SUCCEEDED = 'UPDATE_OCCUPANTS_SUCCEEDED'; +export const UPDATE_OCCUPANTS_FAILED = 'UPDATE_OCCUPANTS_FAILED'; +export const UPDATE_THERMAL_COMFORT_REQUESTED = 'UPDATE_THERMAL_COMFORT_REQUESTED'; +export const UPDATE_THERMAL_COMFORT_SUCCEEDED = 'UPDATE_THERMAL_COMFORT_SUCCEEDED'; +export const UPDATE_THERMAL_COMFORT_FAILED = 'UPDATE_THERMAL_COMFORT_FAILED'; +export const UPDATE_MYRETROFIT_REQUESTED = 'UPDATE_MYRETROFIT_REQUESTED'; +export const UPDATE_MYRETROFIT_SUCCEEDED = 'UPDATE_MYRETROFIT_SUCCEEDED'; +export const UPDATE_MYRETROFIT_FAILED = 'UPDATE_MYRETROFIT_FAILED'; + +export const CREATE_OCCUPANTS_LOG_REQUESTED = 'CREATE_OCCUPANTS_LOG_REQUESTED'; +export const CREATE_OCCUPANTS_LOG_SUCCEEDED = 'CREATE_OCCUPANTS_LOG_SUCCEEDED'; +export const CREATE_OCCUPANTS_LOG_FAILED = 'CREATE_OCCUPANTS_LOG_FAILED'; +export const CREATE_THERMAL_COMFORT_LOG_REQUESTED = 'CREATE_THERMAL_COMFORT_LOG_REQUESTED'; +export const CREATE_THERMAL_COMFORT_LOG_SUCCEEDED = 'CREATE_THERMAL_COMFORT_LOG_SUCCEEDED'; +export const CREATE_THERMAL_COMFORT_LOG_FAILED = 'CREATE_THERMAL_COMFORT_LOG_FAILED'; +export const CREATE_MYRETROFIT_REQUESTED = 'CREATE_MYRETROFIT_REQUESTED'; +export const CREATE_MYRETROFIT_SUCCEEDED = 'CREATE_MYRETROFIT_SUCCEEDED'; +export const CREATE_MYRETROFIT_FAILED = 'CREATE_MYRETROFIT_FAILED'; + +export const DELETE_MYRETROFIT_REQUESTED = 'DELETE_MYRETROFIT_REQUESTED'; +export const DELETE_MYRETROFIT_SUCCEEDED = 'DELETE_MYRETROFIT_SUCCEEDED'; +export const DELETE_MYRETROFIT_FAILED = 'DELETE_MYRETROFIT_FAILED'; diff --git a/src/containers/Performance/index.js b/src/containers/Performance/index.js new file mode 100644 index 0000000000000000000000000000000000000000..40b4a6ca52ff3dcd30aa2546b8e8775df171d87f --- /dev/null +++ b/src/containers/Performance/index.js @@ -0,0 +1,221 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import Loading from '../../components/Loading'; +import { + loadOccupants, + loadOccupantsLog, + loadThermalComfort, + loadThermalComfortLog, + loadRetrofits, + loadRetrofitsLog, + loadRetrofitMeta, + updateOccupants, + updateThermalComfort, + createMyRetrofit, + updateMyRetrofit, + deleteMyRetrofit, +} from './actions'; +import buildingDetailPropTypes from '../../containers/Building/propTypes'; +import LinkBarDetail from '../../components/LinkBarDetail'; +import Occupants from '../../components/Performance/Occupants'; +import ThermalComfort from '../../components/Performance/ThermalComfort'; +import Retrofits from '../../components/Performance/Retrofits'; + + +class Performance extends Component { + constructor(props) { + super(props); + this.state = { + weather: 'jfk', + }; + } + + componentDidMount() { + const buildingId = this.props.building.building_id; + console.log(buildingId); // eslint-disable-line + this.props.loadOccupants(buildingId); + this.props.loadOccupantsLog(buildingId); + this.props.loadThermalComfort(buildingId); + this.props.loadThermalComfortLog(buildingId); + this.props.loadRetrofits(buildingId); + this.props.loadRetrofitsLog(buildingId); + this.props.loadRetrofitMeta(buildingId); + } + + render() { + let mainContent = null; + + const blockStyle = { marginBottom: '40px', marginTop: '40px' }; + const headerStyle = { + textAlign: 'left', + marginBottom: '25px', + paddingLeft: '10px', + }; + + console.log(this.props); // eslint-disable-line + + const { + user, + performance, + } = this.props; + const { + occupants, + thermalComfort, + occupantsLog, + thermalComfortLog, + retrofits, + retrofitsLog, + retrofitMeta, + } = performance; + + mainContent = ; + + if (user !== undefined && user.user_id !== undefined && + occupants.data !== undefined && thermalComfort.data !== undefined && + occupants.data !== null && thermalComfort.data !== null && + occupants.data.occupants !== undefined && thermalComfort.data.thermalComfort !== undefined && + occupantsLog.data !== null && thermalComfortLog.data !== null && + occupantsLog.data.data !== undefined && thermalComfortLog.data.data !== undefined && + retrofits.data !== null && retrofitsLog.data !== null && retrofitMeta.data !== null + ) { + + const userId = user.user_id.split('|')[1]; + + let occupantsData = {}; + if (occupants.data.occupants === null) { + occupantsData = { + total_units: 0, + pre_vacant_units: 0, + updateOccupants: 0, + }; + } else { + occupantsData = occupants.data.occupants; + } + + let thermalComfortData = {}; + if (thermalComfort.data.thermalComfort === null) { + thermalComfortData = { + pre_heating_set_point: 0, + post_heating_set_point: 0, + pre_cooling_set_point: 0, + post_cooling_set_point: 0, + }; + } else { + thermalComfortData = thermalComfort.data.thermalComfort; + } + + const retrofitsData = retrofits.data.retrofits === null ? [] : retrofits.data.retrofits; + + mainContent = ( +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + ); + } + + return ( +
    + + {mainContent} +
    + ); + } +} + +Performance.propTypes = { + building: buildingDetailPropTypes, + loadOccupants: PropTypes.func, + loadOccupantsLog: PropTypes.func, + loadThermalComfort: PropTypes.func, + loadThermalComfortLog: PropTypes.func, + loadRetrofits: PropTypes.func, + loadRetrofitsLog: PropTypes.func, + loadRetrofitMeta: PropTypes.func, + updateOccupants: PropTypes.func, + updateThermalComfort: PropTypes.func, + createMyRetrofit: PropTypes.func, + updateMyRetrofit: PropTypes.func, + deleteMyRetrofit: PropTypes.func, + user: PropTypes.objectOf, + performance: PropTypes.objectOf, +}; + +const mapDispatchToProps = dispatch => ( + bindActionCreators({ + loadOccupants, + loadOccupantsLog, + loadThermalComfort, + loadThermalComfortLog, + loadRetrofits, + loadRetrofitsLog, + loadRetrofitMeta, + updateOccupants, + updateThermalComfort, + createMyRetrofit, + updateMyRetrofit, + deleteMyRetrofit, + }, dispatch) +); + +const mapStateToProps = state => ({ + performance: state.bloclink, + user: state.user, +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Performance); diff --git a/src/containers/Performance/reducer.js b/src/containers/Performance/reducer.js new file mode 100644 index 0000000000000000000000000000000000000000..3b3c1ef4cb632964166725b47902e5dff0856546 --- /dev/null +++ b/src/containers/Performance/reducer.js @@ -0,0 +1,407 @@ +import { + OCCUPANTS_REQUESTED, + OCCUPANTS_SUCCEEDED, + OCCUPANTS_FAILED, + OCCUPANTS_LOG_REQUESTED, + OCCUPANTS_LOG_SUCCEEDED, + OCCUPANTS_LOG_FAILED, + THERMAL_COMFORT_REQUESTED, + THERMAL_COMFORT_SUCCEEDED, + THERMAL_COMFORT_FAILED, + THERMAL_COMFORT_LOG_REQUESTED, + THERMAL_COMFORT_LOG_SUCCEEDED, + THERMAL_COMFORT_LOG_FAILED, + RETROFITS_REQUESTED, + RETROFITS_SUCCEEDED, + RETROFITS_FAILED, + RETROFITS_LOG_REQUESTED, + RETROFITS_LOG_SUCCEEDED, + RETROFITS_LOG_FAILED, + RETROFIT_META_REQUESTED, + RETROFIT_META_SUCCEEDED, + RETROFIT_META_FAILED, + UPDATE_OCCUPANTS_REQUESTED, + UPDATE_OCCUPANTS_SUCCEEDED, + UPDATE_OCCUPANTS_FAILED, + UPDATE_MYRETROFIT_REQUESTED, + UPDATE_MYRETROFIT_SUCCEEDED, + UPDATE_MYRETROFIT_FAILED, + CREATE_MYRETROFIT_REQUESTED, + CREATE_MYRETROFIT_SUCCEEDED, + CREATE_MYRETROFIT_FAILED, + DELETE_MYRETROFIT_REQUESTED, + DELETE_MYRETROFIT_SUCCEEDED, + DELETE_MYRETROFIT_FAILED, +} from './constants'; + +const PerformanceInitialState = { + occupants: { + loading: false, + error: false, + data: null, + }, + occupantsLog: { + loading: false, + error: false, + data: null, + }, + thermalComfort: { + loading: false, + error: false, + data: null, + }, + thermalComfortLog: { + loading: false, + error: false, + data: null, + }, + retrofits: { + loading: false, + error: false, + data: [], + }, + retrofitsLog: { + loading: false, + error: false, + data: null, + }, + retrofitMeta: { + loading: false, + error: false, + data: null, + }, +}; + +export default function (state = PerformanceInitialState, action) { + switch (action.type) { + case OCCUPANTS_REQUESTED: + return { + ...state, + occupants: { + ...state.occupants, + loading: true, + error: false, + }, + }; + + case OCCUPANTS_SUCCEEDED: + return { + ...state, + occupants: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case OCCUPANTS_FAILED: + return { + ...state, + occupants: { + loading: false, + error: action.error, + }, + }; + + case OCCUPANTS_LOG_REQUESTED: + return { + ...state, + occupantsLog: { + ...state.occupantsLog, + loading: true, + error: false, + }, + }; + + case OCCUPANTS_LOG_SUCCEEDED: + return { + ...state, + occupantsLog: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case OCCUPANTS_LOG_FAILED: + return { + ...state, + occupantsLog: { + loading: false, + error: action.error, + }, + }; + + case THERMAL_COMFORT_REQUESTED: + return { + ...state, + thermalComfort: { + ...state.thermalComfort, + loading: true, + error: false, + }, + }; + + case THERMAL_COMFORT_SUCCEEDED: + return { + ...state, + thermalComfort: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case THERMAL_COMFORT_FAILED: + return { + ...state, + thermalComfort: { + loading: false, + error: action.error, + }, + }; + + case THERMAL_COMFORT_LOG_REQUESTED: + return { + ...state, + thermalComfortLog: { + ...state.thermalComfortLog, + loading: true, + error: false, + }, + }; + + case THERMAL_COMFORT_LOG_SUCCEEDED: + return { + ...state, + thermalComfortLog: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case THERMAL_COMFORT_LOG_FAILED: + return { + ...state, + thermalComfortLog: { + loading: false, + error: action.error, + }, + }; + + case RETROFITS_REQUESTED: + return { + ...state, + retrofits: { + ...state.retrofits, + loading: true, + error: false, + }, + }; + + case RETROFITS_SUCCEEDED: + return { + ...state, + retrofits: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case RETROFITS_FAILED: + return { + ...state, + retrofits: { + loading: false, + error: action.error, + }, + }; + + case RETROFITS_LOG_REQUESTED: + return { + ...state, + retrofitsLog: { + ...state.retrofitsLog, + loading: true, + error: false, + }, + }; + + case RETROFITS_LOG_SUCCEEDED: + return { + ...state, + retrofitsLog: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case RETROFITS_LOG_FAILED: + return { + ...state, + retrofitsLog: { + loading: false, + error: action.error, + }, + }; + + case RETROFIT_META_REQUESTED: + return { + ...state, + retrofitMeta: { + ...state.retrofitMeta, + loading: true, + error: false, + }, + }; + + case RETROFIT_META_SUCCEEDED: + return { + ...state, + retrofitMeta: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case RETROFIT_META_FAILED: + return { + ...state, + retrofitMeta: { + loading: false, + error: action.error, + }, + }; + + case UPDATE_OCCUPANTS_REQUESTED: + return { + ...state, + occupants: { + ...state.occupants, + loading: true, + error: false, + }, + }; + + case UPDATE_OCCUPANTS_SUCCEEDED: + return { + ...state, + occupants: { + ...state.occupants, + loading: false, + error: false, + }, + }; + + case UPDATE_OCCUPANTS_FAILED: + return { + ...state, + occupants: { + loading: false, + error: action.error, + }, + }; + + case CREATE_MYRETROFIT_REQUESTED: + return { + ...state, + retrofits: { + ...state.retrofits, + loading: true, + error: false, + }, + }; + + case CREATE_MYRETROFIT_SUCCEEDED: { + const retrofits = state.retrofits.data.retrofits; + retrofits.splice(-1, 1); + retrofits.push(action.instance.instance); + return { + ...state, + retrofits: { + data: { + retrofits, + }, + loading: false, + error: false, + }, + }; + } + case CREATE_MYRETROFIT_FAILED: + return { + ...state, + retrofits: { + loading: false, + error: action.error, + }, + }; + + case UPDATE_MYRETROFIT_REQUESTED: + return { + ...state, + retrofits: { + ...state.retrofits, + loading: true, + error: false, + }, + }; + + case UPDATE_MYRETROFIT_SUCCEEDED: + return { + ...state, + retrofits: { + ...state.retrofits, + loading: false, + error: false, + }, + }; + + case UPDATE_MYRETROFIT_FAILED: + return { + ...state, + retrofits: { + loading: false, + error: action.error, + }, + }; + + case DELETE_MYRETROFIT_REQUESTED: + return { + ...state, + retrofits: { + ...state.retrofits, + loading: true, + error: false, + }, + }; + + case DELETE_MYRETROFIT_SUCCEEDED: { + return { + ...state, + retrofits: { + data: { + retrofits: + state.retrofits.data.retrofits.filter(retrofit => retrofit.id !== action.instance.id), + }, + loading: false, + error: false, + }, + }; + } + case DELETE_MYRETROFIT_FAILED: + return { + ...state, + retrofits: { + loading: false, + error: action.error, + }, + }; + + default: + return state; + } +} diff --git a/src/containers/Performance/sagas.js b/src/containers/Performance/sagas.js new file mode 100644 index 0000000000000000000000000000000000000000..21c847766d0c1d0be007baeade214dd463dd87a0 --- /dev/null +++ b/src/containers/Performance/sagas.js @@ -0,0 +1,226 @@ +import { call, put, takeEvery } from 'redux-saga/effects'; +import SagaRequests from '../../utils/sagaRequests'; +import request from '../../utils/request'; +import { getHeaders } from '../../utils/restServices'; +import { + OCCUPANTS_REQUESTED, + OCCUPANTS_LOG_REQUESTED, + THERMAL_COMFORT_REQUESTED, + THERMAL_COMFORT_LOG_REQUESTED, + RETROFITS_REQUESTED, + RETROFITS_LOG_REQUESTED, + RETROFIT_META_REQUESTED, + UPDATE_OCCUPANTS_REQUESTED, + UPDATE_THERMAL_COMFORT_REQUESTED, + CREATE_MYRETROFIT_REQUESTED, + UPDATE_MYRETROFIT_REQUESTED, + DELETE_MYRETROFIT_REQUESTED, + CREATE_OCCUPANTS_LOG_REQUESTED, + CREATE_THERMAL_COMFORT_LOG_REQUESTED, +} from './constants'; + +import { + occupantsLoaded, occupantsFailed, + occupantsLogLoaded, occupantsLogFailed, + thermalComfortLoaded, thermalComfortFailed, + thermalComfortLogLoaded, thermalComfortLogFailed, + retrofitsLoaded, retrofitsFailed, + retrofitsLogLoaded, retrofitsLogFailed, + retrofitMetaLoaded, retrofitMetaFailed, + updateOccupantsSucceeded, updateOccupantsFailed, + updateThermalComfortSucceeded, updateThermalComfortFailed, + updateMyRetrofitSucceeded, updateMyRetrofitFailed, + createMyRetrofitSucceeded, createMyRetrofitFailed, + createOccupantsLogSucceeded, createOccupantsLogFailed, + createThermalComfortLogSucceeded, createThermalComfortLogFailed, + deleteMyRetrofitSucceeded, deleteMyRetrofitFailed, +} from './actions'; + +function* loadOccupants(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/occupants`; + yield SagaRequests.get(action, url, occupantsLoaded, occupantsFailed); +} + +function* loadOccupantsLog(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/occupants-log`; + yield SagaRequests.get(action, url, occupantsLogLoaded, occupantsLogFailed); +} + +function* loadThermalComfort(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/thermal-comfort/`; + yield SagaRequests.get(action, url, thermalComfortLoaded, thermalComfortFailed); +} + +function* loadThermalComfortLog(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/thermal-comfort-log/`; + yield SagaRequests.get(action, url, thermalComfortLogLoaded, thermalComfortLogFailed); +} + +function* loadRetrofits(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/retrofits/`; + yield SagaRequests.get(action, url, retrofitsLoaded, retrofitsFailed); +} + +function* loadRetrofitsLog(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/retrofits-log/`; + yield SagaRequests.get(action, url, retrofitsLogLoaded, retrofitsLogFailed); +} + +function* loadRetrofitMeta(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/retrofit-meta/`; + yield SagaRequests.get(action, url, retrofitMetaLoaded, retrofitMetaFailed); +} + +function* updateOccupants(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/occupants/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'PUT', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + yield put(updateOccupantsSucceeded(action.payload)); + } else { + yield put(updateOccupantsFailed(res.err)); + } +} + +function* updateThermalComfort(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/thermal-comfort/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'PUT', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + yield put(updateThermalComfortSucceeded(action.payload)); + } else { + yield put(updateThermalComfortFailed(res.err)); + } +} + +function* createMyRetrofit(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/retrofits/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + console.log(res); // eslint-disable-line + yield put(createMyRetrofitSucceeded(res)); + } else { + yield put(createMyRetrofitFailed(res.err)); + } +} + +function* updateMyRetrofit(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/retrofits/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'PUT', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + console.log(res); // eslint-disable-line + yield put(updateMyRetrofitSucceeded(res)); + } else { + yield put(updateMyRetrofitFailed(res.err)); + } +} + +function* deleteMyRetrofit(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/retrofits/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'DELETE', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + yield put(deleteMyRetrofitSucceeded(action.payload)); + } else { + yield put(deleteMyRetrofitFailed(res.err)); + } +} + +function* createOccupantsLog(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/occupants-log/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + yield put(createOccupantsLogSucceeded(action.payload, res)); + } else { + yield put(createOccupantsLogFailed(res.err)); + } +} + +function* createThermalComfortLog(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/performance/thermal-comfort-log/`; + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(action.payload), + } + ); + + if (!res.err) { + yield put(createThermalComfortLogSucceeded(action.payload, res)); + } else { + yield put(createThermalComfortLogFailed(res.err)); + } +} + +function* bloclinkWatcher() { + yield takeEvery(OCCUPANTS_REQUESTED, loadOccupants); + yield takeEvery(OCCUPANTS_LOG_REQUESTED, loadOccupantsLog); + yield takeEvery(THERMAL_COMFORT_REQUESTED, loadThermalComfort); + yield takeEvery(THERMAL_COMFORT_LOG_REQUESTED, loadThermalComfortLog); + yield takeEvery(RETROFITS_REQUESTED, loadRetrofits); + yield takeEvery(RETROFITS_LOG_REQUESTED, loadRetrofitsLog); + yield takeEvery(RETROFIT_META_REQUESTED, loadRetrofitMeta); + yield takeEvery(UPDATE_OCCUPANTS_REQUESTED, updateOccupants); + yield takeEvery(UPDATE_THERMAL_COMFORT_REQUESTED, updateThermalComfort); + yield takeEvery(UPDATE_MYRETROFIT_REQUESTED, updateMyRetrofit); + yield takeEvery(CREATE_OCCUPANTS_LOG_REQUESTED, createOccupantsLog); + yield takeEvery(CREATE_THERMAL_COMFORT_LOG_REQUESTED, createThermalComfortLog); + yield takeEvery(CREATE_MYRETROFIT_REQUESTED, createMyRetrofit); + yield takeEvery(DELETE_MYRETROFIT_REQUESTED, deleteMyRetrofit); +} + +export default bloclinkWatcher; diff --git a/src/reducers.js b/src/reducers.js index 5bf5ae9a1062f43b94532462678242010e5bacc5..0044eac8fc7a27868856ca6aa899b4ef72a56f09 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -17,6 +17,7 @@ import buildingArea from './containers/BuildingArea/reducer'; import weather from './containers/Weather/reducer'; import events from './containers/Event/reducer'; import blocnote from './containers/Blocnote/reducer'; +import bloclink from './containers/Performance/reducer'; export default combineReducers({ @@ -37,4 +38,5 @@ export default combineReducers({ weather, events, blocnote, + bloclink, }); diff --git a/src/routes.js b/src/routes.js index 9ef9c9b8d062331206808339af5334025ece1a6f..63ea9d48c64f5a572b8556e728255030c9ebb511 100644 --- a/src/routes.js +++ b/src/routes.js @@ -23,6 +23,7 @@ import Sensors from './containers/Sensors/Sensors'; import SensorInstall from './containers/Sensors/SensorInstall'; import GatewayList from './components/SensorInstall/GatewayList'; import BuildingReports from './containers/BuildingReports'; +import Performance from './containers/Performance'; import Blocnote from './containers/Blocnote'; import FinancialInputs from './containers/Blocnote/FinancialInputs/'; import PreliminaryFinance from './containers/Blocnote/PreliminaryFinance/'; @@ -61,6 +62,9 @@ export default ( + + + diff --git a/src/sagas.js b/src/sagas.js index d880c23e59846c7ff9383acd9af729ae3ff7124e..7d7ea2671868677c2ddc156ed89857d2754d92c2 100644 --- a/src/sagas.js +++ b/src/sagas.js @@ -14,6 +14,7 @@ import buildingAreaSaga from './containers/BuildingArea/sagas'; import weatherSaga from './containers/Weather/sagas'; import eventsSaga from './containers/Event/sagas'; import blocnoteSaga from './containers/Blocnote/sagas'; +import performanceSaga from './containers/Performance/sagas'; export default function* rootSaga() { @@ -34,5 +35,6 @@ export default function* rootSaga() { weatherSaga(), eventsSaga(), blocnoteSaga(), + performanceSaga(), ]; }