From 56080b3afb92b5f173e3048dbacb1e92ccb561f0 Mon Sep 17 00:00:00 2001 From: Conor McNamara Date: Sun, 19 Mar 2023 11:30:08 +0000 Subject: [PATCH] Add a leaderboard filter, render optimisations and custom errors --- functions/index.js | 21 +++++-- functions/test/index.test.js | 9 ++- src/components/Navbar.vue | 1 + src/pages/AccountPage.vue | 11 +++- src/pages/InsightsPage.vue | 61 ++++++++++++++++---- src/pages/LoginPage.vue | 32 ++++++++++- src/pages/MapPage.vue | 106 ++++++++++++++++++++--------------- src/pages/SignUpPage.vue | 22 +++++++- src/store/store.js | 1 + 9 files changed, 191 insertions(+), 73 deletions(-) diff --git a/functions/index.js b/functions/index.js index 07b70f8..8bbd137 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,3 +1,7 @@ +// exports.scheduledFunction = functions.pubsub.schedule('every 10 minutes').onRun((context) => { + // functions.logger.info("Test log") +// }) + // Firebase imports const functions = require("firebase-functions"); const admin = require('firebase-admin'); @@ -18,13 +22,15 @@ exports.getStationData = functions.https.onRequest((request, response) => { // fetch the "stations" collection admin.firestore().collection('stations').get().then((snapshot) => { if (snapshot.empty) { - response.status(404).send({data: "Error fetching station data from the database"}) + functions.logger.log("Error fetching station data from Firestore") + response.status(404).send({data: "Error fetching station data from Firestore"}) return; } // iterate through each of the collection's documents snapshot.forEach(doc => { jsonData.push(doc.data()); }); + functions.logger.log("Successfully fetched station data from Firestore") response.json({data: jsonData}); }) }); @@ -93,6 +99,7 @@ exports.postStationData = functions.https.onRequest((request, response) => { parseString(res.data, function(err, result) { let jsonData = parseJSON(result) batchWriteDB(request, response, db, jsonData, dartCodes, "Train") + functions.logger.log("Successfully fetched and upload station data from Irish Rail") response.send({data: "Successfully fetched and upload station data from Irish Rail"}) }) }) @@ -113,13 +120,15 @@ exports.getLiveTrainData = functions.https.onRequest((request, response) => { // fetch the "liveTrainData" collection admin.firestore().collection('liveTrainData').get().then((snapshot) => { if (snapshot.empty) { - response.status(404).send({data: "Error fetching live train data from the database"}); + functions.logger.log("Error fetching live train data from Firestore") + response.status(404).send({data: "Successfully fetched live train data from Firestore"}); return; } // iterate through each of the collection's documents snapshot.forEach(doc => { jsonData.push(doc.data()); }); + functions.logger.log("Successfully fetched live train data from Firestore") response.json({data: jsonData}); }) }); @@ -187,6 +196,7 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => { parseString(res.data, function(err, result) { let jsonData = parseJSON(result) batchWriteDB(request, response, db, jsonData, "DART"); + functions.logger.log("Successfully fetched and uploaded live train data from Irish Rail") response.send({data: "Successfully fetched and uploaded live train data from Irish Rail"}); }) }) @@ -203,6 +213,7 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => { exports.getPreferences = functions.https.onCall((data, context) => { if (!context.auth) return "Error request is not verified" return admin.firestore().collection('preferences').doc(context.auth.uid).get().then((preferences) => { + functions.logger.log("Successfully fetched user preferences from Firestore") return preferences.data() }) }) @@ -211,7 +222,8 @@ exports.getPreferences = functions.https.onCall((data, context) => { exports.postPreferences = functions.https.onCall((data, context) => { if (!context.auth) return "Error request is not verified" return admin.firestore().collection('preferences').doc(context.auth.uid).set({data}).then(() => { - return "Successfully saved preferences" + functions.logger.log("Successfully saved user preferences into Firestore") + return "Successfully saved user preferences into Firestore" }) }) @@ -219,6 +231,7 @@ exports.postPreferences = functions.https.onCall((data, context) => { exports.deletePreferences = functions.https.onCall((data, context) => { if (!context.auth) return "Error request is not verified" return admin.firestore().collection('preferences').doc(context.auth.uid).delete().then(() => { - return "Successfully deleted preferences" + functions.logger.log("Successfully deleted user preferences from Firestore") + return "Successfully deleted user preferences from Firestore" }) }) \ No newline at end of file diff --git a/functions/test/index.test.js b/functions/test/index.test.js index 60a5dcd..452082b 100644 --- a/functions/test/index.test.js +++ b/functions/test/index.test.js @@ -1,12 +1,11 @@ const chai = require('chai'); const chaiHttp = require('chai-http'); - chai.use(chaiHttp); const expect = chai.expect; describe('Firebase cloud function tests', function() { this.timeout(100000); - it('Test getting live train data from the database', async() => { + it('Test /getLiveTrainData', async() => { const result = await chai.request('https://us-central1-irishrailtracker.cloudfunctions.net') .get('/getLiveTrainData') expect(result.statusCode).to.equal(200); @@ -22,7 +21,7 @@ describe('Firebase cloud function tests', function() { }), this.timeout(100000); - it('Test getting station data from the database', async() => { + it('Test /getStationData', async() => { const result = await chai.request('https://us-central1-irishrailtracker.cloudfunctions.net') .get('/getStationData') expect(result.statusCode).to.equal(200); @@ -36,14 +35,14 @@ describe('Firebase cloud function tests', function() { }), this.timeout(100000); - it('Test updating the database with live train data', async() => { + it('Test /postLiveTrainData', async() => { const result = await chai.request('https://us-central1-irishrailtracker.cloudfunctions.net') .get('/postLiveTrainData') expect(result.statusCode).to.equal(200); }), this.timeout(100000); - it('Test updating the database with live station data', async() => { + it('Test /postStationData', async() => { const result = await chai.request('https://us-central1-irishrailtracker.cloudfunctions.net') .get('/postStationData') expect(result.statusCode).to.equal(200); diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue index 6ee054d..c0f4b94 100644 --- a/src/components/Navbar.vue +++ b/src/components/Navbar.vue @@ -51,6 +51,7 @@ export default { onAuthStateChanged(auth, (user) => { user ? this.isLoggedIn = true : this.isLoggedIn = false store.setLoginStatus(this.isLoggedIn) + store.isWaitingForLoginStatus = false }) }, diff --git a/src/pages/AccountPage.vue b/src/pages/AccountPage.vue index c0e1efc..93d398a 100644 --- a/src/pages/AccountPage.vue +++ b/src/pages/AccountPage.vue @@ -19,7 +19,7 @@

