Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Trip Functionality #54

Merged
merged 51 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
1f318cc
Add functionality to add trips to firestore.
zghera Jul 12, 2020
b092c8d
Add functionality to re-render trips container when new trip is added.
zghera Jul 12, 2020
42f141f
Move Auth hard-coded functions into components that use it rather tha…
zghera Jul 13, 2020
a415a18
Add documentation for all functions.
zghera Jul 13, 2020
ec9788d
Merge branch 'create-add-trip-modal' into add-trip-functionality
zghera Jul 13, 2020
766ec62
Merge branch 'create-add-trip-modal' into add-trip-functionality
zghera Jul 13, 2020
57f2ea7
Add option in add trips form to add new collaborator.
zghera Jul 14, 2020
11ded33
Add date form control UI element and functionality.
zghera Jul 14, 2020
ce990d7
Update createFormGroup swtich statement default error statement.
zghera Jul 14, 2020
860f086
Sort trips fetched by creation date in descending order for ease of v…
zghera Jul 14, 2020
bb76321
Restructure formatTripData for consistency and testing sake.
zghera Jul 14, 2020
c6d5cf3
Remove empty strings from collaborators.
zghera Jul 15, 2020
f087f07
Move temp auth utils to separate file.
zghera Jul 15, 2020
ab3b68c
Add todo for email verification.
zghera Jul 15, 2020
40679b0
Export all functions.
zghera Jul 15, 2020
afda928
Rename getCollaboratorsArr to be getCollaboratorsArray.
zghera Jul 15, 2020
cd47b9e
Add tests for create-new-trip.js.
zghera Jul 15, 2020
36e21fb
Rename add trip file and event handler for clarity.
zghera Jul 15, 2020
9647e39
Rename component AddTrip to AddTripModal.
zghera Jul 15, 2020
706b123
Touch up JSDoc.
zghera Jul 15, 2020
4b5ce2e
Enfore consistent import statement groupings: React|other external li…
zghera Jul 15, 2020
8ab738d
Re-arrange tests so so the expecteOutput var is at the beginning of t…
zghera Jul 15, 2020
477a537
Re-name back to add-trip.js to fix diff issues.
zghera Jul 15, 2020
67f5ba0
Use git mv to fix diff issues.
zghera Jul 15, 2020
9dc09a6
Revert back to add-trip name with git mv.
zghera Jul 15, 2020
32baba1
Final try: rename add-trip to add-trip-modal with git mv.
zghera Jul 15, 2020
e38bd94
Revert "Final try: rename add-trip to add-trip-modal with git mv."
zghera Jul 15, 2020
4c11683
Update import to add-trip.js.
zghera Jul 15, 2020
5a29c00
Refactor createFormGroup returned component.
zghera Jul 15, 2020
eba0d65
Fix import spacing.
zghera Jul 15, 2020
c8c6f31
Fix bug where description and destination were flipped in the formatt…
zghera Jul 16, 2020
8ac2902
Place destination before description in UI.
zghera Jul 16, 2020
f5afedd
Instantiating firestore instance at top of files that need it over pa…
zghera Jul 16, 2020
2d1223f
Change 'close' button text to 'cancel'.
zghera Jul 16, 2020
138a798
Add todo to remove removing empty fields once issue 67 and 72 are fixed.
zghera Jul 16, 2020
2afd509
Move input filtering function to Utils and place trip creation in add…
zghera Jul 17, 2020
cea76ef
Update JSDoc.
zghera Jul 17, 2020
ba25731
Merge branch 'master' into add-trip-functionality
zghera Jul 17, 2020
9b3d830
Remove extra import line in filter-input.js.
zghera Jul 17, 2020
2a18ff8
Fix styling of objects.
zghera Jul 17, 2020
4fca011
Merge branch 'master' into add-trip-functionality
zghera Jul 17, 2020
b49fbb3
Merge branch 'master' into add-trip-functionality
zghera Jul 17, 2020
879f184
Move getTimestampFromDateString from filter-input to time utils.
zghera Jul 17, 2020
ae1dc69
Change import in filter input.
zghera Jul 17, 2020
96b89c2
Use condensed import for react-bootstrap.
zghera Jul 17, 2020
cc4df5e
Include/name all functions for temp-auth-utils the same as specified …
zghera Jul 17, 2020
5fefe81
Fix JSDoc for getCuruserUid.
zghera Jul 17, 2020
32bf05b
Fix import to correct function name so tests pass.
zghera Jul 17, 2020
f714716
Add function comment for getCollaboratorEmails.
zghera Jul 21, 2020
b147244
Add issues to 'temporary' comment.
zghera Jul 21, 2020
9d2b0b7
Merge branch 'master' into add-trip-functionality
zghera Jul 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions frontend/src/components/Utils/filter-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as firebase from 'firebase/app';

