From 764eb5c4f10c6a8e0c7d8b033dcc3f6c88037366 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Wed, 15 Jul 2020 21:28:08 -0400 Subject: [PATCH 01/23] Initial implementation of edit trips. Two bugs: 1. Form input boxes should be value instead of placeholder when editing a trip. 2. The list of collaborators shows up in each individual box rather than one in each box. --- frontend/src/components/ViewTrips/index.js | 15 ++----- .../components/ViewTrips/save-trip-modal.js | 4 +- frontend/src/components/ViewTrips/trip.js | 39 ++++++++++++++++--- .../components/ViewTrips/trips-container.js | 12 ++++-- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/ViewTrips/index.js b/frontend/src/components/ViewTrips/index.js index c5cce8ca..e9e21478 100644 --- a/frontend/src/components/ViewTrips/index.js +++ b/frontend/src/components/ViewTrips/index.js @@ -98,20 +98,12 @@ class ViewTrips extends React.Component { * to ensure the modal has the visual characteristics of an "edit trip" modal * and overwrites and existing Trip document in the database. * - * TODO(Issue #69): Get individual tripId and trip data for placeholderObj. */ - showEditTripModal = () => { + showEditTripModal = (tripId, placeholderObj) => { this.setState({ title: 'Edit Trip', - tripId: null, - placeholderObj: { - name: null, - description: null, - destination: null, - startDate: null, - endDate: null, - collaborators: [] - } + tripId: tripId, + placeholderObj: placeholderObj }); this.showSaveTripModal(); } @@ -138,6 +130,7 @@ class ViewTrips extends React.Component { diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 8e654796..559cfbbe 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -120,8 +120,8 @@ function createFormGroup(controlId, formLabel, inputType, placeholder, ref) { * @param {Object} props These are the props for this component: * - db: Firestore database instance. * - show: Boolean that determines if the add trips modal should be displayed. - * - handleClose: The function that handles closing the add trips modal. - * - refreshTripsContainer: Function that handles refreshing the TripsContainer + * - handleClose: Event handler responsible for closing the add trips modal. + * - refreshTripsContainer: Handler that refreshes the TripsContainer * component upon trip creation (Remove when fix Issue #62). * - title: The title of the modal. * - tripId: For adding a new trip, this will be null. For editting an existing diff --git a/frontend/src/components/ViewTrips/trip.js b/frontend/src/components/ViewTrips/trip.js index 8b197a90..0bd7e532 100644 --- a/frontend/src/components/ViewTrips/trip.js +++ b/frontend/src/components/ViewTrips/trip.js @@ -29,6 +29,10 @@ export function getCollaboratorEmails(collaboratorUidArr) { return collaboratorEmailArr.join(', '); } +function timestampToISOString(timestamp) { + return timestamp.toDate().toISOString().substring(0,10); +} + /** * Component corresponding to the container containing an individual trip. * @@ -39,18 +43,41 @@ export function getCollaboratorEmails(collaboratorUidArr) { * @param {Object} props These are the props for this component: * - tripObj: JS object holding a Trip document fields and corresponding values. * - tripId: Document ID for the current Trip document. + * - handleEditTrip: Handler that displays the edit trip modal. + * - key: Special React attribute that ensures a new Trip instance is + * created whenever this key is updated */ const Trip = (props) => { + const name = props.tripObj.name; + const description = props.tripObj.description; + const destination = props.tripObj.destination; + const collaboratorEmailsStr = + getCollaboratorEmails(props.tripObj.collaborators); + + const placeholderObj = { + name: name, + description: description, + destination: destination, + startDate: timestampToISOString(props.tripObj.start_date), + endDate: timestampToISOString(props.tripObj.end_date), + collaborators: collaboratorEmailsStr.split(', ') + }; + return (
-

{props.tripObj.name}

-

{props.tripObj.description}

+

{name}

+

{description}

{getDateRange(props.tripObj)}

-

{props.tripObj.destination}

-

{getCollaboratorEmails(props.tripObj.collaborators)}

+

{destination}

+

{collaboratorEmailsStr}

- {/* TODO(Issue 15): Add edit trip page. */} - +
); diff --git a/frontend/src/components/ViewTrips/trips-container.js b/frontend/src/components/ViewTrips/trips-container.js index 8f0f38d7..07e95bd7 100644 --- a/frontend/src/components/ViewTrips/trips-container.js +++ b/frontend/src/components/ViewTrips/trips-container.js @@ -28,14 +28,18 @@ function queryUserTrips(db, userEmail) { * * @param {Promise} querySnapshot Promise * object containing the query results with zero or more Trip documents. + * @param {EventHandler} handleEditTrip Displays the edit trip modal. * @return {Promise>} Promise object containing an array * of Trip React/HTML elements corresponding to the Trip documents included * in `querySnapshot`. */ -function serveTrips(querySnapshot) { +function serveTrips(querySnapshot, handleEditTrip) { return new Promise(function(resolve) { const tripsContainer = querySnapshot.docs.map(doc => - ( )); + ( + ) + ); resolve(tripsContainer); }); } @@ -60,6 +64,7 @@ function getErrorElement(error) { * * @param {Object} props These are the props for this component: * - db: Firestore database instance. + * - handleEditTrip: Handler that displays the edit trip modal. * - key: Special React attribute that ensures a new TripsContainer instance is * created whenever this key is updated (Remove when fix Issue #62). * @extends React.Component @@ -76,7 +81,8 @@ class TripsContainer extends React.Component { try { const querySnapshot = await queryUserTrips( this.props.db, getCurUserEmail()); - let tripsContainer = await serveTrips(querySnapshot); + let tripsContainer = await serveTrips(querySnapshot, + this.props.handleEditTrip); this.setState({ trips: tripsContainer }); } catch (error) { From 78a9356549c26ad3ad9b6ba3fb4525041d026f75 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Wed, 15 Jul 2020 22:10:23 -0400 Subject: [PATCH 02/23] Rename placeholderObj to defaultFormObj now that these values are used for either placeholder or defaultValue props. --- frontend/src/components/ViewTrips/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/ViewTrips/index.js b/frontend/src/components/ViewTrips/index.js index e9e21478..958fcdd6 100644 --- a/frontend/src/components/ViewTrips/index.js +++ b/frontend/src/components/ViewTrips/index.js @@ -22,7 +22,7 @@ class ViewTrips extends React.Component { refreshSaveTripModal: false, modalTitle: null, tripId: null, - placeholderObj: { + defaultFormObj: { name: null, description: null, destination: null, @@ -71,7 +71,7 @@ class ViewTrips extends React.Component { /** * Handler that displays the add trip page. * - * Sets state for the states `title`, `tripId`, and `placeholderObj` in order + * Sets state for the states `title`, `tripId`, and `defaultFormObj` in order * to ensure the modal has the visual characteristics of an "add trip" modal * and creates a new Trip document in the database. */ @@ -79,7 +79,7 @@ class ViewTrips extends React.Component { this.setState({ title: 'Add New Trip', tripId: null, - placeholderObj: { + defaultFormObj: { name: 'Enter Trip Name', description: 'Enter Trip Description', destination: 'Enter Trip Destination', @@ -94,16 +94,16 @@ class ViewTrips extends React.Component { /** * Handler that displays the edit trip page. * - * Sets state for the states `title`, `tripId`, and `placeholderObj` in order + * Sets state for the states `title`, `tripId`, and `defaultFormObj` in order * to ensure the modal has the visual characteristics of an "edit trip" modal * and overwrites and existing Trip document in the database. * */ - showEditTripModal = (tripId, placeholderObj) => { + showEditTripModal = (tripId, tripData) => { this.setState({ title: 'Edit Trip', tripId: tripId, - placeholderObj: placeholderObj + defaultFormObj: tripData }); this.showSaveTripModal(); } @@ -120,7 +120,7 @@ class ViewTrips extends React.Component { refreshTripsContainer={this.refreshTripsContainer} title={this.state.title} tripId={this.state.tripId} - placeholderObj={this.state.placeholderObj} + defaultFormObj={this.state.defaultFormObj} key={this.state.refreshSaveTripModal} />
From 2e4398c2ce5f2aaff648e66eb7d81534753ef4dd Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Wed, 15 Jul 2020 22:15:32 -0400 Subject: [PATCH 03/23] Fix placeholder for edit trip bug: now edit trip modal form inputs are values rather than placeholders. --- .../components/ViewTrips/save-trip-modal.js | 109 +++++++++++++----- 1 file changed, 78 insertions(+), 31 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 559cfbbe..5d402d41 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -10,15 +10,26 @@ import createTrip from './create-new-trip.js'; * Returns a Form.Control element with input type 'text' and other fields * specified by the function parameters. * - * @param {string} placeholder Text placehold in the form input + * @param {string} defaultVal Text default value in the form input * @param {React.RefObject} ref Ref attached to the value inputted in the form. + * @param {boolean} isNewTripForm True if form is adding new trip, false if + * form is editting existing trip. * @return {JSX.Element} The Form.Control element. */ -function createTextFormControl(placeholder, ref) { +function createTextFormControl(defaultVal, ref, isNewTripForm) { + if (isNewTripForm) { + return ( + + ); + } return ( ); @@ -42,30 +53,57 @@ function createDateFormControl(defaultValue, ref) { ); } +/** + * Returns a Form.Control element with input type 'text' and other fields + * specified by the function parameters. + * + * @param {string} defaultVal Text default value in the form input + * @param {React.RefObject} ref Ref attached to the value inputted in the form. + * @param {number} idx Index of the email Form.Control used for key prop. + * @param {boolean} isNewTripForm True if form is adding new trip, false if + * form is editting existing trip. + * @return {JSX.Element} The Form.Control element. + */ +function createEmailFormControl(defaultVal, ref, idx, isNewTripForm) { + if (isNewTripForm) { + return ( + + ); + } + return ( + + ); +} + /** * Returns a Form.Control element with input type 'text' and other fields * specified by the function parameters. * * TODO(Issue #67): Email verification before submitting the form. * - * @param {string} placeholder Text placehold in the form input + * @param {string} defaultVal Text placehold in the form input * @param {React.RefObject} refArr The list of refs attached to the emails * inputted in the form. + * @param {boolean} isNewTripForm True if form is adding new trip, false if + * form is editting existing trip. * @return {JSX.Element} The Form.Control element. */ -function createMultiFormControl(placeholder, refArr) { +function createMultiFormControl(defaultVal, refArr, isNewTripForm) { return ( <> - {refArr.map((ref, idx) => { - return ( - - ); - })} + {refArr.map((ref, idx) => + createEmailFormControl(defaultVal, ref, idx, isNewTripForm) + )} ); } @@ -77,22 +115,25 @@ function createMultiFormControl(placeholder, refArr) { * input prop. * @param {string} formLabel Label/title for the form input. * @param {string} inputType Input type of the form. - * @param {string} placeholder Text placeholder in the form input. + * @param {string} defaultVal Text default value in the form input. * @param {React.RefObject} ref Ref attatched to the valued inputted in the form. * @param {string} subFormText Subtext instructions under a form input. + * @param {boolean} isNewTripForm True if form is adding new trip, false if + * form is editting existing trip. * @return {JSX.Element} The Form.Group element. */ -function createFormGroup(controlId, formLabel, inputType, placeholder, ref) { +function createFormGroup(controlId, formLabel, inputType, + defaultVal, ref, isNewTripForm) { let formControl; switch(inputType) { case 'text': - formControl = createTextFormControl(placeholder, ref); + formControl = createTextFormControl(defaultVal, ref, isNewTripForm); break; case 'date': - formControl = createDateFormControl(placeholder, ref); + formControl = createDateFormControl(defaultVal, ref); break; case 'emails': - formControl = createMultiFormControl(placeholder, ref); + formControl = createMultiFormControl(defaultVal, ref, isNewTripForm); break; default: console.error('There should be no other input type') @@ -115,7 +156,7 @@ function createFormGroup(controlId, formLabel, inputType, placeholder, ref) { * This component "acts" as a parent of the (non-existant) AddTripModal and * EditTripModal components. The only differences in the implementation between * the two fake components are dervied from the props `title`, `tripid`, and - * `placeholderObj` (see below). + * `defaultFormObj` (see below). * * @param {Object} props These are the props for this component: * - db: Firestore database instance. @@ -126,7 +167,7 @@ function createFormGroup(controlId, formLabel, inputType, placeholder, ref) { * - title: The title of the modal. * - tripId: For adding a new trip, this will be null. For editting an existing * trip, this will the document id associated with the trip. - * - placeholderObj: Object containing the placeholder/default values for the + * - defaultFormObj: Object containing the placeholder/default values for the * form input text boxes. * - key: Special React attribute that ensures a new AddTripModal instance is * created whenever this key is updated @@ -146,9 +187,9 @@ class SaveTripModal extends React.Component { this.endDateRef = React.createRef(); // Create the number of collaborator input box refs as number of - // collaborators specified in the placeholderObj + // collaborators specified in prop `defaultFormObj` const collaboratorsRefArr = []; - for (let i = 0; i < this.props.placeholderObj.collaborators.length; i++) { + for (let i = 0; i < this.props.defaultFormObj.collaborators.length; i++) { collaboratorsRefArr.push(React.createRef()) } this.state = { collaboratorsRefArr: collaboratorsRefArr } @@ -185,6 +226,8 @@ class SaveTripModal extends React.Component { /** @inheritdoc */ render() { + const isNewTripForm = this.props.tripId === null; + return ( @@ -194,18 +237,22 @@ class SaveTripModal extends React.Component {
{createFormGroup('tripNameGroup', 'Trip Name', 'text', - this.props.placeholderObj.name, this.nameRef)} + this.props.defaultFormObj.name, this.nameRef, isNewTripForm)} {createFormGroup('tripDescripGroup', 'Trip Description', 'text', - this.props.placeholderObj.description, this.descriptionRef)} + this.props.defaultFormObj.description, this.descriptionRef, + isNewTripForm)} {createFormGroup('tripDestGroup', 'Trip Destination', 'text', - this.props.placeholderObj.destination, this.destinationRef)} + this.props.defaultFormObj.destination, this.destinationRef, + isNewTripForm)} {createFormGroup('tripStartDateGroup', 'Start Date', 'date', - this.props.placeholderObj.startDate, this.startDateRef)} + this.props.defaultFormObj.startDate, this.startDateRef, + isNewTripForm)} {createFormGroup('tripEndDateGroup', 'End Date', 'date', - this.props.placeholderObj.endDate, this.endDateRef)} + this.props.defaultFormObj.endDate, this.endDateRef, + isNewTripForm)} {createFormGroup('tripCollabsGroup', 'Trip Collaborators', 'emails', - this.props.placeholderObj.collaborators, - this.state.collaboratorsRefArr)} + this.props.defaultFormObj.collaborators, + this.state.collaboratorsRefArr, isNewTripForm)} From fbb72f07fe6736d04d4436ed69dddcb8d184965a Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 10:20:36 -0400 Subject: [PATCH 04/23] Rename tripObj to tripData for clarity (it is the document data). --- frontend/src/components/ViewTrips/trip.js | 28 +++++++++---------- .../components/ViewTrips/trips-container.js | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/ViewTrips/trip.js b/frontend/src/components/ViewTrips/trip.js index 0bd7e532..e928fead 100644 --- a/frontend/src/components/ViewTrips/trip.js +++ b/frontend/src/components/ViewTrips/trip.js @@ -7,17 +7,17 @@ import ViewActivitiesButton from './view-activities-button.js'; /** * Returns the date range of the trip associated with the Trip document data - * `tripObj`. + * `tripData`. * - * Note: tripObj will always contain valid star_date and end_date fields. + * Note: tripData will always contain valid star_date and end_date fields. * - * @param {firebase.firestore.DocumentData} tripObj Object containing the fields + * @param {firebase.firestore.DocumentData} tripData Object containing the fields * and values for a Trip document. * @return Date range of the trip (if it exists). */ -export function getDateRange(tripObj) { - const startDate = tripObj.start_date.toDate(); - const endDate = tripObj.end_date.toDate(); +export function getDateRange(tripData) { + const startDate = tripData.start_date.toDate(); + const endDate = tripData.end_date.toDate(); return `${startDate.getMonth() + 1}/${startDate.getDate()}/` + `${startDate.getFullYear()} - ${endDate.getMonth() + 1}/` + `${endDate.getDate()}/${endDate.getFullYear()}`; @@ -41,25 +41,25 @@ function timestampToISOString(timestamp) { * on the 'display' side. * * @param {Object} props These are the props for this component: - * - tripObj: JS object holding a Trip document fields and corresponding values. + * - tripData: Object holding a Trip document fields and corresponding values. * - tripId: Document ID for the current Trip document. * - handleEditTrip: Handler that displays the edit trip modal. * - key: Special React attribute that ensures a new Trip instance is * created whenever this key is updated */ const Trip = (props) => { - const name = props.tripObj.name; - const description = props.tripObj.description; - const destination = props.tripObj.destination; + const name = props.tripData.name; + const description = props.tripData.description; + const destination = props.tripData.destination; const collaboratorEmailsStr = - getCollaboratorEmails(props.tripObj.collaborators); + getCollaboratorEmails(props.tripData.collaborators); const placeholderObj = { name: name, description: description, destination: destination, - startDate: timestampToISOString(props.tripObj.start_date), - endDate: timestampToISOString(props.tripObj.end_date), + startDate: timestampToISOString(props.tripData.start_date), + endDate: timestampToISOString(props.tripData.end_date), collaborators: collaboratorEmailsStr.split(', ') }; @@ -67,7 +67,7 @@ const Trip = (props) => {

{name}

{description}

-

{getDateRange(props.tripObj)}

+

{getDateRange(props.tripData)}

{destination}

{collaboratorEmailsStr}

diff --git a/frontend/src/components/ViewTrips/trips-container.js b/frontend/src/components/ViewTrips/trips-container.js index 07e95bd7..609e557b 100644 --- a/frontend/src/components/ViewTrips/trips-container.js +++ b/frontend/src/components/ViewTrips/trips-container.js @@ -36,7 +36,7 @@ function queryUserTrips(db, userEmail) { function serveTrips(querySnapshot, handleEditTrip) { return new Promise(function(resolve) { const tripsContainer = querySnapshot.docs.map(doc => - ( ) ); From 7f8529000c71c49f7b70999688f7d851a0dfbe01 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 12:24:02 -0400 Subject: [PATCH 05/23] Update form control creation function JSDoc. --- frontend/src/components/ViewTrips/save-trip-modal.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 5d402d41..780a8e2d 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -7,7 +7,7 @@ import Form from 'react-bootstrap/Form'; import createTrip from './create-new-trip.js'; /** - * Returns a Form.Control element with input type 'text' and other fields + * Returns a Form.Control element with input type 'text' and other props * specified by the function parameters. * * @param {string} defaultVal Text default value in the form input @@ -36,7 +36,7 @@ function createTextFormControl(defaultVal, ref, isNewTripForm) { } /** - * Returns a Form.Control element with input type 'text' and other fields + * Returns a Form.Control element with input type 'date' and other props * specified by the function parameters. * * @param {React.RefObject} refArr The list of refs attached to the emails @@ -54,7 +54,7 @@ function createDateFormControl(defaultValue, ref) { } /** - * Returns a Form.Control element with input type 'text' and other fields + * Returns a Form.Control element with input type 'email' and other props * specified by the function parameters. * * @param {string} defaultVal Text default value in the form input @@ -86,8 +86,8 @@ function createEmailFormControl(defaultVal, ref, idx, isNewTripForm) { } /** - * Returns a Form.Control element with input type 'text' and other fields - * specified by the function parameters. + * Returns multiple Form.Control elements with input type 'email' and other + * props specified by the function parameters. * * TODO(Issue #67): Email verification before submitting the form. * @@ -144,7 +144,6 @@ function createFormGroup(controlId, formLabel, inputType, {formLabel} {formControl} - {/* Temporary instructions until fix Issue #52 */} ) From 62993fdedb64d33b9d1b43f213144836cfebd681 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 14:25:08 -0400 Subject: [PATCH 06/23] Fix bug: current user was displayed when editting collaborators and cause duplicate current users when saving trip. Fix invlolves removing current user from list of collaborators when editting collaborator and add Issue #71 to let collaborators remove themselves from a trip. --- .../components/ViewTrips/save-trip-modal.js | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 780a8e2d..7501749f 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -10,18 +10,18 @@ import createTrip from './create-new-trip.js'; * Returns a Form.Control element with input type 'text' and other props * specified by the function parameters. * - * @param {string} defaultVal Text default value in the form input + * @param {string} defaultText Default text value in the form input. * @param {React.RefObject} ref Ref attached to the value inputted in the form. * @param {boolean} isNewTripForm True if form is adding new trip, false if * form is editting existing trip. * @return {JSX.Element} The Form.Control element. */ -function createTextFormControl(defaultVal, ref, isNewTripForm) { +function createTextFormControl(defaultText, ref, isNewTripForm) { if (isNewTripForm) { return ( ); @@ -29,7 +29,7 @@ function createTextFormControl(defaultVal, ref, isNewTripForm) { return ( ); @@ -39,16 +39,17 @@ function createTextFormControl(defaultVal, ref, isNewTripForm) { * Returns a Form.Control element with input type 'date' and other props * specified by the function parameters. * + * @param {string} defaultDate Default ISO date string in the form input. * @param {React.RefObject} refArr The list of refs attached to the emails * inputted in the form. * @return {JSX.Element} The Form.Control element. */ -function createDateFormControl(defaultValue, ref) { +function createDateFormControl(defaultDate, ref) { return ( ); } @@ -57,19 +58,19 @@ function createDateFormControl(defaultValue, ref) { * Returns a Form.Control element with input type 'email' and other props * specified by the function parameters. * - * @param {string} defaultVal Text default value in the form input + * @param {string} defaultEmail Default text value in the form input. * @param {React.RefObject} ref Ref attached to the value inputted in the form. * @param {number} idx Index of the email Form.Control used for key prop. * @param {boolean} isNewTripForm True if form is adding new trip, false if * form is editting existing trip. * @return {JSX.Element} The Form.Control element. */ -function createEmailFormControl(defaultVal, ref, idx, isNewTripForm) { +function createEmailFormControl(defaultEmail, ref, idx, isNewTripForm) { if (isNewTripForm) { return ( @@ -78,7 +79,7 @@ function createEmailFormControl(defaultVal, ref, idx, isNewTripForm) { return ( @@ -89,20 +90,27 @@ function createEmailFormControl(defaultVal, ref, idx, isNewTripForm) { * Returns multiple Form.Control elements with input type 'email' and other * props specified by the function parameters. * + * One is added to the index of the emails show in order to display all + * collaborators except the current user. + * * TODO(Issue #67): Email verification before submitting the form. * - * @param {string} defaultVal Text placehold in the form input - * @param {React.RefObject} refArr The list of refs attached to the emails - * inputted in the form. + * TODO(Issue #72): More intuitive remove collaborator when !`isNewTripForm`. + * + * @param {!Array} defaultEmailArr Array of the emails to be displayed + * in the default form fields. + * @param {!Array} refArr Array of refs attached to the + * emails inputted in the form. * @param {boolean} isNewTripForm True if form is adding new trip, false if * form is editting existing trip. * @return {JSX.Element} The Form.Control element. */ -function createMultiFormControl(defaultVal, refArr, isNewTripForm) { +function createMultiFormControl(defaultEmailArr, refArr, isNewTripForm) { return ( <> {refArr.map((ref, idx) => - createEmailFormControl(defaultVal, ref, idx, isNewTripForm) + createEmailFormControl(defaultEmailArr[idx + 1], + ref, idx, isNewTripForm) )} ); @@ -115,7 +123,7 @@ function createMultiFormControl(defaultVal, refArr, isNewTripForm) { * input prop. * @param {string} formLabel Label/title for the form input. * @param {string} inputType Input type of the form. - * @param {string} defaultVal Text default value in the form input. + * @param {string} defaultVal Default value in the form input. * @param {React.RefObject} ref Ref attatched to the valued inputted in the form. * @param {string} subFormText Subtext instructions under a form input. * @param {boolean} isNewTripForm True if form is adding new trip, false if @@ -185,10 +193,13 @@ class SaveTripModal extends React.Component { this.startDateRef = React.createRef(); this.endDateRef = React.createRef(); - // Create the number of collaborator input box refs as number of - // collaborators specified in prop `defaultFormObj` + // Create the number of collaborator input box refs as one less than the + // number of collaborators specified in prop `defaultFormObj` (do not + // include current user in list) + // + // TODO(Issue 71): Give user option to remove themself as collab. from trip. const collaboratorsRefArr = []; - for (let i = 0; i < this.props.defaultFormObj.collaborators.length; i++) { + for (let i = 1; i < this.props.defaultFormObj.collaborators.length; i++) { collaboratorsRefArr.push(React.createRef()) } this.state = { collaboratorsRefArr: collaboratorsRefArr } From 6ac1b0c870186e6e325525a91b477b7127e056c5 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 14:56:32 -0400 Subject: [PATCH 07/23] Update document writen/overwritten success console.logs. --- frontend/src/components/ViewTrips/create-new-trip.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ViewTrips/create-new-trip.js b/frontend/src/components/ViewTrips/create-new-trip.js index 03bbf12e..168786e9 100644 --- a/frontend/src/components/ViewTrips/create-new-trip.js +++ b/frontend/src/components/ViewTrips/create-new-trip.js @@ -121,7 +121,11 @@ function createTrip(db, tripId, rawTripObj) { addTripToFirestore(db, tripId, formattedTripObj) .then(docRef => { - console.log("Document written with ID: ", docRef.id); + if (tripId === null) { + console.log("Document created with ID: ", docRef.id); + } else { + console.log("Document overwritten with ID: ", tripId); + } }) .catch(error => { console.error("Error adding document: ", error); From f0139c9c6f0ffc7c6fb35ea0d01c238ed53b82a6 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 15:17:44 -0400 Subject: [PATCH 08/23] Improve JSDoc in save-trip-modal.js. --- frontend/src/components/ViewTrips/save-trip-modal.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 7501749f..b24b0d34 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -103,7 +103,7 @@ function createEmailFormControl(defaultEmail, ref, idx, isNewTripForm) { * emails inputted in the form. * @param {boolean} isNewTripForm True if form is adding new trip, false if * form is editting existing trip. - * @return {JSX.Element} The Form.Control element. + * @return {JSX.Element} The Form.Control elements. */ function createMultiFormControl(defaultEmailArr, refArr, isNewTripForm) { return ( @@ -160,10 +160,13 @@ function createFormGroup(controlId, formLabel, inputType, /** * Component corresponding to the save trips modal. * - * This component "acts" as a parent of the (non-existant) AddTripModal and + * This component acts as a "pseudo-parent" of the AddTripModal and * EditTripModal components. The only differences in the implementation between * the two fake components are dervied from the props `title`, `tripid`, and - * `defaultFormObj` (see below). + * `defaultFormObj` (see below). The primary difference between the add and + * edit trip modals is the former displays placeholder values in the empty form + * fields whereas the latter displays the current values of the trip in the + * respective form fields. * * @param {Object} props These are the props for this component: * - db: Firestore database instance. From 244b0ccd65c6e9f478c040b7bee964abbbce6068 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 15:29:09 -0400 Subject: [PATCH 09/23] Rename isNewtripForm to isAddNewTripForm and make it instance variable for ease of use anywhere in component class. --- .../components/ViewTrips/save-trip-modal.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index b24b0d34..8df49bb0 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -12,12 +12,12 @@ import createTrip from './create-new-trip.js'; * * @param {string} defaultText Default text value in the form input. * @param {React.RefObject} ref Ref attached to the value inputted in the form. - * @param {boolean} isNewTripForm True if form is adding new trip, false if + * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. * @return {JSX.Element} The Form.Control element. */ -function createTextFormControl(defaultText, ref, isNewTripForm) { - if (isNewTripForm) { +function createTextFormControl(defaultText, ref, isAddTripForm) { + if (isAddTripForm) { return ( } defaultEmailArr Array of the emails to be displayed * in the default form fields. * @param {!Array} refArr Array of refs attached to the * emails inputted in the form. - * @param {boolean} isNewTripForm True if form is adding new trip, false if + * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. * @return {JSX.Element} The Form.Control elements. */ -function createMultiFormControl(defaultEmailArr, refArr, isNewTripForm) { +function createMultiFormControl(defaultEmailArr, refArr, isAddTripForm) { return ( <> {refArr.map((ref, idx) => createEmailFormControl(defaultEmailArr[idx + 1], - ref, idx, isNewTripForm) + ref, idx, isAddTripForm) )} ); @@ -126,22 +126,22 @@ function createMultiFormControl(defaultEmailArr, refArr, isNewTripForm) { * @param {string} defaultVal Default value in the form input. * @param {React.RefObject} ref Ref attatched to the valued inputted in the form. * @param {string} subFormText Subtext instructions under a form input. - * @param {boolean} isNewTripForm True if form is adding new trip, false if + * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. * @return {JSX.Element} The Form.Group element. */ function createFormGroup(controlId, formLabel, inputType, - defaultVal, ref, isNewTripForm) { + defaultVal, ref, isAddTripForm) { let formControl; switch(inputType) { case 'text': - formControl = createTextFormControl(defaultVal, ref, isNewTripForm); + formControl = createTextFormControl(defaultVal, ref, isAddTripForm); break; case 'date': formControl = createDateFormControl(defaultVal, ref); break; case 'emails': - formControl = createMultiFormControl(defaultVal, ref, isNewTripForm); + formControl = createMultiFormControl(defaultVal, ref, isAddTripForm); break; default: console.error('There should be no other input type') @@ -195,6 +195,7 @@ class SaveTripModal extends React.Component { this.destinationRef = React.createRef(); this.startDateRef = React.createRef(); this.endDateRef = React.createRef(); + this.isAddTripForm = this.props.tripId === null; // Create the number of collaborator input box refs as one less than the // number of collaborators specified in prop `defaultFormObj` (do not @@ -239,8 +240,6 @@ class SaveTripModal extends React.Component { /** @inheritdoc */ render() { - const isNewTripForm = this.props.tripId === null; - return ( @@ -250,22 +249,23 @@ class SaveTripModal extends React.Component { {createFormGroup('tripNameGroup', 'Trip Name', 'text', - this.props.defaultFormObj.name, this.nameRef, isNewTripForm)} + this.props.defaultFormObj.name, this.nameRef, + this.isAddTripForm)} {createFormGroup('tripDescripGroup', 'Trip Description', 'text', this.props.defaultFormObj.description, this.descriptionRef, - isNewTripForm)} + this.isAddTripForm)} {createFormGroup('tripDestGroup', 'Trip Destination', 'text', this.props.defaultFormObj.destination, this.destinationRef, - isNewTripForm)} + this.isAddTripForm)} {createFormGroup('tripStartDateGroup', 'Start Date', 'date', this.props.defaultFormObj.startDate, this.startDateRef, - isNewTripForm)} + this.isAddTripForm)} {createFormGroup('tripEndDateGroup', 'End Date', 'date', this.props.defaultFormObj.endDate, this.endDateRef, - isNewTripForm)} + this.isAddTripForm)} {createFormGroup('tripCollabsGroup', 'Trip Collaborators', 'emails', this.props.defaultFormObj.collaborators, - this.state.collaboratorsRefArr, isNewTripForm)} + this.state.collaboratorsRefArr, this.isAddTripForm)} From e869354061a241e10f4e89472dd96ce73b241d1d Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Thu, 16 Jul 2020 15:54:06 -0400 Subject: [PATCH 10/23] Revert "Update document writen/overwritten success console.logs." This reverts commit 6ac1b0c870186e6e325525a91b477b7127e056c5. --- frontend/src/components/ViewTrips/create-new-trip.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/src/components/ViewTrips/create-new-trip.js b/frontend/src/components/ViewTrips/create-new-trip.js index 168786e9..03bbf12e 100644 --- a/frontend/src/components/ViewTrips/create-new-trip.js +++ b/frontend/src/components/ViewTrips/create-new-trip.js @@ -121,11 +121,7 @@ function createTrip(db, tripId, rawTripObj) { addTripToFirestore(db, tripId, formattedTripObj) .then(docRef => { - if (tripId === null) { - console.log("Document created with ID: ", docRef.id); - } else { - console.log("Document overwritten with ID: ", tripId); - } + console.log("Document written with ID: ", docRef.id); }) .catch(error => { console.error("Error adding document: ", error); From 15e66a305de93c8d8c80423f68d29a7a8e032f14 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Fri, 17 Jul 2020 13:32:35 -0400 Subject: [PATCH 11/23] single quotes. --- .../components/ViewTrips/save-trip-modal.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 21a33be1..88b14692 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -24,7 +24,7 @@ function createTextFormControl(defaultText, ref, isAddTripForm) { if (isAddTripForm) { return ( @@ -32,7 +32,7 @@ function createTextFormControl(defaultText, ref, isAddTripForm) { } return ( @@ -51,7 +51,7 @@ function createTextFormControl(defaultText, ref, isAddTripForm) { function createDateFormControl(defaultDate, ref) { return ( @@ -73,7 +73,7 @@ function createEmailFormControl(defaultEmail, ref, idx, isAddTripForm) { if (isAddTripForm) { return ( { - console.log("Document written with ID: ", docRef.id); + console.log('Document written with ID: ', docRef.id); }) .catch(error => { - console.error("Error adding document: ", error); + console.error('Error adding document: ', error); }); } @@ -244,10 +244,10 @@ class SaveTripModal extends React.Component { .doc(tripId) .set(tripData) .then(() => { - console.log("Document written with ID: ", tripId); + console.log('Document written with ID: ', tripId); }) .catch(error => { - console.error("Error adding document: ", error); + console.error('Error adding document: ', error); }); } From 20390d6668fe29a2e59c880e1b9b377f4205bb54 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Fri, 17 Jul 2020 14:07:50 -0400 Subject: [PATCH 12/23] Include use of both placeholders and default form values. --- .../components/ViewTrips/save-trip-modal.js | 93 +++++++++++-------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 88b14692..1b6311ed 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -14,18 +14,20 @@ const db = app.firestore(); * Returns a Form.Control element with input type 'text' and other props * specified by the function parameters. * - * @param {string} defaultText Default text value in the form input. * @param {React.RefObject} ref Ref attached to the value inputted in the form. * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. + * @param {string} placeholder Placeholder text value in the form input. + * @param {!string=} defaultText Optional default text value in the form input. * @return {JSX.Element} The Form.Control element. */ -function createTextFormControl(defaultText, ref, isAddTripForm) { +function createTextFormControl(ref, isAddTripForm, + placeholder, defaultText = null) { if (isAddTripForm) { return ( ); @@ -33,6 +35,7 @@ function createTextFormControl(defaultText, ref, isAddTripForm) { return ( @@ -43,12 +46,12 @@ function createTextFormControl(defaultText, ref, isAddTripForm) { * Returns a Form.Control element with input type 'date' and other props * specified by the function parameters. * - * @param {string} defaultDate Default ISO date string in the form input. - * @param {React.RefObject} refArr The list of refs attached to the emails - * inputted in the form. + * @param {React.RefObject} ref Ref attached to the date inputted in the form. + * @param {string=} defaultDate Optional default ISO date string placed in the + * form input. * @return {JSX.Element} The Form.Control element. */ -function createDateFormControl(defaultDate, ref) { +function createDateFormControl(ref, defaultDate = '') { return ( =} defaultEmailArr Array of the emails to be displayed + * in the default form fields. * @return {JSX.Element} The Form.Control element. */ -function createEmailFormControl(defaultEmail, ref, idx, isAddTripForm) { +function createEmailFormControl(ref, idx, isAddTripForm, + placeholder, defaultEmailArr = null) { if (isAddTripForm) { return ( @@ -83,7 +89,8 @@ function createEmailFormControl(defaultEmail, ref, idx, isAddTripForm) { return ( @@ -101,20 +108,22 @@ function createEmailFormControl(defaultEmail, ref, idx, isAddTripForm) { * * TODO(Issue #72): More intuitive remove collaborator when !`isAddTripForm`. * - * @param {!Array} defaultEmailArr Array of the emails to be displayed - * in the default form fields. * @param {!Array} refArr Array of refs attached to the * emails inputted in the form. * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. + * @param {string} placeholder Placeholder text value in the form input. + * @param {!Array=} defaultEmailArr Array of the emails to be displayed + * in the default form fields. * @return {JSX.Element} The Form.Control elements. */ -function createMultiFormControl(defaultEmailArr, refArr, isAddTripForm) { +function createMultiFormControl(refArr, isAddTripForm, + placeholder, defaultEmailArr = null) { return ( <> {refArr.map((ref, idx) => - createEmailFormControl(defaultEmailArr[idx + 1], - ref, idx, isAddTripForm) + createEmailFormControl(ref, idx, isAddTripForm, + placeholder, defaultEmailArr) )} ); @@ -127,25 +136,27 @@ function createMultiFormControl(defaultEmailArr, refArr, isAddTripForm) { * input prop. * @param {string} formLabel Label/title for the form input. * @param {string} inputType Input type of the form. - * @param {string} defaultVal Default value in the form input. * @param {React.RefObject} ref Ref attached to the values inputted in the form. - * @param {string} subFormText Subtext instructions under a form input. * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. + * @param {string} placeholder Placeholder text value in the form input. + * @param {string} defaultVal Default value in the form input. * @return {JSX.Element} The Form.Group element. */ function createFormGroup(controlId, formLabel, inputType, - defaultVal, ref, isAddTripForm) { + ref, isAddTripForm, placeholder, defaultVal) { let formControl; switch(inputType) { case 'text': - formControl = createTextFormControl(defaultVal, ref, isAddTripForm); + formControl = createTextFormControl(ref, isAddTripForm, + placeholder, defaultVal); break; case 'date': - formControl = createDateFormControl(defaultVal, ref); + formControl = createDateFormControl(ref, defaultVal); break; case 'emails': - formControl = createMultiFormControl(defaultVal, ref, isAddTripForm); + formControl = createMultiFormControl(ref, isAddTripForm, + placeholder, defaultVal); break; default: console.error('There should be no other input type') @@ -198,14 +209,18 @@ class SaveTripModal extends React.Component { this.isAddTripForm = this.props.tripId === null; - // Create the number of collaborator input box refs as one less than the - // number of collaborators specified in prop `defaultFormObj` (do not - // include current user in list) + // For edit trips, create the number of collaborator input box refs as one + // less than the number of collaborators specified in prop `defaultFormObj` + // (do not include current user in list). // // TODO(Issue 71): Give user option to remove themself as collab. from trip. const collaboratorsRefArr = []; - for (let i = 1; i < this.props.defaultFormObj.collaborators.length; i++) { - collaboratorsRefArr.push(React.createRef()) + if (this.isAddTripForm) { + collaboratorsRefArr.push(React.createRef()); + } else { + for (let i = 1; i < this.props.defaultFormObj.collaborators.length; i++) { + collaboratorsRefArr.push(React.createRef()) + } } this.state = { collaboratorsRefArr: collaboratorsRefArr } } @@ -306,23 +321,23 @@ class SaveTripModal extends React.Component { {createFormGroup('tripNameGroup', 'Trip Name', 'text', - this.props.defaultFormObj.name, this.nameRef, - this.isAddTripForm)} + this.nameRef, this.isAddTripForm, 'Enter Trip Name', + this.props.defaultFormObj.name)} {createFormGroup('tripDescGroup', 'Trip Description', 'text', - this.props.defaultFormObj.description, this.descriptionRef, - this.isAddTripForm)} + this.descriptionRef, this.isAddTripForm, + 'Enter Trip Description', this.props.defaultFormObj.description)} {createFormGroup('tripDestGroup', 'Trip Destination', 'text', - this.props.defaultFormObj.destination, this.destinationRef, - this.isAddTripForm)} + this.destinationRef, this.isAddTripForm, + 'Enter Trip Destination', this.props.defaultFormObj.destination)} {createFormGroup('tripStartDateGroup', 'Start Date', 'date', - this.props.defaultFormObj.startDate, this.startDateRef, - this.isAddTripForm)} + this.startDateRef, this.isAddTripForm, '', + this.props.defaultFormObj.startDate)} {createFormGroup('tripEndDateGroup', 'End Date', 'date', - this.props.defaultFormObj.endDate, this.endDateRef, - this.isAddTripForm)} + this.endDateRef, this.isAddTripForm, '', + this.props.defaultFormObj.endDate)} {createFormGroup('tripCollabsGroup', 'Trip Collaborators', 'emails', - this.props.defaultFormObj.collaborators, - this.state.collaboratorsRefArr, this.isAddTripForm)} + this.state.collaboratorsRefArr, this.isAddTripForm, + 'person@email.xyz', this.props.defaultFormObj.collaborators)} From 4de61bb3e994498314612282f8a9fb73e1b174e6 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Fri, 17 Jul 2020 14:10:16 -0400 Subject: [PATCH 13/23] Remove placeholder values from defaultFormObj for adding trips. --- frontend/src/components/ViewTrips/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/ViewTrips/index.js b/frontend/src/components/ViewTrips/index.js index 14d52e9f..227bb0c8 100644 --- a/frontend/src/components/ViewTrips/index.js +++ b/frontend/src/components/ViewTrips/index.js @@ -76,13 +76,13 @@ class ViewTrips extends React.Component { this.setState({ tripId: null, defaultFormObj: { - name: 'Enter Trip Name', - description: 'Enter Trip Description', - destination: 'Enter Trip Destination', - startDate: '', - endDate: '', - collaborators: ['person@email.xyz'] - } + name: null, + description: null, + destination: null, + startDate: null, + endDate: null, + collaborators: [] + } }); this.showSaveTripModal(); } From f069221985956fd0470a5a197e88e882c16a93c3 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Fri, 17 Jul 2020 17:19:15 -0400 Subject: [PATCH 14/23] Add timestampToISOString to time utils. --- frontend/src/components/Utils/time.js | 10 ++++++++++ frontend/src/components/ViewTrips/trip.js | 13 ++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Utils/time.js b/frontend/src/components/Utils/time.js index fe354c26..dd88f315 100644 --- a/frontend/src/components/Utils/time.js +++ b/frontend/src/components/Utils/time.js @@ -73,3 +73,13 @@ export function getTimestampFromDateString(dateStr) { const date = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]); return firebase.firestore.Timestamp.fromDate(date); } + +/** + * Returns a date string in ISO format. + * + * @param {firebase.firestore.Timestamp} timestamp Firestore timestamp object. + * @return {string} ISO formatted date string. + */ +export function timestampToISOString(timestamp) { + return timestamp.toDate().toISOString().substring(0,10); +} diff --git a/frontend/src/components/ViewTrips/trip.js b/frontend/src/components/ViewTrips/trip.js index 446ff813..09fbe117 100644 --- a/frontend/src/components/ViewTrips/trip.js +++ b/frontend/src/components/ViewTrips/trip.js @@ -2,7 +2,8 @@ import React from 'react'; import Button from 'react-bootstrap/Button'; -import { getUserEmailFromUid } from '../Utils/temp-auth-utils.js' +import { timestampToISOString } from '../Utils/time.js'; +import { getUserEmailFromUid } from '../Utils/temp-auth-utils.js'; import ViewActivitiesButton from './view-activities-button.js'; /** @@ -27,16 +28,18 @@ export function getDateRange(tripData) { `${endDate.getDate()}/${endDate.getFullYear()}`; } +/** + * Returns a string with comma separated collaborator emails. + * + * @param {!Array} collaboratorUidArr Array of collaborator uids. + * @return {string} Comma separated string of collaborator emails. + */ export function getCollaboratorEmails(collaboratorUidArr) { const collaboratorEmailArr = collaboratorUidArr.map(uid => getUserEmailFromUid(uid)); return collaboratorEmailArr.join(', '); } -function timestampToISOString(timestamp) { - return timestamp.toDate().toISOString().substring(0,10); -} - /** * Component corresponding to the container containing an individual trip. * From de6f5a9d270cb8cd3f639cde47c1310099fcd6ba Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Fri, 17 Jul 2020 17:23:12 -0400 Subject: [PATCH 15/23] Improve JSDoc for time util timestamp <--> iso date string functions. --- frontend/src/components/Utils/time.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Utils/time.js b/frontend/src/components/Utils/time.js index dd88f315..6b71e246 100644 --- a/frontend/src/components/Utils/time.js +++ b/frontend/src/components/Utils/time.js @@ -61,7 +61,7 @@ export function timestampToFormatted(msTimestamp, timezone = "America/New_York") /** * Return a Firestore Timestamp corresponding to the date in `dateStr`. * - * @param {string} dateStr String containing a date in the form 'yyyy-mm-dd'. + * @param {string} dateStr String containing a date in the form 'YYYY-MM-DD'. * @return {firebase.firestore.Timestamp} Firestore timestamp object created. */ export function getTimestampFromDateString(dateStr) { @@ -75,10 +75,10 @@ export function getTimestampFromDateString(dateStr) { } /** - * Returns a date string in ISO format. + * Formats a Firestore timestamp into a date string in ISO format. * * @param {firebase.firestore.Timestamp} timestamp Firestore timestamp object. - * @return {string} ISO formatted date string. + * @return {string} ISO formatted date string: "YYYY-MM-DD or 2020-05-12". */ export function timestampToISOString(timestamp) { return timestamp.toDate().toISOString().substring(0,10); From b95c3ab460652bea38e34b24b4691aa5c2cbe42d Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Tue, 21 Jul 2020 13:01:22 -0400 Subject: [PATCH 16/23] Chang logic for the default form object for add trip. --- frontend/src/components/ViewTrips/index.js | 18 +--- .../components/ViewTrips/save-trip-modal.js | 87 ++++++++----------- 2 files changed, 39 insertions(+), 66 deletions(-) diff --git a/frontend/src/components/ViewTrips/index.js b/frontend/src/components/ViewTrips/index.js index 227bb0c8..d12d0ef8 100644 --- a/frontend/src/components/ViewTrips/index.js +++ b/frontend/src/components/ViewTrips/index.js @@ -19,14 +19,7 @@ class ViewTrips extends React.Component { refreshTripsContainer: false, refreshSaveTripModal: false, tripId: null, - defaultFormObj: { - name: null, - description: null, - destination: null, - startDate: null, - endDate: null, - collaborators: [] - } + defaultFormObj: null, }; } @@ -75,14 +68,7 @@ class ViewTrips extends React.Component { showAddTripModal = () => { this.setState({ tripId: null, - defaultFormObj: { - name: null, - description: null, - destination: null, - startDate: null, - endDate: null, - collaborators: [] - } + defaultFormObj: null, }); this.showSaveTripModal(); } diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index 7e07319a..e5da1e81 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -3,7 +3,7 @@ import React from 'react'; import app from '../Firebase'; import { Button, Modal, Form } from 'react-bootstrap'; -import { COLLECTION_TRIPS } from '../../constants/database.js'; +import * as DB from '../../constants/database.js'; import { formatTripData } from '../Utils/filter-input.js'; const db = app.firestore(); @@ -13,23 +13,12 @@ const db = app.firestore(); * specified by the function parameters. * * @param {React.RefObject} ref Ref attached to the value inputted in the form. - * @param {boolean} isAddTripForm True if form is adding new trip, false if - * form is editting existing trip. * @param {string} placeholder Placeholder text value in the form input. - * @param {!string=} defaultText Optional default text value in the form input. + * @param {?string} defaultText Optional default text value in the form input. + * Null if no default text. * @return {JSX.Element} The Form.Control element. */ -function createTextFormControl(ref, isAddTripForm, - placeholder, defaultText = null) { - if (isAddTripForm) { - return ( - - ); - } +function createTextFormControl(ref, placeholder, defaultText) { return ( =} defaultEmailArr Array of the emails to be displayed - * in the default form fields. + * @param {?Array} defaultEmailArr Array of the emails to be displayed + * in the default form fields. Null if no default emails. * @return {JSX.Element} The Form.Control element. */ -function createEmailFormControl(ref, idx, isAddTripForm, - placeholder, defaultEmailArr = null) { - if (isAddTripForm) { +function createEmailFormControl(ref, idx, placeholder, defaultEmailArr) { + if (defaultEmailArr === null) { return ( {refArr.map((ref, idx) => - createEmailFormControl(ref, idx, isAddTripForm, - placeholder, defaultEmailArr) + createEmailFormControl(ref, idx, placeholder, defaultEmailArr) )} ); @@ -135,26 +119,22 @@ function createMultiFormControl(refArr, isAddTripForm, * @param {string} formLabel Label/title for the form input. * @param {string} inputType Input type of the form. * @param {React.RefObject} ref Ref attached to the values inputted in the form. - * @param {boolean} isAddTripForm True if form is adding new trip, false if - * form is editting existing trip. * @param {string} placeholder Placeholder text value in the form input. * @param {string} defaultVal Default value in the form input. * @return {JSX.Element} The Form.Group element. */ function createFormGroup(controlId, formLabel, inputType, - ref, isAddTripForm, placeholder, defaultVal) { + ref, placeholder, defaultVal) { let formControl; switch(inputType) { case 'text': - formControl = createTextFormControl(ref, isAddTripForm, - placeholder, defaultVal); + formControl = createTextFormControl(ref, placeholder, defaultVal); break; case 'date': formControl = createDateFormControl(ref, defaultVal); break; case 'emails': - formControl = createMultiFormControl(ref, isAddTripForm, - placeholder, defaultVal); + formControl = createMultiFormControl(ref, placeholder, defaultVal); break; default: console.error('There should be no other input type') @@ -236,7 +216,7 @@ class SaveTripModal extends React.Component { * @param {Object} tripData Data the new trip document will contain. */ addNewTrip(tripData) { - db.collection(COLLECTION_TRIPS) + db.collection(DB.COLLECTION_TRIPS) .add(tripData) .then(docRef => { console.log('Document written with ID: ', docRef.id); @@ -253,7 +233,7 @@ class SaveTripModal extends React.Component { * @param {Object} tripData Data the new trip document will contain. */ updateExistingTrip(tripId, tripData) { - db.collection(COLLECTION_TRIPS) + db.collection(DB.COLLECTION_TRIPS) .doc(tripId) .set(tripData) .then(() => { @@ -308,6 +288,13 @@ class SaveTripModal extends React.Component { return 'Edit Trip'; } + getDefaultFormField = (field) => { + if (this.isAddTripForm) { + return null; + } + return this.props.defaultFormObj[field]; + } + /** @inheritdoc */ render() { return ( @@ -319,23 +306,23 @@ class SaveTripModal extends React.Component { {createFormGroup('tripNameGroup', 'Trip Name', 'text', - this.nameRef, this.isAddTripForm, 'Enter Trip Name', - this.props.defaultFormObj.name)} + this.nameRef, 'Enter Trip Name', + this.getDefaultFormField(DB.TRIPS_NAME))} {createFormGroup('tripDescGroup', 'Trip Description', 'text', - this.descriptionRef, this.isAddTripForm, - 'Enter Trip Description', this.props.defaultFormObj.description)} + this.descriptionRef, 'Enter Trip Description', + this.getDefaultFormField(DB.TRIPS_DESCRIPTION))} {createFormGroup('tripDestGroup', 'Trip Destination', 'text', - this.destinationRef, this.isAddTripForm, - 'Enter Trip Destination', this.props.defaultFormObj.destination)} + this.destinationRef, 'Enter Trip Destination', + this.getDefaultFormField(DB.TRIPS_DESTINATION))} {createFormGroup('tripStartDateGroup', 'Start Date', 'date', - this.startDateRef, this.isAddTripForm, '', - this.props.defaultFormObj.startDate)} + this.startDateRef, '', + this.getDefaultFormField(DB.TRIPS_START_DATE))} {createFormGroup('tripEndDateGroup', 'End Date', 'date', - this.endDateRef, this.isAddTripForm, '', - this.props.defaultFormObj.endDate)} + this.endDateRef, '', + this.getDefaultFormField(DB.TRIPS_END_DATE))} {createFormGroup('tripCollabsGroup', 'Trip Collaborators', 'emails', - this.state.collaboratorsRefArr, this.isAddTripForm, - 'person@email.xyz', this.props.defaultFormObj.collaborators)} + this.state.collaboratorsRefArr,'person@email.xyz', + this.getDefaultFormField(DB.TRIPS_COLLABORATORS))} From da8225463c83ed2bddb1f3e1cfe73d6e47216463 Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Tue, 21 Jul 2020 13:16:50 -0400 Subject: [PATCH 17/23] Add documentation to getDefaultFormField. --- frontend/src/components/ViewTrips/save-trip-modal.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/components/ViewTrips/save-trip-modal.js b/frontend/src/components/ViewTrips/save-trip-modal.js index e5da1e81..d0a3543d 100644 --- a/frontend/src/components/ViewTrips/save-trip-modal.js +++ b/frontend/src/components/ViewTrips/save-trip-modal.js @@ -288,6 +288,13 @@ class SaveTripModal extends React.Component { return 'Edit Trip'; } + /** Returns the default form value for the trip field specified by `field`. + * + * @param {!string} field A trip document field + * (the constants in `database.js`). + * @return {?string} Default form value for edit trip modal or null for + * add trip modals. + */ getDefaultFormField = (field) => { if (this.isAddTripForm) { return null; From b8724908a4ca0c5479a3deddc1acfbb612dd75af Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Tue, 21 Jul 2020 15:45:54 -0400 Subject: [PATCH 18/23] Change trip struct/object to use same fields as in database.js to ensure default value works for date form inputs. --- frontend/src/components/ViewTrips/trip.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/ViewTrips/trip.js b/frontend/src/components/ViewTrips/trip.js index 1de3ea8d..08e74fc8 100644 --- a/frontend/src/components/ViewTrips/trip.js +++ b/frontend/src/components/ViewTrips/trip.js @@ -62,12 +62,12 @@ const Trip = (props) => { const collaboratorEmailsStr = getCollaboratorEmails(props.tripData.collaborators); - const placeholderObj = { + const formattedTripData = { name: name, description: description, destination: destination, - startDate: timestampToISOString(props.tripData.start_date), - endDate: timestampToISOString(props.tripData.end_date), + start_date: timestampToISOString(props.tripData.start_date), + end_date: timestampToISOString(props.tripData.end_date), collaborators: collaboratorEmailsStr.split(', ') }; @@ -81,7 +81,7 @@ const Trip = (props) => { From 3bca8252de9dc623e313b8f95743e2954ee155cf Mon Sep 17 00:00:00 2001 From: Zach Ghera Date: Wed, 22 Jul 2020 19:05:04 -0400 Subject: [PATCH 23/23] Move save trip modal form element creation functions to separate file. --- frontend/src/components/Utils/filter-input.js | 8 ++++---- .../src/components/Utils/temp-auth-utils.js | 12 +++++------ frontend/src/components/Utils/time.js | 16 +++++++-------- .../ViewTrips/save-trip-form-elements.js | 20 +++++++++---------- frontend/src/components/ViewTrips/trip.js | 6 +++--- .../components/ViewTrips/trips-container.js | 2 +- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/frontend/src/components/Utils/filter-input.js b/frontend/src/components/Utils/filter-input.js index 0ce01d56..be16a313 100644 --- a/frontend/src/components/Utils/filter-input.js +++ b/frontend/src/components/Utils/filter-input.js @@ -6,8 +6,8 @@ import { getTimestampFromDateString } from './time.js' /** * Return a string containing the cleaned text input. * - * @param {!string} rawInput String containing raw form input. - * @return {!string} Cleaned string. + * @param {string} rawInput String containing raw form input. + * @return {string} Cleaned string. */ export function getCleanedTextInput(rawInput, defaultValue) { return rawInput === '' ? defaultValue : rawInput; @@ -20,9 +20,9 @@ export function getCleanedTextInput(rawInput, defaultValue) { * TODO(#72 & #67): Remove 'remove empty fields' once there is better way to * remove collaborators (#72) and there is email validation (#67). * - * @param {!Array{!string}} collaboratorEmailsArr Array of emails corresponding + * @param {!Array{string}} collaboratorEmailsArr Array of emails corresponding * to the collaborators of the trip (not including the trip creator email). - * @return {!Array{!string}} Array of all collaborator uids (including trip + * @return {!Array{string}} Array of all collaborator uids (including trip * creator uid). */ export function getCollaboratorUidArray(collaboratorEmailArr) { diff --git a/frontend/src/components/Utils/temp-auth-utils.js b/frontend/src/components/Utils/temp-auth-utils.js index de3ab447..a9ba28e0 100644 --- a/frontend/src/components/Utils/temp-auth-utils.js +++ b/frontend/src/components/Utils/temp-auth-utils.js @@ -10,7 +10,7 @@ /** * Temporary hardcoded function that returns the current users email. * - * @return {!string} Hardcoded user email string. + * @return {string} Hardcoded user email string. */ export function getCurUserEmail() { return 'matt.murdock'; @@ -19,7 +19,7 @@ export function getCurUserEmail() { /** * Temporary hardcoded function that returns the current users uid. * - * @return {!string} Hardcoded user uid string. + * @return {string} Hardcoded user uid string. */ export function getCurUserUid() { return getUserUidFromUserEmail(getCurUserEmail()); @@ -29,8 +29,8 @@ export function getCurUserUid() { * Temporary hardcoded function that returns the user's uid given the user's * email. * - * @param {!string} userEmail A users email. - * @return {!string} The 'fake' uid associated with the user email that is + * @param {string} userEmail A users email. + * @return {string} The 'fake' uid associated with the user email that is * created with the form '_`userEmail`_'. */ export function getUserUidFromUserEmail(userEmail) { @@ -41,8 +41,8 @@ export function getUserUidFromUserEmail(userEmail) { * Temporary hardcoded function that returns the a user's email given the * fake uid that was stored in the Trip document. * - * @param {!string} uid Fake string uid that is in the form '_userEmail_'. - * @return {!string} The email corresponding to the fake uid. + * @param {string} uid Fake string uid that is in the form '_userEmail_'. + * @return {string} The email corresponding to the fake uid. */ export function getUserEmailFromUid(uid) { return uid.substring(1, uid.length - 1); diff --git a/frontend/src/components/Utils/time.js b/frontend/src/components/Utils/time.js index cdb7f8ac..6b71e246 100644 --- a/frontend/src/components/Utils/time.js +++ b/frontend/src/components/Utils/time.js @@ -4,8 +4,8 @@ import * as firebase from 'firebase/app'; * Format a timestamp (in milliseconds) into a pretty string with just the time. * * @param {int} msTimestamp - * @param {!string} timezone - * @returns {!string} Time formatted into a string like '10:19 AM'. + * @param {string} timezone + * @returns {string} Time formatted into a string like '10:19 AM'. */ export function timestampToTimeFormatted(msTimestamp, timezone = 'America/New_York') { const date = new Date(msTimestamp); @@ -21,8 +21,8 @@ export function timestampToTimeFormatted(msTimestamp, timezone = 'America/New_Yo * Format a timestamp (in milliseconds) into a pretty string with just the date. * * @param {int} msTimestamp - * @param {!string} timezone - * @returns {!string} Time formatted into a string like 'Monday, January 19, 1970'. + * @param {string} timezone + * @returns {string} Time formatted into a string like 'Monday, January 19, 1970'. */ export function timestampToDateFormatted(msTimestamp, timezone='America/New_York') { const date = new Date(msTimestamp); @@ -40,8 +40,8 @@ export function timestampToDateFormatted(msTimestamp, timezone='America/New_York * Format a timestamp (in milliseconds) into a pretty string. * * @param {int} msTimestamp - * @param {!string} timezone - * @returns {!string} Time formatted into a string like + * @param {string} timezone + * @returns {string} Time formatted into a string like * "Monday, January 19, 1970, 02:48 AM" */ export function timestampToFormatted(msTimestamp, timezone = "America/New_York") { @@ -61,7 +61,7 @@ export function timestampToFormatted(msTimestamp, timezone = "America/New_York") /** * Return a Firestore Timestamp corresponding to the date in `dateStr`. * - * @param {!string} dateStr String containing a date in the form 'YYYY-MM-DD'. + * @param {string} dateStr String containing a date in the form 'YYYY-MM-DD'. * @return {firebase.firestore.Timestamp} Firestore timestamp object created. */ export function getTimestampFromDateString(dateStr) { @@ -78,7 +78,7 @@ export function getTimestampFromDateString(dateStr) { * Formats a Firestore timestamp into a date string in ISO format. * * @param {firebase.firestore.Timestamp} timestamp Firestore timestamp object. - * @return {!string} ISO formatted date string: "YYYY-MM-DD or 2020-05-12". + * @return {string} ISO formatted date string: "YYYY-MM-DD or 2020-05-12". */ export function timestampToISOString(timestamp) { return timestamp.toDate().toISOString().substring(0,10); diff --git a/frontend/src/components/ViewTrips/save-trip-form-elements.js b/frontend/src/components/ViewTrips/save-trip-form-elements.js index 710fb90f..e327c91f 100644 --- a/frontend/src/components/ViewTrips/save-trip-form-elements.js +++ b/frontend/src/components/ViewTrips/save-trip-form-elements.js @@ -7,7 +7,7 @@ import { Form } from 'react-bootstrap'; * specified by the function parameters. * * @param {React.RefObject} ref Ref attached to the value inputted in the form. - * @param {!string} placeholder Placeholder text value in the form input. + * @param {string} placeholder Placeholder text value in the form input. * @param {?string} defaultText Optional default text value in the form input. * Null if no default text. * @return {JSX.Element} The Form.Control element. @@ -48,8 +48,8 @@ function createDateFormControl(ref, defaultDate) { * * @param {React.RefObject} ref Ref attached to the value inputted in the form. * @param {number} idx Index of the email Form.Control used for key prop. - * @param {!string} placeholder Placeholder text value in the form input. - * @param {?Array} defaultEmailArr Array of the emails to be displayed + * @param {string} placeholder Placeholder text value in the form input. + * @param {?Array} defaultEmailArr Array of the emails to be displayed * in the default form fields. Null if no default emails. * @return {JSX.Element} The Form.Control element. */ @@ -90,8 +90,8 @@ function createEmailFormControl(ref, idx, placeholder, defaultEmailArr) { * emails inputted in the form. * @param {boolean} isAddTripForm True if form is adding new trip, false if * form is editting existing trip. - * @param {!string} placeholder Placeholder text value in the form input. - * @param {?Array} defaultEmailArr Array of the emails to be displayed + * @param {string} placeholder Placeholder text value in the form input. + * @param {?Array} defaultEmailArr Array of the emails to be displayed * in the default form fields. * @return {JSX.Element} The Form.Control elements. */ @@ -108,13 +108,13 @@ function createMultiFormControl(refArr, placeholder, defaultEmailArr) { /** * Returns a Form.Group element with components specified by the input args. * - * @param {!string} controlId Prop that accessibly wires the nested label and + * @param {string} controlId Prop that accessibly wires the nested label and * input prop. - * @param {!string} formLabel Label/title for the form input. - * @param {!string} inputType Input type of the form. + * @param {string} formLabel Label/title for the form input. + * @param {string} inputType Input type of the form. * @param {!React.RefObject} ref Ref attached to the values inputted in the form. - * @param {!string} placeholder Placeholder text value in the form input. - * @param {?string|?Array} defaultVal Default value in the form input. + * @param {string} placeholder Placeholder text value in the form input. + * @param {?string|?Array} defaultVal Default value in the form input. * @return {JSX.Element} The Form.Group element. */ export function createFormGroup(controlId, formLabel, inputType, diff --git a/frontend/src/components/ViewTrips/trip.js b/frontend/src/components/ViewTrips/trip.js index c2af6dc0..92641d92 100644 --- a/frontend/src/components/ViewTrips/trip.js +++ b/frontend/src/components/ViewTrips/trip.js @@ -18,7 +18,7 @@ import ViewActivitiesButton from './view-activities-button.js'; * * @param {!firebase.firestore.DocumentData} tripData Object containing the * fields and values for a Trip document. - * @return {!string} Date range of the trip. + * @return {string} Date range of the trip. */ export function getDateRange(tripData) { const startDate = tripData.start_date.toDate(); @@ -30,9 +30,9 @@ export function getDateRange(tripData) { /** * - * @param {!Array} collaboratorUidArr Array of collaborator uids + * @param {!Array} collaboratorUidArr Array of collaborator uids * stored in trip document. - * @returns {!string} Collaborator emails in comma separated string. + * @returns {string} Collaborator emails in comma separated string. * Ex: "person1@email.com, person2@email.com". */ export function getCollaboratorEmails(collaboratorUidArr) { diff --git a/frontend/src/components/ViewTrips/trips-container.js b/frontend/src/components/ViewTrips/trips-container.js index 99da66de..0f61d8ac 100644 --- a/frontend/src/components/ViewTrips/trips-container.js +++ b/frontend/src/components/ViewTrips/trips-container.js @@ -53,7 +53,7 @@ function serveTrips(querySnapshot, handleEditTrip) { /** * Returns a `
` element with the specified error message. * - * @param {!string} error Error message in `componentDidMount` catch statement. + * @param {string} error Error message in `componentDidMount` catch statement. * @return {Promise} Promise object containing a `
` element * with the error message `error` inside. */