Skip to content

Commit

Permalink
Merge pull request #512 from TAMUSHPE/dev-web
Browse files Browse the repository at this point in the history
Dev web
  • Loading branch information
JasonIsAzn authored Sep 7, 2024
2 parents 7ec43ac + 0e49d1e commit d5b6e55
Show file tree
Hide file tree
Showing 3 changed files with 312 additions and 57 deletions.
92 changes: 77 additions & 15 deletions shpe-app-web/app/(main)/membership/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import Header from '@/components/Header';
import { getMembers, getMembersToVerify } from '@/api/firebaseUtils';
import { FaSync } from "react-icons/fa";
import { SHPEEventLog } from '@/types/events';
import { User } from '@/types/user';
import { isMemberVerified, RequestWithDoc } from '@/types/membership';
Expand All @@ -27,19 +28,71 @@ const Membership = () => {

const fetchMembers = async () => {
setLoading(true);
const response = await getMembers();
setStudents(response);
const filteredMembers = response.filter((member) => {
console.log(member.publicInfo?.chapterExpiration);
return isMemberVerified(member.publicInfo?.chapterExpiration, member.publicInfo?.nationalExpiration);
});
setMembers(filteredMembers);
try {
const response = await getMembers();
setStudents(response);

const filteredMembers = response.filter((member) => {
console.log(member.publicInfo?.chapterExpiration);
return isMemberVerified(
member.publicInfo?.chapterExpiration,
member.publicInfo?.nationalExpiration
);
});
setMembers(filteredMembers);

localStorage.setItem('cachedMembers', JSON.stringify(response));
localStorage.setItem('cachedOfficialMembers', JSON.stringify(filteredMembers));
localStorage.setItem('cachedMembersTimestamp', Date.now().toString());

const incomingReqs = await getMembersToVerify();
setRequestsWithDocuments(incomingReqs);
setLoading(false);
const incomingReqs = await getMembersToVerify();
setRequestsWithDocuments(incomingReqs);
localStorage.setItem('cachedRequests', JSON.stringify(incomingReqs));
localStorage.setItem('cachedRequestsTimestamp', Date.now().toString());

} catch (error) {
console.error('Error fetching members:', error);
} finally {
setLoading(false);
}
};

const checkCacheAndFetchMembers = () => {
const cachedMembers = localStorage.getItem('cachedMembers');
const cachedOfficialMembers = localStorage.getItem('cachedOfficialMembers');
const cachedMembersTimestamp = localStorage.getItem('cachedMembersTimestamp');
const cachedRequests = localStorage.getItem('cachedRequests');
const cachedRequestsTimestamp = localStorage.getItem('cachedRequestsTimestamp');

const isCacheValid = (timestamp: string): boolean => {
return Date.now() - parseInt(timestamp, 10) < 24 * 60 * 60 * 1000;
};

if (
cachedMembers &&
cachedOfficialMembers &&
cachedMembersTimestamp &&
isCacheValid(cachedMembersTimestamp) &&
cachedRequests &&
cachedRequestsTimestamp &&
isCacheValid(cachedRequestsTimestamp)
) {
const studentsData = JSON.parse(cachedMembers);
setStudents(studentsData);

setMembers(JSON.parse(cachedOfficialMembers));

setRequestsWithDocuments(JSON.parse(cachedRequests));
setLoading(false);
} else {
fetchMembers();
}
};

useEffect(() => {
checkCacheAndFetchMembers();
}, []);

const handleApprove = async (member: RequestWithDoc) => {
const userDocRef = doc(db, 'users', member.uid);

Expand Down Expand Up @@ -110,10 +163,12 @@ const Membership = () => {
}
};

useEffect(() => {
fetchMembers();
setLoading(false);
}, []);
const handleReload = async () => {
if (window.confirm("Are you sure you want to reload the members?")) {
setLoading(true);
await fetchMembers();
}
};

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
Expand Down Expand Up @@ -142,7 +197,6 @@ const Membership = () => {

return (
<div className="w-full h-full flex flex-col">

<div className="text-white bg-[#500000] text-center text-2xl flex">
<button onClick={() => setTab('members')} className="w-1/2">
Offical Members
Expand Down Expand Up @@ -232,6 +286,14 @@ const Membership = () => {
})}
</table>
)}