import { getCurUserEmail, getUserUidFromUserEmail } from './temp-auth-utils.js'
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.
*/
export function getCleanedTextInput(rawInput, defaultValue) {
return rawInput === '' ? defaultValue : rawInput;
}

/**
* Return an array of collaborator uids given the emails provided in the
* add trip form.
*
* 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
* to the collaborators of the trip (not including the trip creator email).
* @return {!Array{string}} Array of all collaborator uids (including trip
* creator uid).
*/
export function getCollaboratorUidArray(collaboratorEmailArr) {
collaboratorEmailArr = [getCurUserEmail()].concat(collaboratorEmailArr);

// Removes empty fields (temporary until fix #67 & #72).
while (collaboratorEmailArr.includes('')) {
const emptyStrIdx = collaboratorEmailArr.indexOf('');
collaboratorEmailArr.splice(emptyStrIdx, 1);
}
return collaboratorEmailArr
.map(userEmail => getUserUidFromUserEmail(userEmail));
}

/**
* Returns a formatted and cleaned trip object that will be used as the data
* for the created Trip document.
*
* We know that rawTripObj will contain all of the necessary fields because each
* key-value pair is explicitly included. This means, only the value
* corresponding to each key needs to be checked.
* For text element inputs, React has built in protections against injection/XSS
* attacks. Thus, no sanitization is needed for text inputs besides providing a
* default value in a Trip field where applicable.
*
* @param {Object} rawTripObj A JS Object containing the raw form data from the
* add trip form.
* @return {Object} Formatted/cleaned version of `rawTripObj` holding the data
* for the new Trip document that is to be created.
*/
export function formatTripData(rawTripObj) {
const defaultName = "Untitled Trip";
const defaultDestination = "No Destination"

const formattedTripObj = {
trip_creation_time: firebase.firestore.Timestamp.now(),
name: getCleanedTextInput(rawTripObj.name, defaultName),
description: rawTripObj.description,
destination: getCleanedTextInput(rawTripObj.destination,
defaultDestination),
start_date: getTimestampFromDateString(rawTripObj.startDate),
end_date: getTimestampFromDateString(rawTripObj.endDate),
collaborators: getCollaboratorUidArray(rawTripObj.collaboratorEmails),
};

return formattedTripObj;
}
67 changes: 67 additions & 0 deletions frontend/src/components/Utils/filter-input.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getUserUidFromUserEmail } from './temp-auth-utils';
import { getCleanedTextInput, getCollaboratorUidArray } from './filter-input.js';

describe('getCleanedTextInput tests', () => {
test('No input entered in form (empty string)', () => {
const testDefaultValue = 'Untitled Trip';
const testRawName = '';
const expectedTripName = testDefaultValue;

const testTripName = getCleanedTextInput(testRawName, testDefaultValue);

expect(testTripName).toEqual(expectedTripName);
});

test('Input entered into form', () => {
const testDefaultValue = 'Untitled Trip';
const testRawName = 'Trip to No Man\'s Land';
const expectedTripName = testRawName;

const testTripName = getCleanedTextInput(testRawName, testDefaultValue);

expect(testTripName).toEqual(expectedTripName);
});
});

const mockCurUserEmail = '[email protected]';
// TODO(Issue #55): Replace mock with real auth file once integrated.
jest.mock('./temp-auth-utils.js', () => ({
getCurUserEmail: () => mockCurUserEmail,
getUserUidFromUserEmail: (userEmail) => '_' + userEmail + '_',
}));
describe('getCollaboratorUidArray tests', () => {
test('No collaborators entered', () => {
const expectedUidArr = [getUserUidFromUserEmail(mockCurUserEmail)];
// This is the list that is created when there are no collaborators added
// (automatically one empty string from the constructor created ref).
const testEmailArr = [''];

const testUidArr = getCollaboratorUidArray(testEmailArr);

expect(testUidArr).toEqual(expectedUidArr);
});

test('Some added collaborators', () => {
const person1Email = '[email protected]';
const person2Email = '[email protected]';
const expectedUidArr = [getUserUidFromUserEmail(mockCurUserEmail),
getUserUidFromUserEmail(person1Email), getUserUidFromUserEmail(person2Email)];
const testEmailArr = [person1Email, person2Email];

const testUidArr = getCollaboratorUidArray(testEmailArr);

expect(testUidArr).toEqual(expectedUidArr);
});

test('Some added collaborators and some blank entries', () => {
const person1Email = '[email protected]';
const person2Email = '[email protected]';
const expectedUidArr = [getUserUidFromUserEmail(mockCurUserEmail),
getUserUidFromUserEmail(person1Email), getUserUidFromUserEmail(person2Email)];
const testEmailArr = ['', person1Email, '', person2Email, ''];

const testUidArr = getCollaboratorUidArray(testEmailArr);

expect(testUidArr).toEqual(expectedUidArr);
});
});
58 changes: 58 additions & 0 deletions frontend/src/components/Utils/temp-auth-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @fileoverview This is a temporary file that is used to implement 'fake'
* versions of the Auth utility functions used in the ViewTrips components.
*
* TODO(Issue 55): Remove this whole file function and replace any imports to
* this file with Auth utils.
*/


