From 9d7058d533a6dddd53b16c1868272ccdf8e001a7 Mon Sep 17 00:00:00 2001 From: chark1es Date: Sat, 1 Mar 2025 16:04:07 -0800 Subject: [PATCH] different stats view --- .../ProfileSection/ShowProfileLogs.tsx | 151 +++++------------- .../dashboard/ProfileSection/Stats.tsx | 65 ++++---- 2 files changed, 76 insertions(+), 140 deletions(-) diff --git a/src/components/dashboard/ProfileSection/ShowProfileLogs.tsx b/src/components/dashboard/ProfileSection/ShowProfileLogs.tsx index 98bee65..e7a87f4 100644 --- a/src/components/dashboard/ProfileSection/ShowProfileLogs.tsx +++ b/src/components/dashboard/ProfileSection/ShowProfileLogs.tsx @@ -1,31 +1,11 @@ import { useEffect, useState, useCallback, useMemo } from "react"; -import { Get } from "../../../scripts/pocketbase/Get"; import { Authentication } from "../../../scripts/pocketbase/Authentication"; import { DataSyncService } from "../../../scripts/database/DataSyncService"; import { Collections } from "../../../schemas/pocketbase/schema"; import debounce from 'lodash/debounce'; import type { Log } from "../../../schemas/pocketbase"; -interface PaginatedResponse { - page: number; - perPage: number; - totalItems: number; - totalPages: number; - items: Log[]; -} - -interface CacheEntry { - timestamp: number; - data: Log[]; - totalItems: number; - totalPages: number; - lastFetched: string; // ISO date string to track when we last fetched all logs -} - const LOGS_PER_PAGE = 5; -const CACHE_EXPIRY = 5 * 60 * 1000; // 5 minutes -const CACHE_KEY_PREFIX = 'logs_cache_'; -const BATCH_SIZE = 100; // Number of logs to fetch per batch export default function ShowProfileLogs() { const [logs, setLogs] = useState([]); @@ -35,62 +15,10 @@ export default function ShowProfileLogs() { const [totalPages, setTotalPages] = useState(1); const [totalLogs, setTotalLogs] = useState(0); const [searchQuery, setSearchQuery] = useState(''); - const [cachedLogs, setCachedLogs] = useState([]); + const [allLogs, setAllLogs] = useState([]); const [isFetchingAll, setIsFetchingAll] = useState(false); - const getCacheKey = (userId: string) => `${CACHE_KEY_PREFIX}${userId}`; - - const saveToCache = (userId: string, data: CacheEntry) => { - try { - localStorage.setItem(getCacheKey(userId), JSON.stringify(data)); - } catch (error) { - console.warn('Failed to save to cache:', error); - } - }; - - const getFromCache = (userId: string): CacheEntry | null => { - try { - const cached = localStorage.getItem(getCacheKey(userId)); - if (!cached) return null; - - const parsedCache = JSON.parse(cached) as CacheEntry; - if (Date.now() - parsedCache.timestamp > CACHE_EXPIRY) { - localStorage.removeItem(getCacheKey(userId)); - return null; - } - - return parsedCache; - } catch (error) { - console.warn('Failed to read from cache:', error); - return null; - } - }; - - const fetchAllLogs = async (userId: string): Promise => { - const dataSync = DataSyncService.getInstance(); - let allLogs: Log[] = []; - let page = 1; - let hasMore = true; - - // First, sync all logs for this user - await dataSync.syncCollection( - Collections.LOGS, - `user_id = "${userId}"`, - "-created" - ); - - // Then get all logs from IndexedDB - allLogs = await dataSync.getData( - Collections.LOGS, - false, // Don't force sync again - `user_id = "${userId}"`, - "-created" - ); - - return allLogs; - }; - - const fetchLogs = async (page: number, skipCache = false) => { + const fetchLogs = async (skipCache = false) => { setLoading(true); setError(null); @@ -105,34 +33,29 @@ export default function ShowProfileLogs() { } try { - // Check cache first if not skipping - if (!skipCache) { - const cached = getFromCache(userId); - if (cached) { - setCachedLogs(cached.data); - setTotalPages(Math.ceil(cached.data.length / LOGS_PER_PAGE)); - setTotalLogs(cached.data.length); - setLoading(false); - return; - } - } - setIsFetchingAll(true); - const allLogs = await fetchAllLogs(userId); - // Save to cache - saveToCache(userId, { - timestamp: Date.now(), - data: allLogs, - totalItems: allLogs.length, - totalPages: Math.ceil(allLogs.length / LOGS_PER_PAGE), - lastFetched: new Date().toISOString() - }); + // Use DataSyncService to fetch logs + const dataSync = DataSyncService.getInstance(); - setCachedLogs(allLogs); - setCurrentPage(page); - setTotalPages(Math.ceil(allLogs.length / LOGS_PER_PAGE)); - setTotalLogs(allLogs.length); + // First sync logs for this user + await dataSync.syncCollection( + Collections.LOGS, + `user_id = "${userId}"`, + "-created" + ); + + // Then get all logs from IndexedDB + const fetchedLogs = await dataSync.getData( + Collections.LOGS, + false, // Don't force sync again + `user_id = "${userId}"`, + "-created" + ); + + setAllLogs(fetchedLogs); + setTotalPages(Math.ceil(fetchedLogs.length / LOGS_PER_PAGE)); + setTotalLogs(fetchedLogs.length); } catch (error) { console.error("Failed to fetch logs:", error); setError("Error loading activity"); @@ -148,17 +71,17 @@ export default function ShowProfileLogs() { // When not searching, return only the current page of logs const startIndex = (currentPage - 1) * LOGS_PER_PAGE; const endIndex = startIndex + LOGS_PER_PAGE; - return cachedLogs.slice(startIndex, endIndex); + return allLogs.slice(startIndex, endIndex); } const query = searchQuery.toLowerCase(); - return cachedLogs.filter(log => { + return allLogs.filter(log => { return ( - log.message.toLowerCase().includes(query) || - new Date(log.created).toLocaleString().toLowerCase().includes(query) + log.message?.toLowerCase().includes(query) || + (log.created && new Date(log.created).toLocaleString().toLowerCase().includes(query)) ); }); - }, [searchQuery, cachedLogs, currentPage]); + }, [searchQuery, allLogs, currentPage]); // Update displayed logs whenever filtered results change useEffect(() => { @@ -169,6 +92,10 @@ export default function ShowProfileLogs() { const debouncedSearch = useCallback( debounce((query: string) => { setSearchQuery(query); + // Reset to first page when searching + if (query.trim()) { + setCurrentPage(1); + } }, 300), [] ); @@ -190,20 +117,20 @@ export default function ShowProfileLogs() { }; const handleRefresh = () => { - fetchLogs(currentPage, true); + fetchLogs(true); }; useEffect(() => { - fetchLogs(1); + fetchLogs(); }, []); - if (loading && !cachedLogs.length) { + if (loading && !allLogs.length) { return (

- {isFetchingAll ? 'Fetching all activity...' : 'Loading activity...'} + {isFetchingAll ? 'Fetching your activity...' : 'Loading activity...'}

); } @@ -219,7 +146,7 @@ export default function ShowProfileLogs() { ); } - if (logs.length === 0 && !searchQuery) { + if (logs.length === 0 && !searchQuery && !loading) { return (

@@ -256,7 +183,7 @@ export default function ShowProfileLogs() { {isFetchingAll && (

-

Fetching all logs, please wait...

+

Fetching all activity, please wait...

)} @@ -296,11 +223,11 @@ export default function ShowProfileLogs() { {/* Pagination Controls */} - {!searchQuery && ( + {!searchQuery && totalLogs > LOGS_PER_PAGE && (
- Showing {(currentPage - 1) * LOGS_PER_PAGE + 1}-{Math.min(currentPage * LOGS_PER_PAGE, totalLogs)} of {totalLogs} results + Showing {totalLogs ? (currentPage - 1) * LOGS_PER_PAGE + 1 : 0}-{Math.min(currentPage * LOGS_PER_PAGE, totalLogs)} of {totalLogs} results
diff --git a/src/components/dashboard/ProfileSection/Stats.tsx b/src/components/dashboard/ProfileSection/Stats.tsx index d325083..bc99569 100644 --- a/src/components/dashboard/ProfileSection/Stats.tsx +++ b/src/components/dashboard/ProfileSection/Stats.tsx @@ -1,5 +1,4 @@ import { useEffect, useState } from "react"; -import { Get } from "../../../scripts/pocketbase/Get"; import { Authentication } from "../../../scripts/pocketbase/Authentication"; import { DataSyncService } from "../../../scripts/database/DataSyncService"; import { Collections } from "../../../schemas/pocketbase/schema"; @@ -8,32 +7,35 @@ import type { Event, Log, User } from "../../../schemas/pocketbase"; // Extended User interface with points property interface ExtendedUser extends User { points?: number; + member_type?: string; } export function Stats() { const [eventsAttended, setEventsAttended] = useState(0); const [loyaltyPoints, setLoyaltyPoints] = useState(0); - const [activityLevel, setActivityLevel] = useState("Low"); - const [activityDesc, setActivityDesc] = useState("New Member"); const [pointsChange, setPointsChange] = useState("No activity"); + const [membershipStatus, setMembershipStatus] = useState("Member"); + const [memberSince, setMemberSince] = useState(null); + const [upcomingEvents, setUpcomingEvents] = useState(0); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const fetchStats = async () => { try { setIsLoading(true); - const get = Get.getInstance(); const auth = Authentication.getInstance(); const dataSync = DataSyncService.getInstance(); const userId = auth.getCurrentUser()?.id; if (!userId) return; - // Get current quarter dates + // Get current date const now = new Date(); - const month = now.getMonth(); // 0-11 + // Get current quarter dates for points calculation + const month = now.getMonth(); // 0-11 let quarterStart = new Date(); + // Fall: Sept-Dec if (month >= 8 && month <= 11) { quarterStart = new Date(now.getFullYear(), 8, 1); // Sept 1 @@ -58,7 +60,17 @@ export function Stats() { const user = await dataSync.getItem(Collections.USERS, userId); const totalPoints = user?.points || 0; - // Sync logs for the current quarter + // Set membership status and date + setMembershipStatus(user?.member_type || "Member"); + if (user?.created) { + const createdDate = new Date(user.created); + setMemberSince(createdDate.toLocaleDateString(undefined, { + year: 'numeric', + month: 'long' + })); + } + + // Sync logs for the current quarter to calculate points change await dataSync.syncCollection( Collections.LOGS, `user_id = "${userId}" && created >= "${quarterStart.toISOString()}" && type = "update" && part = "event check-in"`, @@ -75,41 +87,38 @@ export function Stats() { // Calculate quarterly points const quarterlyPoints = logs.reduce((total, log) => { - const pointsMatch = log.message.match(/Awarded (\d+) points/); + const pointsMatch = log.message?.match(/Awarded (\d+) points/); if (pointsMatch) { return total + parseInt(pointsMatch[1]); } return total; }, 0); + // Set points change message + setPointsChange(quarterlyPoints > 0 ? `+${quarterlyPoints} this quarter` : "No activity"); + // Sync events collection await dataSync.syncCollection(Collections.EVENTS); // Get events from IndexedDB const events = await dataSync.getData(Collections.EVENTS); + // Count attended events const attendedEvents = events.filter(event => event.attendees?.some(attendee => attendee.user_id === userId) ); + setEventsAttended(attendedEvents.length); - const numEventsAttended = attendedEvents.length; - setEventsAttended(numEventsAttended); + // Count upcoming events (events that haven't ended yet) + const upcoming = events.filter(event => { + if (!event.end_date) return false; + const endDate = new Date(event.end_date); + return endDate > now && event.published; + }); + setUpcomingEvents(upcoming.length); + + // Set loyalty points setLoyaltyPoints(totalPoints); - - // Set points change message with quarterly points - setPointsChange(quarterlyPoints > 0 ? `+${quarterlyPoints} this quarter` : "No activity"); - - // Determine activity level - if (numEventsAttended >= 10) { - setActivityLevel("High"); - setActivityDesc("Very Active"); - } else if (numEventsAttended >= 5) { - setActivityLevel("Medium"); - setActivityDesc("Active Member"); - } else if (numEventsAttended >= 1) { - setActivityLevel("Low"); - setActivityDesc("Getting Started"); - } } catch (error) { console.error("Error fetching stats:", error); } finally { @@ -160,10 +169,10 @@ export function Stats() {
-
Activity Level
-
{activityLevel}
+
Upcoming Events
+
{upcomingEvents}
-
{activityDesc}
+
Available to attend