Delete account

-

Delete filter preferences data

+

Delete map filter preferences data

@@ -79,7 +79,12 @@ export default { }) .catch((error) => { this.reAuthSuccessful = false - this.showToast(error.message, "red") + if (error.message.includes("wrong")) { + this.showToast("Wrong password inputted", "red") + } + else { + this.showToast(error.message, "red") + } }) }, @@ -171,7 +176,7 @@ export default { const deletePreferencesData = httpsCallable(functions, 'deletePreferences') deletePreferencesData().then(() => { this.resetCredentials() - this.showToast("Successfully deleted filter preferences", "green") + this.showToast("Successfully map deleted filter preferences", "green") }) .catch((error) => { this.showToast(error.message, "red") diff --git a/src/pages/InsightsPage.vue b/src/pages/InsightsPage.vue index b3dcffc..35e0035 100644 --- a/src/pages/InsightsPage.vue +++ b/src/pages/InsightsPage.vue @@ -53,15 +53,28 @@

Leaderboard

-
-

{{ this.rawData[item.jsonIndex]["TrainCode"][0] }}

-

{{ item.time }} mins late

-

{{ item.time * -1}} mins early

+
+ + +
+ +
+

{{ this.rawData[train.jsonIndex]["TrainCode"][0] }}

+

{{ train.time }} mins late

+

{{ train.time * -1}} mins early

+
+ +
+

{{ this.rawData[train.jsonIndex]["TrainCode"][0] }}

+

{{ train.time }} mins late

+

{{ train.time * -1}} mins early

diff --git a/src/pages/LoginPage.vue b/src/pages/LoginPage.vue index ee90c8a..2884170 100644 --- a/src/pages/LoginPage.vue +++ b/src/pages/LoginPage.vue @@ -10,8 +10,8 @@

Password

- Don't have an account? Forgot password? + Don't have an account?
@@ -70,22 +70,48 @@ export default { login() { const auth = getAuth(app) + if (!this.email || !this.password) { + this.showToast("Missing credentials", "red") + return + } + signInWithEmailAndPassword(auth, this.email, this.password).then(() => { this.showToast("Logged in successfully", "green") this.$router.push({path:'/'}) }) .catch((error) => { - this.showToast(error.message, "red") + if (error.message.includes("email")) { + this.showToast("Invalid email", "red") + } + else if (error.message.includes("user")) { + this.showToast("Could not find this user", "red") + } + else { + this.showToast(error.message, "red") + } }) }, resetPasswordEmail() { + if (!this.email) { + this.showToast("Missing credentials", "red") + return + } + sendPasswordResetEmail(auth, this.email).then(() => { this.showToast("Reset password email sent", "green") this.email = "" }) .catch((error) => { - this.showToast(error.message, "red") + if (error.message.includes("email")) { + this.showToast("Invalid email", "red") + } + else if (error.message.includes("user")) { + this.showToast("Could not find this user", "red") + } + else { + this.showToast(error.message, "red") + } }) } } diff --git a/src/pages/MapPage.vue b/src/pages/MapPage.vue index 0c09cdb..aad2448 100644 --- a/src/pages/MapPage.vue +++ b/src/pages/MapPage.vue @@ -6,29 +6,30 @@ Map Filters @@ -56,14 +55,31 @@ export default { }, signup() { - this.displayFirebaseError = false; + if (!this.email || !this.password) { + this.showToast("Missing credentials", "red") + return + } + + if (this.password.length < 6) { + this.showToast("Password must be 6 or more characters", "red") + return + } + const auth = getAuth(app) createUserWithEmailAndPassword(auth, this.email, this.password).then(() => { this.showToast("Signed up successfully", "green") this.$router.push({path:'/'}) }) .catch((error) => { - this.showToast(error.message, "red") + if (error.message.includes("already")) { + this.showToast("Email already in use", "red") + } + else if (error.message.includes("email")) { + this.showToast("Invalid email", "red") + } + else { + this.showToast(error.message, "red") + } }) } } diff --git a/src/store/store.js b/src/store/store.js index 3822d8d..8a8c6bf 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -11,6 +11,7 @@ export const store = reactive({ displaySelectedTrain: false, displayedSelectedStation: false, loggedIn: false, + isWaitingForLoginStatus: true, setInsights(insights) { this.insights = insights