/**
* Temporary hardcoded function that returns the current users email.
*
* @return Hardcoded user email string.
*/
export function getCurUserEmail() {
return 'matt.murdock';
}

/**
* Temporary hardcoded function that returns the current users uid.
*
* @return Hardcoded user uid string.
*/
export function getCurUserUid() {
return getUserUidFromUserEmail(getCurUserEmail());
}

/**
* 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
* created with the form '_`userEmail`_'.
*/
export function getUserUidFromUserEmail(userEmail) {
return '_' + 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.
*/
export function getUserEmailFromUid(uid) {
return uid.substring(1, uid.length - 1);
}

const authUtils = {
getCurUserEmail,
getCurUserUid,
getUserUidFromUserEmail,
getUserEmailFromUid
}

export default authUtils;
66 changes: 42 additions & 24 deletions frontend/src/components/Utils/time.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
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
* @param {int} msTimestamp
* @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);
const formatOptions = {
hour: 'numeric',
minute: '2-digit',
const formatOptions = {
hour: 'numeric',
minute: '2-digit',
timeZone: timezone
};
return date.toLocaleTimeString('en-US', formatOptions);;
Expand All @@ -18,40 +20,56 @@ 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
* @param {int} msTimestamp
* @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);
const formatOptions = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
const formatOptions = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
timeZone: timezone
};
return date.toLocaleDateString('en-US', formatOptions);
}

/**
/**
* Format a timestamp (in milliseconds) into a pretty string.
*
* @param {int} msTimestamp
* @param {string} timezone
* @returns {string} Time formatted into a string like
*
* @param {int} msTimestamp
* @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") {
let date = new Date(msTimestamp);
let formatOptions = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
let formatOptions = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
timeZone: timezone
Copy link
Collaborator Author

@zghera zghera Jul 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes were automatically made by VSCode and I am don't know if its worth the time of going into vim and manually changing it back in each branch.

};
return date.toLocaleString('en-US', formatOptions);
}

/**
* Return a Firestore Timestamp corresponding to the date in `dateStr`.
*
* @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) {
const dateParts = dateStr.split('-').map(str => +str);
if (dateParts.length === 1 && dateParts[0] === 0) {
return firebase.firestore.Timestamp.now();
}

const date = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
return firebase.firestore.Timestamp.fromDate(date);
}
37 changes: 36 additions & 1 deletion frontend/src/components/Utils/time.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as firebase from 'firebase/app';
import 'firebase/firebase-firestore';

import * as utils from './time.js';

const TZ_CHICAGO = 'America/Chicago';
const TZ_SINGAPORE = 'Asia/Singapore';
const TZ_SINGAPORE = 'Asia/Singapore';

test('new york date timestamp format', () => {
// Month parameter is zero indexed so it's actually the 10th month.
Expand Down Expand Up @@ -56,3 +59,35 @@ test('other full timestamp format', () => {
expect(actualCentral).toEqual(expectedCentral);
expect(actualSingapore).toEqual(expectedSingapore);
})

const mockTimeNow = 0;
jest.mock('firebase/app', () => ({
firestore: {
Timestamp: {
now: () => mockTimeNow,
fromDate: (date) => date,
}
}
}));
describe('getTimeStampFromDateString tests', () => {
test('No date entered in form', () => {
const expectedTimestamp = mockTimeNow;
const testRawDate = '';

const testTimestamp = utils.getTimestampFromDateString(testRawDate);

expect(testTimestamp).toEqual(expectedTimestamp);
});

test('Date entered in form', () => {
const testDate = new Date(2020, 5, 4); // July 4, 2020
const expectedTimestamp = firebase.firestore.Timestamp.fromDate(testDate);

// This is the type of string (yyyy-mm-dd) that is returned from the form
// input type 'date'.
const testRawDate = testDate.toISOString().substring(0,10);
const testTimestamp = utils.getTimestampFromDateString(testRawDate);

expect(testTimestamp).toEqual(expectedTimestamp);
});
});
Loading