{/* Reload Button */}
<button
onClick={handleReload}
className="absolute bottom-4 right-4 bg-blue-600 text-white p-3 rounded-full shadow-lg hover:bg-blue-700 transition"
>
<FaSync />
</button>
</div>
);
};
Expand Down
153 changes: 122 additions & 31 deletions shpe-app-web/app/(main)/points/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getEvents, getMembers, updatePointsInFirebase } from "@/api/firebaseUti
import { SHPEEvent, SHPEEventLog } from '@/types/events';
import { format } from 'date-fns';
import { User } from "@/types/user";
import { FaChevronLeft, FaChevronRight, FaFilter, FaUndo, FaSave } from "react-icons/fa";
import { FaChevronLeft, FaChevronRight, FaFilter, FaUndo, FaSave, FaSync } from "react-icons/fa";
import { httpsCallable } from "firebase/functions";
import { auth, functions } from "@/config/firebaseConfig";
import { onAuthStateChanged } from "firebase/auth";
Expand Down Expand Up @@ -35,51 +35,79 @@ const Points = () => {
const months = generateSchoolYearMonths();
const updateAllUserPoints = httpsCallable(functions, 'updateAllUserPoints');

// Set the initial current month index to the real current month
const currentMonthDate = new Date();
const realCurrentMonthIndex = months.findIndex(
(month) => month.getFullYear() === currentMonthDate.getFullYear() && month.getMonth() === currentMonthDate.getMonth()
);
const [currentMonthIndex, setCurrentMonthIndex] = useState<number>(realCurrentMonthIndex !== -1 ? realCurrentMonthIndex : 0);

const fetchMembers = async () => {
const response = await getMembers() as UserWithLogs[];
setMembers(response);

// Initialize the original points state
const initialPoints = response.reduce((acc: PointsRecord, member) => {
if (member.publicInfo?.uid && member.eventLogs) {
member.eventLogs.forEach(log => {
if (log.eventId) {
acc[`${member.publicInfo?.uid}-${log.eventId}`] = log.points || 0;
}

// Calculate Instagram points per month
if (log.instagramLogs) {
months.forEach((month, index) => {
const pointsForInstagram = calculateInstagramPoints(log.instagramLogs!, month);
acc[`${member.publicInfo?.uid}-instagram-${index}`] = pointsForInstagram;
});
}
});
}

return acc;
}, {});
setLoading(true);
try {
const response = await getMembers() as UserWithLogs[];
setMembers(response);

const initialPoints = response.reduce((acc: PointsRecord, member) => {
if (member.publicInfo?.uid && member.eventLogs) {
member.eventLogs.forEach(log => {
if (log.eventId) {
acc[`${member.publicInfo?.uid}-${log.eventId}`] = log.points || 0;
}

if (log.instagramLogs) {
months.forEach((month, index) => {
const pointsForInstagram = calculateInstagramPoints(log.instagramLogs!, month);
acc[`${member.publicInfo?.uid}-instagram-${index}`] = pointsForInstagram;
});
}
});
}
return acc;
}, {});

setOriginalPoints(initialPoints);
setOriginalPoints(initialPoints);
localStorage.setItem('cachedMembers', JSON.stringify(response));
localStorage.setItem('cachedPoints', JSON.stringify(initialPoints));
localStorage.setItem('cachedMembersTimestamp', Date.now().toString());
} catch (error) {
console.error('Error fetching members:', error);
} finally {
setLoading(false);
}
};

const checkCacheAndFetchMembers = () => {
const cachedMembers = localStorage.getItem('cachedMembers');
const cachedPoints = localStorage.getItem('cachedPoints');
const cachedTimestamp = localStorage.getItem('cachedMembersTimestamp');

const isCacheValid = (timestamp: string): boolean => {
return Date.now() - parseInt(timestamp, 10) < 24 * 60 * 60 * 1000;
};

if (cachedMembers && cachedPoints && cachedTimestamp && isCacheValid(cachedTimestamp)) {
const members = JSON.parse(cachedMembers);
const convertedMembers = convertMembersLogsToTimestamps(members);
setMembers(convertedMembers);
setOriginalPoints(JSON.parse(cachedPoints));
} else {
fetchMembers();
}
};
const fetchEvents = async () => {
const response = await getEvents();
setEvents(response);
try {
const response = await getEvents();
setEvents(response);
} catch (error) {
console.error('Error fetching events:', error);
}
};

useEffect(() => {
fetchMembers();
checkCacheAndFetchMembers();
fetchEvents();
}, []);


useEffect(() => {
if (isEditing && inputRef.current) {
inputRef.current.focus();
Expand Down Expand Up @@ -411,6 +439,14 @@ const Points = () => {
};


const handleReload = async () => {
if (window.confirm("Are you sure you want to reload the points?")) {
setLoading(true);
await fetchMembers();
}
};


if (loading) {
return (
<div className="flex flex-col w-full h-screen items-center justify-center bg-white">
Expand Down Expand Up @@ -636,6 +672,14 @@ const Points = () => {
</tbody>
</table>
</div>

{/* Reload Button */}
<button
onClick={handleReload}
className="absolute bottom-4 right-4 bg-blue-600 text-white p-3 rounded-full shadow-lg hover:bg-blue-700 transition"
>
<FaSync />
</button>
</div>
);
}
Expand Down Expand Up @@ -682,8 +726,55 @@ const getColumnColor = (index: number): string => {
return colors[index % colors.length];
};

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const isPlainDateObject = (obj: any): obj is Date => {
return (
obj &&
typeof obj === 'object' &&
typeof obj.getFullYear === 'function' &&
typeof obj.getMonth === 'function' &&
typeof obj.getDate === 'function'
);
};

// Helper function to check if an object is structured like a Firebase Timestamp
const isPlainTimestampObject = (obj: any): obj is { seconds: number; nanoseconds: number } => {
return (
obj &&
typeof obj === 'object' &&
typeof obj.seconds === 'number' &&
typeof obj.nanoseconds === 'number'
);
};

// Convert the object back to a Timestamp if it's structured like one
const convertToTimestamp = (obj: any): Timestamp | null => {
if (isPlainDateObject(obj)) {
return Timestamp.fromDate(obj);
} else if (isPlainTimestampObject(obj)) {
return new Timestamp(obj.seconds, obj.nanoseconds);
}
return null;
};



// Conversion function to convert Date objects or Timestamp-like objects back to Timestamps
const convertDatesToTimestamps = (log: SHPEEventLog): SHPEEventLog => {
return {
...log,
signInTime: convertToTimestamp(log.signInTime) || log.signInTime,
signOutTime: convertToTimestamp(log.signOutTime) || log.signOutTime,
creationTime: convertToTimestamp(log.creationTime) || log.creationTime,
instagramLogs: log.instagramLogs?.map(log => convertToTimestamp(log) || log),
};
};

// Utility function to convert all event logs in members
const convertMembersLogsToTimestamps = (members: UserWithLogs[]): UserWithLogs[] => {
return members.map(member => ({
...member,
eventLogs: member.eventLogs?.map(convertDatesToTimestamps),
}));
};

export default Points;
Loading

0 comments on commit d5b6e55

Please sign in to comment.