Skip to content

Commit

Permalink
Merge pull request #425 from TAMUSHPE/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
JasonIsAzn authored Aug 16, 2024
2 parents 60753cc + 8826923 commit 6bf3666
Show file tree
Hide file tree
Showing 59 changed files with 10,616 additions and 371 deletions.
4 changes: 2 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "TAMU SHPE",
"slug": "TAMU-SHPE",
"version": "1.0.1",
"version": "1.0.2",
"owner": "tamu-shpe",
"orientation": "portrait",
"icon": "./assets/icon.png",
Expand Down Expand Up @@ -57,7 +57,7 @@
},
"ios": {
"bundleIdentifier": "com.tamu.shpe",
"buildNumber": "2",
"buildNumber": "1",
"userInterfaceStyle": "automatic"
},
"extra": {
Expand Down
2 changes: 1 addition & 1 deletion functions/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export const addInstagramPoints = functions.https.onCall(async (data, context) =
}

const token = context.auth.token;
if (token.admin !== true && token.officer !== true && token.developer !== true) {
if (token.admin !== true && token.officer !== true && token.developer !== true && token.lead !== true && token.representative !== true) {
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}

Expand Down
2 changes: 1 addition & 1 deletion functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { notifyUpcomingEvents, sendNotificationOfficeHours, sendNotificationMemberSHPE, sendNotificationCommitteeRequest, sendNotificationResumeConfirm } from "./pushNotification";
export { updateRanksScheduled, updateRanksOnCall, updateUserPoints, updateAllUserPoints, scheduledUpdateAllPoints } from "./pointSheet";
export { updateUserPoints, updateAllUserPoints, scheduledUpdateAllPoints } from "./pointSheet";
export { updateCommitteeCount } from "./committees";
// export { resetOfficeScheduler } from "./officeReset";
export { resetOfficeOnCall } from "./officeReset";
Expand Down
31 changes: 14 additions & 17 deletions functions/src/officeReset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,27 @@
import * as functions from 'firebase-functions';
import { db } from "./firebaseConfig"


const resetOffice = async () => {
const setOfficer = db.doc(`office-hours/officer-count`);
const officersRef = db.collection('office-hours/officers-status/officers');
const officersRef = db.collection('office-hours');

try {
const snapshot = await officersRef.get();
const updatePromises = snapshot.docs.map((doc) =>
doc.ref.update({
signedIn: false,
})
);

try{
await setOfficer.update({'zachary-office': 0});
officersRef.get().then(async (snapshot) => {
snapshot.forEach(doc => {
doc.ref.update({
signedIn: false,
});
});
});
await Promise.all(updatePromises);
return { success: true };
}
catch (error) {
} catch (error) {
console.error('Update failure:', error);
throw new functions.https.HttpsError('internal', 'Could not reset officer count');
throw new functions.https.HttpsError('internal', 'Could not reset officer statuses');
}
};

}

export const resetOfficeScheduler = functions.pubsub.schedule('0 21 * * *') .onRun(async (context) => {
export const resetOfficeScheduler = functions.pubsub.schedule('0 21 * * *').onRun(async (context) => {
resetOffice()
});

Expand Down
98 changes: 47 additions & 51 deletions functions/src/pointSheet.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { AggregateField } from 'firebase-admin/firestore';
import { db } from "./firebaseConfig"


type RankChange = "increased" | "decreased" | "same";

/** Determines rank change based on current and new ranks.
* - "increased" means that the overall rank *value* is lower than before
* - "decreased" means that the overall rank *value* is higher than before
* - "same" means that the overall rank has stayed the same
*/
/** Determines rank change based on current and new ranks. */
const getRankChange = (oldRank: any, newRank: number): RankChange => {
if (oldRank < newRank) return "decreased";
if (oldRank > newRank) return "increased";
Expand All @@ -21,53 +18,42 @@ const updateUserRank = async (uid: string, userData: FirebaseFirestore.DocumentD
if (!uid || !userData) return;

const userDocRef = db.collection('users').doc(uid);
const rankChange = userData.pointsRank ? getRankChange(userData.pointsRank, newRank) : "same";

await userDocRef.set({
pointsRank: newRank,
rankChange: rankChange,
}, { merge: true });
// If the user has 0 points or no points, delete the rank and rankChange fields
if (!userData.points || userData.points === 0) {
await userDocRef.update({
pointsRank: admin.firestore.FieldValue.delete(),
rankChange: admin.firestore.FieldValue.delete(),
});
} else {
const rankChange = userData.pointsRank ? getRankChange(userData.pointsRank, newRank) : "same";

await userDocRef.set({
pointsRank: newRank,
rankChange: rankChange,
}, { merge: true });
}
}

/** Fetches data from Google Spreadsheet and updates users' ranks in Firestore */
/** Updates the ranks of all users based on their points */
const updateRanks = async (): Promise<string> => {
try {
const snapshot = await db.collection('users').orderBy("points", "desc").get();

let currentRank = 1;
snapshot.forEach((doc) => {
updateUserRank(doc.id, doc.data(), currentRank);
currentRank++;
if (doc.data().points > 0) {
currentRank++;
}
});
console.info(`${snapshot.size} documents updated`);
return "Success";
} catch (error) {
console.error("Error in updateRanks:", error);
throw new Error("Internal Server error");
}
}

/** Scheduled function to update ranks daily at 5AM CST */
export const updateRanksScheduled = functions.pubsub.schedule('0 5 * * *').timeZone('America/Chicago').onRun(async (context) => {
await updateRanks();
});

/** Callable function to manually update ranks */
export const updateRanksOnCall = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError("unauthenticated", "Function cannot be called without authentication.");
}
try {
const result = await updateRanks();
return {
status: 'success',
message: result,
};
} catch (error) {
console.error("Error in updatePointStats:", error);
throw new functions.https.HttpsError('internal', 'Internal Server Error.');
}
});
};

/**
* Sums all user points from their event log collection and returns this value
Expand Down Expand Up @@ -128,7 +114,7 @@ export const updateUserPoints = functions.https.onCall(async (data, context) =>
}

const token = context.auth.token;
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true) {
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true) {
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}

Expand All @@ -152,36 +138,46 @@ export const updateAllUserPoints = functions.https.onCall(async (_, context) =>
}

const token = context.auth.token;
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true) {
if (token.admin !== true && token.officer !== true && token.developer !== true && token.secretary !== true && token.lead !== true && token.representative !== true) {
throw new functions.https.HttpsError("permission-denied", `Invalid credentials`);
}

return db.collection('users').get()
.then((snapshot) => {
snapshot.forEach(async (document) => {
document.ref.set({
points: await calculateUserPoints(document.id),
pointsThisMonth: await calculateUserPointsThisMonth(document.id),
}, { merge: true });
});

return { success: true };
})
.catch((err) => {
throw new functions.https.HttpsError("aborted", `Issue occured while attempting to update user document: ${err}`);
try {
// Update all users' points
const snapshot = await db.collection('users').get();
const updatePromises = snapshot.docs.map(async (document) => {
await document.ref.set({
points: await calculateUserPoints(document.id),
pointsThisMonth: await calculateUserPointsThisMonth(document.id),
}, { merge: true });
});
await Promise.all(updatePromises);

// Once points are updated, update ranks
await updateRanks();

return { success: true };
} catch (err) {
throw new functions.https.HttpsError("aborted", `Issue occurred while attempting to update user documents: ${err}`);
}
});

/** Scheduled function to update all points and ranks daily at 5AM CST */
export const scheduledUpdateAllPoints = functions.pubsub.schedule('0 5 * * *').timeZone('America/Chicago').onRun(async (context) => {
try {
// Update all users' points
const snapshot = await db.collection('users').get();
const updatePromises = snapshot.docs.map(async (document) => {
return document.ref.set({
await document.ref.set({
points: await calculateUserPoints(document.id),
pointsThisMonth: await calculateUserPointsThisMonth(document.id),
}, { merge: true });
});
await Promise.all(updatePromises);

// Once points are updated, update ranks
await updateRanks();

return { success: true };
} catch (err) {
throw new functions.https.HttpsError("aborted", `Issue occurred while attempting to update user documents: ${err}`);
Expand Down
1 change: 1 addition & 0 deletions functions/src/points.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const calculateMOTM = functions.https.onCall(async (data, context) => {
const eligibleUsersRef = db.collection('users')
.where('roles.officer', '==', false)
.where('roles.representative', '==', false)
.where('roles.lead', '==', false)
.orderBy('pointsThisMonth', 'desc');

const eligibleUsersSnapshot = await eligibleUsersRef.get();
Expand Down
5 changes: 4 additions & 1 deletion functions/src/pushNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const getMemberTokens = async (uid: string) => {
*/
const getAvailableOfficersTokens = async (): Promise<string[]> => {
const signedInOfficersTokens: string[] = [];
const snapshot = await db.collection('office-hours/officers-status/officers').where('signedIn', '==', true).get();
const snapshot = await db.collection('office-hours').get();

for (const doc of snapshot.docs) {
const data = doc.data();
Expand All @@ -36,9 +36,12 @@ const getAvailableOfficersTokens = async (): Promise<string[]> => {
}
}

console.log(signedInOfficersTokens, 'SignedInOfficerTokens');

return signedInOfficersTokens;
};


/**
* Checks if a provided token conforms to the structure of an Expo push token.
*
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shpe-app",
"version": "1.0.1",
"version": "1.0.2",
"scripts": {
"start": "npx expo start --dev-client",
"test": "jest --coverage=true --verbose --bail --config ./jest.config.ts",
Expand Down
4 changes: 1 addition & 3 deletions shpe-app-web/app/(main)/committees/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import { getPublicUserData, getCommittees } from "@/api/firebaseUtils";
import { checkAuthAndRedirect } from "@/helpers/auth";
import { Committee } from "@/types/committees";
import Header from "@/components/Header";
import CommitteeCard from "./components/CommitteeCard";
Expand All @@ -20,7 +19,7 @@ const Committees = () => {

const updatedCommittees = await Promise.all(committees.map(async (committee) => {
if (committee.head) {
const userData = await getPublicUserData(committee.head);
const userData = committee.head;
return { ...committee, head: userData };
}
return committee;
Expand All @@ -30,7 +29,6 @@ const Committees = () => {
setLoading(false);
}

checkAuthAndRedirect(router);
fetchCommittees();
setLoading(false);
}, []);
Expand Down
17 changes: 13 additions & 4 deletions shpe-app-web/app/(main)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
'use client'
import { useEffect, useState } from "react";
import { useRouter } from 'next/navigation';
import { checkAuthAndRedirect } from "@/helpers/auth";
import Header from "@/components/Header";
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "@/config/firebaseConfig";

const Dashboard = () => {
const router = useRouter();
const [loading, setLoading] = useState(true);

useEffect(() => {
checkAuthAndRedirect(router);
setLoading(false);
}, []);
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
if (currentUser) {
setLoading(false);
} else {
// User is not logged in, redirect to root
router.push('/');
}
});

return () => unsubscribe();
}, [router]);

if (loading) {
return (
Expand Down
8 changes: 8 additions & 0 deletions shpe-app-web/app/(main)/events/components/DayModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ interface DayModalProps {
export const DayModal: React.FC<DayModalProps> = ({ day, events, isShowing, hide }) => {
const modal = (
<>
{/* Filter */}
<div className="fixed top-0 right-0 w-full h-full bg-[#500000] bg-opacity-30 z-50"></div>

{/* Modal */}
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl z-50">
<div className="flex flex-col gap-3 p-5 w-[60vw] max-w-[800px] h-[60vh] items-center">
{/* Header */}
<h2 className="font-bold text-2xl text-black">{format(day, 'cccc, MMMM do yyyy')}</h2>

{/* Event List */}
<div className="flex flex-col gap-2 w-5/6 mt-3 overflow-auto">
{events.map((event) => {
return (
Expand All @@ -30,6 +36,8 @@ export const DayModal: React.FC<DayModalProps> = ({ day, events, isShowing, hide
);
})}
</div>

{/* Close Button */}
<button
onClick={hide}
className="absolute left-5 bg-[#500000] text-white text-sm font-semibold rounded-md p-2"
Expand Down
Loading

0 comments on commit 6bf3666

Please sign in to comment.