diff --git a/src/components/BuildingOverview/index.js b/src/components/BuildingOverview/index.js index 0333ce5da24264c2f1f91805de14ca29908adecf..45014d37808c858d7a16472fe2dbd989726c1a9a 100644 --- a/src/components/BuildingOverview/index.js +++ b/src/components/BuildingOverview/index.js @@ -21,9 +21,13 @@ export default class BuildingOverview extends Component { }; } + componentDidMount() { + this.props.getFolderUrl(this.state.documentPath); + } + renderProjects = () => { const projectList = this.props.projects.list; - if (projectList.length <= 0) { + if (!projectList || projectList.length <= 0) { return (

None

); @@ -46,6 +50,7 @@ export default class BuildingOverview extends Component { { name: 'Overview', url: '' }, ]} links={[ + { name: 'Building Folder', url: this.props.documents.folderUrl, tags: '' }, /* { name: 'Sales Force', url: '//google.com', tags: '' }, { name: 'Engineering', url: '//google.com', tags: 'engineering/' }, { name: 'Finance', url: '//google.com', tags: 'finance/' }, */ diff --git a/src/components/DocumentCardViewer/index.js b/src/components/DocumentCardViewer/index.js index b894278be2a541828469fae46a610f6c6a89da01..544b6418e0efbe4bfa81051b0da31e24aa582ce5 100644 --- a/src/components/DocumentCardViewer/index.js +++ b/src/components/DocumentCardViewer/index.js @@ -16,7 +16,7 @@ export default class DocumentCardViewer extends Component { } componentDidMount() { - this.props.getDocuments(this.props.documentPath, this.props.fileKey); + this.props.getDocuments([this.props.documentPath], [], this.props.fileKey); this.props.getFolderUrl(this.props.documentPath); } diff --git a/src/components/LinkBarDetail/index.js b/src/components/LinkBarDetail/index.js index 96f797ae7ae3521f51e834fb20a5fd6ec65ab66d..0746568f160a2f137fe70b2fdac7cd8b01642a25 100644 --- a/src/components/LinkBarDetail/index.js +++ b/src/components/LinkBarDetail/index.js @@ -24,9 +24,9 @@ const generateBreadcrumbs = (rootURL, breadcrumbs) => { const generateLinks = (rootURL, links) => { const linkList = links.map(val => ( ( - + {val.name} - + ) )); return linkList; diff --git a/src/components/Project/defaultSlots.js b/src/components/Project/defaultSlots.js new file mode 100644 index 0000000000000000000000000000000000000000..d2af4ccc6d5ac5349bfb38b0790f2641c0b25611 --- /dev/null +++ b/src/components/Project/defaultSlots.js @@ -0,0 +1,414 @@ +const defaultSlots = { + 3: { + name: 'Collect Forms', + id: 3, + description: 'Collect 12 Months of Utility Bills and 3 years of Income. Fill out business section of PNS form.', + slots: [ + { name: 'income-statements', title: 'Income Statements', folder_name: 'Income Statements', state_id: 3 }, + { name: 'sceep-forms', title: 'SCEEP Forms', folder_name: 'SCEEP Forms', state_id: 3 }, + { name: 'pea-reports', title: 'PEA Reports', folder_name: 'PEA Reports', state_id: 3 }, + { name: 'taxes', title: 'Taxes', folder_name: 'Taxes', state_id: 3 }, + { name: 'credit-reports', title: 'Credit Reports', folder_name: 'Credit Reports', state_id: 3 }, + { name: 'certificate-of-incorporation', title: 'Certificate of Incorporation', folder_name: 'Certificate of Incorporation', state_id: 3 }, + { name: 'bank-statements', title: 'Bank Statements', folder_name: 'Bank Statements', state_id: 3 }, + { name: '501(c)3-confirmation', title: '501(c)3 Confirmation', folder_name: '501(c)3 Confirmation', state_id: 3 }, + { name: 'proof-of-insurance', title: 'Proof of Property and Liability Insurance', folder_name: 'Proof of Property and Liability Insurance', state_id: 3 }, + { name: 'property-appraisal', title: 'Property Appraisal', folder_name: 'Property Appraisal', state_id: 3 }, + { name: 'title-of-location', title: 'Title of Location', folder_name: 'Title of Location', state_id: 3 }, + { name: 'misc', title: 'Miscellaneous', folder_name: 'Miscellaneous', state_id: 3 }, + ], + }, + + 4: { + name: 'Create Opportunity', + id: 4, + description: 'Create object on project dashboard with pending state. Create opportunity on salesforce.', + slots: [], + }, + + 5: { + name: 'Confirm Bill Validity', + id: 5, + description: 'Quality Assurance checks bills on dashboard are correct.', + slots: [], + }, + + 6: { + name: 'Confirm Validity of 3 Years of Income Statements ', + id: 6, + description: 'Finance does quality check on 3 Years of Income Statements on dashboard.', + slots: [ + { name: 'financial-reports', title: 'Financial Reports', folder_name: 'Financial Reports', state_id: 6 }, + { name: 'balance-sheets', title: 'Balance Sheets', folder_name: 'Balance Sheets', state_id: 6 }, + ], + }, + + + 7: { + name: 'Schedule Site Visit', + id: 7, + description: 'Schedule site visit with client and project engineer.', + slots: [], + }, + + + 8: { + name: 'Complete Business Development PNS', + id: 8, + description: 'Complete Business Development section of PNS form', + slots: [], + }, + + + 9: { + name: 'Business Development PNS Approval', + id: 9, + description: 'Business Development section of PNS form will go through approval.', + slots: [], + }, + + 10: { + name: 'Perform Remote Assessment', + id: 10, + description: 'Project Engineers perform remote assessment (Energy Watch, Code Violations, Google Earth, Heat Load Calculations).', + slots: [ + { name: 'breakdown-of-bills', title: 'Breakdown of Bills', folder_name: 'Breakdown of Bills', state_id: 10 }, + { name: 'heat-load', title: 'Engineering Models', folder_name: 'Engineering Models', state_id: 10 }, + ], + }, + + + 11: { + name: 'Complete Engineer PNS', + id: 11, + description: 'Complete Engineer section of PNS Form.', + slots: [], + }, + + + 12: { + name: 'Engineer PNS Approval', + id: 12, + description: 'Engineer section of the PNS form will go through approval.', + slots: [], + }, + + 13: { + name: 'Conduct Site Visit', + id: 13, + description: 'Project Engineer conducts site visit audit. All data entry is complete. Sensors are installed.', + slots: [ + { name: 'canvas', title: 'Canvas', folder_name: 'Canvas', state_id: 13 }, + { name: 'pictures', title: 'Pictures', folder_name: 'Pictures', state_id: 13 }, + { name: 'sketches', title: 'Sketches', folder_name: 'Sketches', state_id: 13 }, + { name: 'marketplace-photos', title: 'Marketplace Photos', folder_name: 'Marketplace Photos', state_id: 13 }, + ], + }, + + 14: { + name: 'Perform Initial Engineering Analysis', + id: 14, + description: 'Project Engineer performs energy efficiency calculations for annual savings.', + slots: [ + { name: 'pns', title: 'PNS Form', folder_name: 'PNS Form', state_id: 14 }, + { name: 'cooling-load', title: 'Engineering Models', folder_name: 'Engineering Models', state_id: 14 }, + ], + }, + + 15: { + name: 'Send Diagnostic Report', + id: 15, + description: 'Project Engineer creates diagnostic report.', + slots: [ + { name: 'diagnostic-report', title: 'Diagnostic Report', folder_name: 'Diagnostic Report', state_id: 15 }, + ], + }, + + + 16: { + name: 'Complete Finance PNS', + id: 16, + description: 'Complete Finance section of the PNS Form. This step does not have any dependencies. Must only be complete one month post site visit.', + slots: [], + }, + + + 17: { + name: 'Finance PNS Approval', + id: 17, + description: 'Finance section of the PNS form will go through approval.', + slots: [], + }, + + + 18: { + name: 'Send PNS Form', + id: 18, + description: 'Project Engineer sends client PNS Form.', + slots: [], + }, + + + 19: { + name: 'Follow Up Post Project Update', + id: 19, + description: 'Project Engineer contacts client to discover if client is interested in moving forward.', + slots: [], + }, + + 20: { + name: 'Outsource Retrofit', + id: 20, + description: 'Project Manager assesses potential outsourced retrofits (e.g. lighting and windows).', + slots: [ + { name: 'signed-quotes', title: 'Quotes', folder_name: 'Quotes', state_id: 20 }, + ], + }, + + + 21: { + name: 'HPD Finance', + id: 21, + description: 'Finance discovers if HPD will finance the project.', + slots: [], + }, + + + 22: { + name: 'Confirm Client wants HPD', + id: 22, + description: 'Finance confirms client wants to proceed with HPD program.', + slots: [], + }, + + 23: { + name: 'Preliminary Financial Analysis', + id: 23, + description: 'Run preliminary financial model and suggest budget range for Project Engineer.', + slots: [ + { name: 'financial-model', title: 'Financial Model', folder_name: 'Financial Model', state_id: 23 }, + { name: 'budget', title: 'Budget', folder_name: 'Budget', state_id: 23 }, + ], + }, + + + 24: { + name: 'Confirm Project Financing', + id: 24, + description: 'Finance decides if project is feasible.', + slots: [], + }, + + 25: { + name: 'Perform Detailed Calculations and Analysis', + id: 25, + description: 'Project Engineer runs models (hydronic, steam, cooling, heating, controls).', + slots: [ + { name: 'engineering-models', title: 'Engineering Models', folder_name: 'Engineering Models', state_id: 25 }, + ], + }, + + 26: { + name: 'Write Scope of Work Report', + id: 26, + description: 'Project Engineer selects desired equipment for retrofits.', + slots: [ + { name: 'scope-of-work', title: 'Scope of Work', folder_name: 'Scope of Work', state_id: 26 }, + ], + }, + + 27: { + name: 'Obtain Quotes', + id: 27, + description: 'Project Manager bundles nearby projects together and sends equipment lists to contractor to collect quotes and look for violations.', + slots: [ + { name: 'quotes', title: 'Quotes', folder_name: 'Quotes', state_id: 27 }, + ], + }, + + 28: { + name: 'Create Engineering Energy Output Model', + id: 28, + description: 'Project Engineer generates Engineering Output Model for suggested scenario.', + slots: [ + { name: 'engineering-output-model', title: 'Engineering Output Model', folder_name: 'Engineering Output Model', state_id: 28 }, + ], + }, + + 29: { + name: 'Create Financial Model', + id: 29, + description: 'Finance uses quotes and Engineering Output Model to run financial model for suggested scenario.', + slots: [ + { name: 'financial-model', title: 'Financial Model', folder_name: 'Financial Model', state_id: 29 }, + ], + }, + + 30: { + name: 'Create Client Presentation', + id: 30, + description: 'Project Engineer creates final presentation for various retrofit scenarios with upfront cost estimates and annual savings.', + slots: [ + { name: 'final-presentation', title: 'Final Presentation', folder_name: 'Final Presentation', state_id: 30 }, + ], + }, + + + 31: { + name: 'Schedule Client Presentation', + id: 31, + description: 'Project Manager coordinates with client, engineer, and finance/business to set up presentation date.', + slots: [], + }, + + + 32: { + name: 'Present to Client', + id: 32, + description: 'Project Engineer presents retrofit scenarios and financing options to client.', + slots: [], + }, + + + 33: { + name: 'Client Chooses Retrofits', + id: 33, + description: 'Project Engineer contacts Client to determine actionable retrofits. ', + slots: [], + }, + + 34: { + name: 'Redesign Scope of Work', + id: 34, + description: 'Project Engineer redesigns and finalizes scope of work.', + slots: [ + { name: 'redesigned-scope-of-work', title: 'Redesigned Scope of Work', folder_name: 'Redesigned Scope of Work', state_id: 34 }, + ], + }, + + + 35: { + name: 'Send Client Scope of Work and List of Contractors', + id: 35, + description: 'Client will be sent the scope of work and a list of contractors.', + slots: [], + }, + + 36: { + name: 'Finalize Quotes', + id: 36, + description: 'Project Engineer sends contractor Final Scope of Work and finalizes quotes. Contractor signs contract.', + slots: [ + { name: 'contractor-quotes', title: 'Contractor Quotes', folder_name: 'Contractor Quotes', state_id: 36 }, + ], + }, + + 37: { + name: 'Final Financial Model', + id: 37, + description: 'Finance runs the financial model to obtain actual costs including payback years.', + slots: [ + { name: 'final-financial-model', title: 'Final Financial Model', folder_name: 'Final Financial Model', state_id: 37 }, + ], + }, + + + 38: { + name: 'Update Client with Project Financing', + id: 38, + description: 'Finance contacts client to discuss financing options such as loans and marketplace.', + slots: [], + + }, + + + 39: { + name: 'Client Decides on Marketplace', + id: 39, + description: 'Client decides if they want to use Marketplace.', + slots: [], + }, + + + 40: { + name: 'Marketplace Content', + id: 40, + description: 'The Project content gets collected to prepare it for a Marketplace Launch.', + slots: [], + }, + + + 41: { + name: 'Marketplace', + id: 41, + description: 'The Project is launched on the Marketplace and is being funded.', + slots: [], + }, + + + 42: { + name: 'Contractor Downpayment', + id: 42, + description: 'A downpayment is payed to contractor.', + slots: [], + }, + + 43: { + name: 'Apply for Loan', + id: 43, + description: 'Finance appplies for loan.', + slots: [ + { name: 'nyserda', title: 'NYSERDA Request for Financing Form', folder_name: 'NYSERDA Request for Financing Form', state_id: 43 }, + { name: 'loan-application', title: 'Loan Application', folder_name: 'Loan Application', state_id: 43 }, + ], + }, + + 44: { + name: 'Approval of Underwriter', + id: 44, + description: 'Finance is notified that the loan was approved by the Underwriter (banks).', + slots: [], + }, + + 45: { + name: 'Confirm Funding', + id: 45, + description: 'Finance confirms project is funded.', + slots: [], + }, + + + 46: { + name: 'Commence Construction', + id: 46, + description: 'Construction Manager coordinates with client contractor, and engineers to schedule retrofit construction.', + slots: [], + }, + + + 47: { + name: 'Measurement and Verification', + id: 47, + description: 'Project Engineer collects sensor data during site visit, recalibrates sensors, and reinstalls sensors for "post retrofit" measurement and verification.', + slots: [], + }, + + + 48: { + name: 'Loan Payback', + id: 48, + description: 'Loan is in the process of being paid back.', + slots: [], + }, + + + 49: { + name: 'Completed', + id: 49, + description: 'Project is complete!', + slots: [], + }, + +}; + +export default defaultSlots; diff --git a/src/components/Project/index.js b/src/components/Project/index.js index 6f0d9b01b3c3e437069bc3b2f16f7f2afeeb2fd5..357dd92e68ceb54c4092de3a18ed46c86fb63ca3 100644 --- a/src/components/Project/index.js +++ b/src/components/Project/index.js @@ -1,35 +1,241 @@ -import React, { PropTypes } from 'react'; +import React, { Component, PropTypes } from 'react'; +import defaultSlots from './defaultSlots'; +import documentsPropType from '../../containers/Documents/propTypes'; +import { + completeProjectPropTypes, + completeOverviewPropTypes, +} from '../../containers/Building/propTypes'; +import DocumentCard from '../DocumentCard/'; import LinkBarDetail from '../../components/LinkBarDetail'; -import { completeOverviewPropTypes } from '../../containers/Building/propTypes'; - -/* eslint-disable */ -const Project = ({ buildingId, building }) => { - return ( -
- -
-
-
- Coming Soon! -
- {buildingId} +import ErrorAlert from '../../components/ErrorAlert'; +import { uploadSVG } from '../bpl'; +import { getHeaders, projectDocumentURL } from '../../utils/restServices'; +import request from '../../utils/request'; + +export default class Project extends Component { + constructor(props) { + super(props); + + const { building, buildingId } = this.props; + this.state = { + projectDocumentsPath: `/Buildings/${buildingId}_${building.address}/Project-${this.props.params.projectId}/`, + projectId: this.props.params.projectId, + fileKey: 'project', + convertingFile: false, + error: false, + projectDocumentsLoading: false, + }; + } + + componentDidMount() { + // Get all of the files for this project + this.getProjectDocuments(); + this.props.getFolderUrl(this.state.projectDocumentsPath); + } + + // Make a call to project service to get a list of documents associated with this project + // Then call document service with those keys to get those files + getProjectDocuments = () => { + this.setState({ projectDocumentsLoading: true }); + request(`${projectDocumentURL}?project_id=${this.props.params.projectId}`, { + method: 'GET', + headers: getHeaders(), + }).then((res) => { + if (!res.err) { + const documentKeys = res.data.map(val => ( + val.document_key + )); + // Call document service with the keys + if (documentKeys.length > 0) { + this.props.getDocuments([], documentKeys, this.state.fileKey); + } + } else { + this.setState({ error: res.err }); + } + this.setState({ projectDocumentsLoading: false }); + }); + } + + // Post a project document to the projectservice + postProjectDocument = (documentKey, slot) => { + request(`${projectDocumentURL}`, { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify({ + document_key: documentKey, + project_id: this.state.projectId, + role: slot, + }), + }).then((res) => { + if (res.err) { + this.setState({ error: res.err }); + } + }); + } + + generateDocumentHTML = () => { + // An object that given a slot path name, returns a list of documents + const slotToDocuments = this.props.documents.files.project.reduce((acc, val) => { + const newAcc = acc; + if (acc[val.path]) { + newAcc[val.path].push(val); + } else { + newAcc[val.path] = [val]; + } + return acc; + }, {}); + return Object.keys(defaultSlots).map(val => ( +
+

{`${val}. ${defaultSlots[val].name}`}

+
{defaultSlots[val].description}
+
+ {this.generateStateHTML(val, slotToDocuments)} +
+
+ )); + } + + // Generate the HTML to show all of the files for a state + generateStateHTML = (stateId, slotToDocuments) => ( + defaultSlots[stateId].slots.map((val) => { + const slot = `${val.folder_name}`; + return ( +
+

{val.title}

+ {/* Pass the download path into the render upload button function */} + {this.renderUploadButton(slot)} + {/* Render the documents for this slot */} + {this.renderDocuments(slotToDocuments[`${this.state.projectDocumentsPath}${slot}`])} +
+
+ ); + }) + ) + + uploadHandler = (event) => { + const file = event.target.files[0]; + const { buildingId } = this.props; + const fileKey = this.state.fileKey; + const documentSlot = event.target.name; + const documentPath = `${this.state.projectDocumentsPath}${documentSlot}`; + this.setState({ convertingFile: true }); + const fileReader = new FileReader(); + + fileReader.onload = function fileResults() { + this.setState({ convertingFile: false }); + this.props.uploadDocument(buildingId, documentPath, fileReader.result, '', + file.name, fileKey, + projectDocumentURL, { project_id: this.state.projectId, role: documentSlot }); + }.bind(this); + fileReader.readAsDataURL(file); + } + + renderUploadButton = (slot) => { + let disabled = false; + let img = ( +
+ + Choose a file +
+ ); + if (this.state.convertingFile || this.props.documents.uploading) { + disabled = true; + img = ( +
+
+
+
+
+
+ ); + } + + return ( +
+ + +
+ ); + } + + renderDocuments = (documentList) => { + let docs =
; + if (documentList != null && documentList.length > 0) { + docs = documentList.map(item => ( +
+ +
+ )); + } + return docs; + } + + render() { + let projectExists = false; + if (this.props.projects.list !== undefined) { + projectExists = this.props.projects.list.reduce((acc, val) => ( + acc || val.id === parseInt(this.props.params.projectId, 10) + ), false); + } + let mainContent = (
Loading...
); + if (!this.state.projectDocumentsLoading && + !this.props.documents.loading && + !this.props.projects.loading) { + mainContent = projectExists ? this.generateDocumentHTML() : 'This project does not exist'; + } + return ( +
+ +
+
+
+ + + {mainContent} +
-
- ); -}; + ); + } +} Project.propTypes = { buildingId: PropTypes.string, - building: completeOverviewPropTypes, + building: completeOverviewPropTypes, + params: PropTypes.shape({ + projectId: PropTypes.string, + }), + projects: completeProjectPropTypes, + documents: documentsPropType, + getDocuments: PropTypes.func, + uploadDocument: PropTypes.func, + getFolderUrl: PropTypes.func, }; - -export default Project; diff --git a/src/containers/Building/propTypes.js b/src/containers/Building/propTypes.js index f78618d6eb4c220bf51efae1698c13e21372dc83..7aa650c8ac84d78ed48e632e87496f9e72ea4270 100644 --- a/src/containers/Building/propTypes.js +++ b/src/containers/Building/propTypes.js @@ -21,7 +21,7 @@ export const overviewPropTypes = { }; export const projectPropTypes = { - building_id: string, + building_id: number, client_id: number, created: string, id: number, diff --git a/src/containers/Building/sagas.js b/src/containers/Building/sagas.js index 744dea4909df3571437cbeea25450c06f29ce75b..7abc5562d9835dd8906e42e61bd651bfbeb801c2 100644 --- a/src/containers/Building/sagas.js +++ b/src/containers/Building/sagas.js @@ -1,6 +1,6 @@ import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'; import request from '../../utils/request'; -import { getHeaders, buildingsURL } from '../../utils/restServices'; +import { getHeaders, buildingsURL, projectURL } from '../../utils/restServices'; import { LOAD_BUILDING_DETAIL, @@ -11,6 +11,7 @@ import { buildingDetailLoaded, buildingDetailLoadingError, projectsLoaded, + projectsLoadingError, } from './actions'; /** @@ -37,12 +38,19 @@ function* getBuildingDetail(action) { function* getProjects(action) { const buildingId = action.buildingId; - const res = { - buildingId, - data: [], - }; + const res = yield call( + request, + `${projectURL}?building_id=${buildingId}`, { + method: 'GET', + headers: getHeaders(), + } + ); - yield put(projectsLoaded(res)); + if (!res.err) { + yield put(projectsLoaded(res)); + } else { + yield put(projectsLoadingError(res.err)); + } } /** diff --git a/src/containers/Documents/actions.js b/src/containers/Documents/actions.js index 1048416bd4bf7ea745ed3ada706fee6c1637527e..b7eb9320cf109ec68a424674739b64aa7c0c5612 100644 --- a/src/containers/Documents/actions.js +++ b/src/containers/Documents/actions.js @@ -7,12 +7,14 @@ import { UPLOAD_DOCUMENT_ERROR, LOAD_FOLDER_URL, LOAD_FOLDER_URL_SUCCESS, + POST_TO_SERVICE, } from './constants'; -export function loadDocuments(documentPath, fileKey) { +export function loadDocuments(documentPaths, documentKeys, fileKey) { return { type: LOAD_DOCUMENTS, - documentPath, + documentPaths, + documentKeys, fileKey, }; } @@ -33,7 +35,7 @@ export function documentsLoadingError(error) { } export function uploadDocument(buildingId, documentPath, document, tags, - name, fileKey) { + name, fileKey, servicePostURL, servicePostBody) { return { type: UPLOAD_DOCUMENT, buildingId, @@ -42,6 +44,18 @@ export function uploadDocument(buildingId, documentPath, document, tags, tags, name, fileKey, + servicePostURL, + servicePostBody, + }; +} + +export function postToService(serviceURL, body, docRes, fileKey) { + return { + type: POST_TO_SERVICE, + serviceURL, + body, + docRes, + fileKey, }; } diff --git a/src/containers/Documents/constants.js b/src/containers/Documents/constants.js index 5e8ab2d6ceb06ef2d46233fd0fc50d5aeb04b20f..4efbcad50d9c42a88c8977eec34c3f782565e6da 100644 --- a/src/containers/Documents/constants.js +++ b/src/containers/Documents/constants.js @@ -6,3 +6,4 @@ export const UPLOAD_DOCUMENT_SUCCESS = 'UPLOAD_DOCUMENT_SUCCESS'; export const UPLOAD_DOCUMENT_ERROR = 'UPLOAD_DOCUMENT_ERROR'; export const LOAD_FOLDER_URL = 'LOAD_FOLDER_URL'; export const LOAD_FOLDER_URL_SUCCESS = 'LOAD_FOLDER_URL_SUCCESS'; +export const POST_TO_SERVICE = 'POST_TO_SERVICE'; diff --git a/src/containers/Documents/propTypes.js b/src/containers/Documents/propTypes.js index 185bf29307a1bc20e42ff5525eb9335f0a6c78b4..cc8a037bbea0bd0a29673a01845eabae388bfac8 100644 --- a/src/containers/Documents/propTypes.js +++ b/src/containers/Documents/propTypes.js @@ -1,6 +1,6 @@ import { PropTypes } from 'react'; -const { shape, arrayOf, oneOfType, string, number, bool } = PropTypes; +const { shape, arrayOf, oneOfType, string, number, bool, instanceOf } = PropTypes; export const documentProps = shape({ box_id: number, @@ -21,7 +21,7 @@ export default shape({ loading: bool, error: oneOfType([ bool, - string, + instanceOf(Error), ]), folderUrl: string, files: shape({ diff --git a/src/containers/Documents/sagas.js b/src/containers/Documents/sagas.js index 8234b1899658294d7e306a7114fc8d4dda60500a..95cd3f92b0999b40905e07939585b790839fa9f6 100644 --- a/src/containers/Documents/sagas.js +++ b/src/containers/Documents/sagas.js @@ -6,6 +6,7 @@ import { LOAD_DOCUMENTS, UPLOAD_DOCUMENT, LOAD_FOLDER_URL, + POST_TO_SERVICE, } from './constants'; import { @@ -14,15 +15,22 @@ import { documentUploaded, documentUploadError, folderUrlLoaded, + postToService, } from './actions'; function* getDocuments(action) { - const { documentPath, fileKey } = action; + const { documentPaths, documentKeys, fileKey } = action; + const pathsString = documentPaths.reduce((acc, val) => ( + `${acc}paths[]=${val}&` + ), ''); + const keysString = documentKeys.reduce((acc, val) => ( + `${acc}keys[]=${val}&` + ), ''); const res = yield call( request, - `${documentURL}?paths[]=${documentPath}`, { + `${documentURL}?${pathsString}${keysString}`, { method: 'GET', headers: getHeaders(), } @@ -35,6 +43,23 @@ function* getDocuments(action) { } } +function* postToServiceSaga(action) { + const { serviceURL, body } = action; + const res = yield call( + request, + `${serviceURL}`, { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(body), + } + ); + if (!res.err) { + yield put(documentUploaded(action.docRes, action.fileKey)); + } else { + yield put(documentUploadError(res.err)); + } +} + function* uploadDocument(action) { const { buildingId, documentPath, document, tags, name, fileKey } = action; @@ -54,7 +79,13 @@ function* uploadDocument(action) { ); if (!res.err) { - yield put(documentUploaded(res, fileKey)); + if (action.servicePostURL !== undefined) { + const { servicePostURL, servicePostBody } = action; + servicePostBody.document_key = res.data.key; + yield put(postToService(servicePostURL, servicePostBody, res, fileKey)); + } else { + yield put(documentUploaded(res, fileKey)); + } } else { yield put(documentUploadError(res.err)); } @@ -79,5 +110,6 @@ function* getFolderUrl(action) { export default function* () { yield takeLatest(LOAD_DOCUMENTS, getDocuments); yield takeLatest(UPLOAD_DOCUMENT, uploadDocument); + yield takeLatest(POST_TO_SERVICE, postToServiceSaga); yield takeLatest(LOAD_FOLDER_URL, getFolderUrl); } diff --git a/src/utils/restServices.js b/src/utils/restServices.js index 40a39a0a52503cc90c5875df2254b125da1a164d..0a2ae8dec85a0583ef1db935e17b37dc3ad122a6 100644 --- a/src/utils/restServices.js +++ b/src/utils/restServices.js @@ -15,3 +15,5 @@ export const turkURL = `${buildingService}/turkhit/`; export const documentURL = `${documentService}/document/`; export const accountURL = `${utilityService}/account/`; export const billsURL = `${utilityService}/bills/`; +export const projectURL = `${projectService}/project/`; +export const projectDocumentURL = `${projectService}/project/document/`;