fix database fetching
This commit is contained in:
parent
10d3f32fbc
commit
733f3dc931
6 changed files with 274 additions and 84 deletions
|
@ -7,7 +7,7 @@ import { DataSyncService } from "../../../scripts/database/DataSyncService";
|
|||
import { Collections } from "../../../schemas/pocketbase/schema";
|
||||
import { Icon } from "@iconify/react";
|
||||
import toast from "react-hot-toast";
|
||||
import type { Event, EventAttendee } from "../../../schemas/pocketbase";
|
||||
import type { Event, EventAttendee, LimitedUser } from "../../../schemas/pocketbase";
|
||||
|
||||
// Extended Event interface with additional properties needed for this component
|
||||
interface ExtendedEvent extends Event {
|
||||
|
@ -264,20 +264,48 @@ const EventCheckIn = () => {
|
|||
totalPoints += attendee.points_earned || 0;
|
||||
});
|
||||
|
||||
// Log the points update
|
||||
// console.log(`Updating user points to: ${totalPoints}`);
|
||||
// Update the LimitedUser record with the new points total
|
||||
try {
|
||||
// Try to get the LimitedUser record to check if it exists
|
||||
let limitedUserExists = false;
|
||||
try {
|
||||
const limitedUser = await get.getOne(Collections.LIMITED_USERS, userId);
|
||||
limitedUserExists = !!limitedUser;
|
||||
} catch (e) {
|
||||
// Record doesn't exist
|
||||
limitedUserExists = false;
|
||||
}
|
||||
|
||||
// Update the user record with the new total points
|
||||
await update.updateFields(Collections.USERS, userId, {
|
||||
points: totalPoints
|
||||
});
|
||||
// Create or update the LimitedUser record
|
||||
if (limitedUserExists) {
|
||||
await update.updateFields(Collections.LIMITED_USERS, userId, {
|
||||
points: JSON.stringify(totalPoints),
|
||||
total_events_attended: JSON.stringify(userAttendance.totalItems)
|
||||
});
|
||||
} else {
|
||||
// Get user data to create LimitedUser record
|
||||
const userData = await get.getOne(Collections.USERS, userId);
|
||||
if (userData) {
|
||||
await update.create(Collections.LIMITED_USERS, {
|
||||
id: userId, // Use same ID as user record
|
||||
name: userData.name || 'Anonymous User',
|
||||
major: userData.major || '',
|
||||
points: JSON.stringify(totalPoints),
|
||||
total_events_attended: JSON.stringify(userAttendance.totalItems)
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update LimitedUser record:', error);
|
||||
}
|
||||
|
||||
// Ensure local data is in sync with backend
|
||||
// First sync the new attendance record
|
||||
await dataSync.syncCollection(Collections.EVENT_ATTENDEES);
|
||||
|
||||
// Then sync the updated user data to ensure points are correctly reflected locally
|
||||
// Then sync the updated user and LimitedUser data
|
||||
await dataSync.syncCollection(Collections.USERS);
|
||||
await dataSync.syncCollection(Collections.LIMITED_USERS);
|
||||
|
||||
// Clear event code from local storage
|
||||
await dataSync.clearEventCode();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { Get } from '../../../scripts/pocketbase/Get';
|
||||
import { Authentication } from '../../../scripts/pocketbase/Authentication';
|
||||
import { Collections } from '../../../schemas/pocketbase/schema';
|
||||
import type { LimitedUser } from '../../../schemas/pocketbase/schema';
|
||||
|
||||
interface LeaderboardStats {
|
||||
totalUsers: number;
|
||||
|
@ -54,34 +56,50 @@ export default function LeaderboardStats() {
|
|||
setLoading(true);
|
||||
|
||||
// Get all users without sorting - we'll sort on client side
|
||||
const response = await get.getList('limitedUser', 1, 500, '', '', {
|
||||
const response = await get.getList(Collections.LIMITED_USERS, 1, 500, '', '', {
|
||||
fields: ['id', 'name', 'points']
|
||||
});
|
||||
|
||||
// Parse points from JSON string and convert to number
|
||||
const processedUsers = response.items.map((user: Partial<LimitedUser>) => {
|
||||
let pointsValue = 0;
|
||||
try {
|
||||
if (user.points) {
|
||||
// Parse the JSON string to get the points value
|
||||
const pointsData = JSON.parse(user.points);
|
||||
pointsValue = typeof pointsData === 'number' ? pointsData : 0;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing points data:', e);
|
||||
}
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
parsedPoints: pointsValue
|
||||
};
|
||||
});
|
||||
|
||||
// Filter out users with no points for the leaderboard stats
|
||||
const leaderboardUsers = response.items
|
||||
.filter((user: any) =>
|
||||
user.points !== undefined &&
|
||||
user.points !== null &&
|
||||
user.points > 0
|
||||
)
|
||||
const leaderboardUsers = processedUsers
|
||||
.filter(user => user.parsedPoints > 0)
|
||||
// Sort by points descending
|
||||
.sort((a: any, b: any) => b.points - a.points);
|
||||
.sort((a, b) => b.parsedPoints - a.parsedPoints);
|
||||
|
||||
const totalUsers = leaderboardUsers.length;
|
||||
const totalPoints = leaderboardUsers.reduce((sum: number, user: any) => sum + (user.points || 0), 0);
|
||||
const topScore = leaderboardUsers.length > 0 ? leaderboardUsers[0].points : 0;
|
||||
const totalPoints = leaderboardUsers.reduce((sum: number, user) => sum + user.parsedPoints, 0);
|
||||
const topScore = leaderboardUsers.length > 0 ? leaderboardUsers[0].parsedPoints : 0;
|
||||
|
||||
// Find current user's points and rank - BUT don't filter by points > 0 for the current user
|
||||
let yourPoints = 0;
|
||||
let yourRank = null;
|
||||
|
||||
if (isAuthenticated && currentUserId) {
|
||||
// Look for the current user in ALL users, not just those with points > 0
|
||||
const currentUser = response.items.find((user: any) => user.id === currentUserId);
|
||||
// Look for the current user in ALL processed users, not just those with points > 0
|
||||
const currentUser = processedUsers.find(user => user.id === currentUserId);
|
||||
|
||||
if (currentUser) {
|
||||
yourPoints = currentUser.points || 0;
|
||||
yourPoints = currentUser.parsedPoints || 0;
|
||||
|
||||
// Only calculate rank if user has points
|
||||
if (yourPoints > 0) {
|
||||
|
@ -119,7 +137,7 @@ export default function LeaderboardStats() {
|
|||
};
|
||||
|
||||
fetchStats();
|
||||
}, [isAuthenticated, currentUserId]);
|
||||
}, [get, isAuthenticated, currentUserId]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { Get } from '../../../scripts/pocketbase/Get';
|
||||
import { Authentication } from '../../../scripts/pocketbase/Authentication';
|
||||
import type { User } from '../../../schemas/pocketbase/schema';
|
||||
import type { User, LimitedUser } from '../../../schemas/pocketbase/schema';
|
||||
import { Collections } from '../../../schemas/pocketbase/schema';
|
||||
|
||||
interface LeaderboardUser {
|
||||
id: string;
|
||||
|
@ -63,21 +64,44 @@ export default function LeaderboardTable() {
|
|||
setLoading(true);
|
||||
|
||||
// Fetch users without sorting - we'll sort on client side
|
||||
const response = await get.getList('limitedUser', 1, 100, '', '', {
|
||||
const response = await get.getList(Collections.LIMITED_USERS, 1, 100, '', '', {
|
||||
fields: ['id', 'name', 'points', 'avatar', 'major']
|
||||
});
|
||||
|
||||
// First get the current user separately so we can include them even if they have 0 points
|
||||
let currentUserData = null;
|
||||
if (isAuthenticated && currentUserId) {
|
||||
currentUserData = response.items.find((user: Partial<User>) => user.id === currentUserId);
|
||||
currentUserData = response.items.find((user: Partial<LimitedUser>) => user.id === currentUserId);
|
||||
}
|
||||
|
||||
// Parse points from JSON string and convert to number
|
||||
const processedUsers = response.items.map((user: any) => {
|
||||
let pointsValue = 0;
|
||||
try {
|
||||
if (user.points) {
|
||||
// Parse the JSON string to get the points value
|
||||
const pointsData = JSON.parse(user.points);
|
||||
pointsValue = typeof pointsData === 'number' ? pointsData : 0;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing points data:', e);
|
||||
}
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
major: user.major,
|
||||
avatar: user.avatar, // Include avatar if it exists
|
||||
points: user.points,
|
||||
parsedPoints: pointsValue
|
||||
};
|
||||
});
|
||||
|
||||
// Filter and map to our leaderboard user format, and sort client-side
|
||||
let leaderboardUsers = response.items
|
||||
.filter((user: Partial<User>) => user.points !== undefined && user.points !== null && user.points > 0)
|
||||
.sort((a: Partial<User>, b: Partial<User>) => (b.points || 0) - (a.points || 0))
|
||||
.map((user: Partial<User>, index: number) => {
|
||||
let leaderboardUsers = processedUsers
|
||||
.filter(user => user.parsedPoints > 0)
|
||||
.sort((a, b) => (b.parsedPoints || 0) - (a.parsedPoints || 0))
|
||||
.map((user, index: number) => {
|
||||
// Check if this is the current user
|
||||
if (isAuthenticated && user.id === currentUserId) {
|
||||
setCurrentUserRank(index + 1);
|
||||
|
@ -86,7 +110,7 @@ export default function LeaderboardTable() {
|
|||
return {
|
||||
id: user.id || '',
|
||||
name: user.name || 'Anonymous User',
|
||||
points: user.points || 0,
|
||||
points: user.parsedPoints,
|
||||
avatar: user.avatar,
|
||||
major: user.major
|
||||
};
|
||||
|
@ -94,16 +118,20 @@ export default function LeaderboardTable() {
|
|||
|
||||
// Include current user even if they have 0 points,
|
||||
// but don't include in ranking if they have no points
|
||||
if (isAuthenticated && currentUserData &&
|
||||
!leaderboardUsers.some(user => user.id === currentUserId)) {
|
||||
// User isn't already in the list (has 0 points)
|
||||
leaderboardUsers.push({
|
||||
id: currentUserData.id || '',
|
||||
name: currentUserData.name || 'Anonymous User',
|
||||
points: currentUserData.points || 0,
|
||||
avatar: currentUserData.avatar,
|
||||
major: currentUserData.major
|
||||
});
|
||||
if (isAuthenticated && currentUserId) {
|
||||
// Find current user in processed users
|
||||
const currentUserProcessed = processedUsers.find(user => user.id === currentUserId);
|
||||
|
||||
// If current user exists and isn't already in the leaderboard (has 0 points)
|
||||
if (currentUserProcessed && !leaderboardUsers.some(user => user.id === currentUserId)) {
|
||||
leaderboardUsers.push({
|
||||
id: currentUserProcessed.id || '',
|
||||
name: currentUserProcessed.name || 'Anonymous User',
|
||||
points: currentUserProcessed.parsedPoints || 0,
|
||||
avatar: currentUserProcessed.avatar,
|
||||
major: currentUserProcessed.major
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setUsers(leaderboardUsers);
|
||||
|
@ -117,7 +145,7 @@ export default function LeaderboardTable() {
|
|||
};
|
||||
|
||||
fetchLeaderboard();
|
||||
}, [isAuthenticated, currentUserId]);
|
||||
}, [get, isAuthenticated, currentUserId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchQuery.trim() === '') {
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Authentication } from "../../../scripts/pocketbase/Authentication";
|
|||
import { Update } from "../../../scripts/pocketbase/Update";
|
||||
import { FileManager } from "../../../scripts/pocketbase/FileManager";
|
||||
import { SendLog } from "../../../scripts/pocketbase/SendLog";
|
||||
import { Realtime } from "../../../scripts/pocketbase/Realtime";
|
||||
import FilePreview from "../universal/FilePreview";
|
||||
import type { Event as SchemaEvent, AttendeeEntry } from "../../../schemas/pocketbase";
|
||||
import { DataSyncService } from '../../../scripts/database/DataSyncService';
|
||||
|
@ -240,7 +241,15 @@ const EventForm = memo(({
|
|||
// Show error for rejected files
|
||||
if (rejectedFiles.length > 0) {
|
||||
const errorMessage = `The following files were not added:\n${rejectedFiles.map(f => `${f.name}: ${f.reason}`).join('\n')}`;
|
||||
toast.error(errorMessage);
|
||||
// Use toast with custom styling to ensure visibility above modal
|
||||
toast.error(errorMessage, {
|
||||
duration: 5000,
|
||||
style: {
|
||||
zIndex: 9999, // Ensure it's above the modal
|
||||
maxWidth: '500px',
|
||||
whiteSpace: 'pre-line' // Preserve line breaks
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSelectedFiles(newFiles);
|
||||
|
@ -293,6 +302,31 @@ const EventForm = memo(({
|
|||
>
|
||||
<Icon icon="heroicons:eye" className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-xs"
|
||||
onClick={async () => {
|
||||
if (event?.id) {
|
||||
try {
|
||||
// Get file URL with token for protected files
|
||||
const url = await fileManager.getFileUrlWithToken(
|
||||
"events",
|
||||
event.id,
|
||||
filename,
|
||||
true
|
||||
);
|
||||
|
||||
// Open file in new tab
|
||||
window.open(url, '_blank');
|
||||
} catch (error) {
|
||||
console.error("Failed to open file:", error);
|
||||
toast.error("Failed to open file. Please try again.");
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon="heroicons:arrow-top-right-on-square" className="h-4 w-4" />
|
||||
</button>
|
||||
<div className="text-error">
|
||||
{filesToDelete.has(filename) ? (
|
||||
<button
|
||||
|
@ -571,7 +605,8 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
auth: Authentication.getInstance(),
|
||||
update: Update.getInstance(),
|
||||
fileManager: FileManager.getInstance(),
|
||||
sendLog: SendLog.getInstance()
|
||||
sendLog: SendLog.getInstance(),
|
||||
realtime: Realtime.getInstance()
|
||||
}), []);
|
||||
|
||||
// Handle field changes
|
||||
|
@ -590,17 +625,35 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
const initializeEventData = useCallback(async (eventId: string) => {
|
||||
try {
|
||||
if (eventId) {
|
||||
// Show loading state
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Clear cache to ensure fresh data
|
||||
const dataSync = DataSyncService.getInstance();
|
||||
await dataSync.clearCache();
|
||||
|
||||
// Fetch fresh event data
|
||||
const eventData = await services.get.getOne<Event>(Collections.EVENTS, eventId);
|
||||
// Fetch fresh event data with expanded relations if needed
|
||||
const eventData = await services.get.getOne<Event>(
|
||||
Collections.EVENTS,
|
||||
eventId,
|
||||
{
|
||||
disableAutoCancellation: true,
|
||||
// Add any fields to expand if needed
|
||||
// expand: ['related_field1', 'related_field2']
|
||||
}
|
||||
);
|
||||
|
||||
if (!eventData) {
|
||||
throw new Error("Event not found");
|
||||
}
|
||||
|
||||
// Log successful data fetch
|
||||
await services.sendLog.send(
|
||||
"view",
|
||||
"event",
|
||||
`Loaded event data: ${eventData.event_name} (${eventId})`
|
||||
);
|
||||
|
||||
// Ensure dates are properly formatted for datetime-local input
|
||||
if (eventData.start_date) {
|
||||
// Convert to Date object first to ensure proper formatting
|
||||
|
@ -631,8 +684,36 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
has_food: eventData.has_food || false
|
||||
});
|
||||
|
||||
// Set up realtime subscription for this event
|
||||
const realtime = services.realtime;
|
||||
|
||||
// Define the RealtimeEvent type for proper typing
|
||||
interface RealtimeEvent<T> {
|
||||
action: "create" | "update" | "delete";
|
||||
record: T;
|
||||
}
|
||||
|
||||
const subscriptionId = realtime.subscribeToRecord<RealtimeEvent<Event>>(
|
||||
Collections.EVENTS,
|
||||
eventId,
|
||||
(data) => {
|
||||
if (data.action === "update") {
|
||||
// Auto-refresh data when event is updated elsewhere
|
||||
initializeEventData(eventId);
|
||||
toast.success("Event data has been updated");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Store subscription ID for cleanup
|
||||
(window as any).eventSubscriptionId = subscriptionId;
|
||||
|
||||
// console.log("Event data loaded successfully:", eventData);
|
||||
} else {
|
||||
// Creating a new event
|
||||
const now = new Date();
|
||||
const oneHourLater = new Date(now.getTime() + 60 * 60 * 1000);
|
||||
|
||||
setEvent({
|
||||
id: '',
|
||||
created: '',
|
||||
|
@ -643,8 +724,8 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
location: '',
|
||||
files: [],
|
||||
points_to_reward: 0,
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
start_date: Get.formatLocalDate(now, false),
|
||||
end_date: Get.formatLocalDate(oneHourLater, false),
|
||||
published: false,
|
||||
has_food: false
|
||||
});
|
||||
|
@ -656,8 +737,10 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
} catch (error) {
|
||||
console.error("Failed to initialize event data:", error);
|
||||
toast.error("Failed to load event data. Please try again.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [services.get]);
|
||||
}, [services]);
|
||||
|
||||
// Expose initializeEventData to window
|
||||
useEffect(() => {
|
||||
|
@ -698,6 +781,12 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
// Clean up realtime subscription if it exists
|
||||
if ((window as any).eventSubscriptionId) {
|
||||
services.realtime.unsubscribe((window as any).eventSubscriptionId);
|
||||
delete (window as any).eventSubscriptionId;
|
||||
}
|
||||
|
||||
setEvent({
|
||||
id: "",
|
||||
created: "",
|
||||
|
@ -719,12 +808,24 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
setPreviewUrl("");
|
||||
setPreviewFilename("");
|
||||
|
||||
// Clear file input element to reset filename display
|
||||
const fileInput = document.querySelector('input[name="editEventFiles"]') as HTMLInputElement;
|
||||
if (fileInput) {
|
||||
fileInput.value = "";
|
||||
}
|
||||
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (modal) modal.close();
|
||||
}, [hasUnsavedChanges, isSubmitting]);
|
||||
}, [hasUnsavedChanges, isSubmitting, services.realtime]);
|
||||
|
||||
// Function to close modal after saving (without confirmation)
|
||||
const closeModalAfterSave = useCallback(() => {
|
||||
// Clean up realtime subscription if it exists
|
||||
if ((window as any).eventSubscriptionId) {
|
||||
services.realtime.unsubscribe((window as any).eventSubscriptionId);
|
||||
delete (window as any).eventSubscriptionId;
|
||||
}
|
||||
|
||||
setEvent({
|
||||
id: "",
|
||||
created: "",
|
||||
|
@ -746,9 +847,15 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
setPreviewUrl("");
|
||||
setPreviewFilename("");
|
||||
|
||||
// Reset the file input element to clear the filename display
|
||||
const fileInput = document.querySelector('input[name="editEventFiles"]') as HTMLInputElement;
|
||||
if (fileInput) {
|
||||
fileInput.value = "";
|
||||
}
|
||||
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (modal) modal.close();
|
||||
}, []);
|
||||
}, [services.realtime]);
|
||||
|
||||
const handleSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { Authentication } from "../../../scripts/pocketbase/Authentication";
|
||||
import { Collections } from "../../../schemas/pocketbase/schema";
|
||||
import type { Event, User } from "../../../schemas/pocketbase";
|
||||
import type { Event, User, LimitedUser } from "../../../schemas/pocketbase";
|
||||
import { Get } from "../../../scripts/pocketbase/Get";
|
||||
import type { EventAttendee } from "../../../schemas/pocketbase";
|
||||
import { Update } from "../../../scripts/pocketbase/Update";
|
||||
|
||||
// Extended User interface with points property
|
||||
// Extended User interface with member_type property
|
||||
interface ExtendedUser extends User {
|
||||
points?: number;
|
||||
member_type?: string;
|
||||
}
|
||||
|
||||
|
@ -83,47 +82,44 @@ export function Stats() {
|
|||
|
||||
setEventsAttended(attendedEvents.totalItems);
|
||||
|
||||
// Get user points - either from the user record or calculate from attendees
|
||||
// Calculate points from attendees
|
||||
let totalPoints = 0;
|
||||
|
||||
// Calculate quarterly points
|
||||
const quarterStartDate = getCurrentQuarterStartDate();
|
||||
let pointsThisQuarter = 0;
|
||||
|
||||
// If user has points field, use that for total points
|
||||
if (currentUser && currentUser.points !== undefined) {
|
||||
totalPoints = currentUser.points;
|
||||
// Calculate both total and quarterly points from attendees
|
||||
attendedEvents.items.forEach(attendee => {
|
||||
const points = attendee.points_earned || 0;
|
||||
totalPoints += points;
|
||||
|
||||
// Still need to calculate quarterly points from attendees
|
||||
attendedEvents.items.forEach(attendee => {
|
||||
const checkinDate = new Date(attendee.time_checked_in);
|
||||
if (checkinDate >= quarterStartDate) {
|
||||
pointsThisQuarter += attendee.points_earned || 0;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Calculate both total and quarterly points from attendees
|
||||
attendedEvents.items.forEach(attendee => {
|
||||
const points = attendee.points_earned || 0;
|
||||
totalPoints += points;
|
||||
const checkinDate = new Date(attendee.time_checked_in);
|
||||
if (checkinDate >= quarterStartDate) {
|
||||
pointsThisQuarter += points;
|
||||
}
|
||||
});
|
||||
|
||||
const checkinDate = new Date(attendee.time_checked_in);
|
||||
if (checkinDate >= quarterStartDate) {
|
||||
pointsThisQuarter += points;
|
||||
}
|
||||
});
|
||||
// Try to get the LimitedUser record to check if points match
|
||||
try {
|
||||
const limitedUserRecord = await get.getOne(
|
||||
Collections.LIMITED_USERS,
|
||||
userId
|
||||
);
|
||||
|
||||
// Update the user record with calculated points if needed
|
||||
if (currentUser) {
|
||||
if (limitedUserRecord && limitedUserRecord.points) {
|
||||
try {
|
||||
const update = Update.getInstance();
|
||||
await update.updateFields(Collections.USERS, currentUser.id, {
|
||||
points: totalPoints
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error updating user points:", error);
|
||||
// Parse the points JSON string
|
||||
const parsedPoints = JSON.parse(limitedUserRecord.points);
|
||||
if (parsedPoints !== totalPoints) {
|
||||
console.log(`Points mismatch: LimitedUser has ${parsedPoints}, calculated ${totalPoints}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing points from LimitedUser:', e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// LimitedUser record might not exist yet, that's okay
|
||||
}
|
||||
|
||||
setPointsEarned(totalPoints);
|
||||
|
|
|
@ -32,7 +32,7 @@ export interface User extends BaseRecord {
|
|||
major?: string;
|
||||
zelle_information?: string;
|
||||
last_login?: string;
|
||||
points?: number; // Total points earned from events
|
||||
// points?: number; // Total points earned from events (DEPRECATED)
|
||||
notification_preferences?: string; // JSON string of notification settings
|
||||
display_preferences?: string; // JSON string of display settings (theme, font size, etc.)
|
||||
accessibility_settings?: string; // JSON string of accessibility settings (color blind mode, reduced motion)
|
||||
|
@ -41,6 +41,18 @@ export interface User extends BaseRecord {
|
|||
requested_email?: boolean; // Whether the user has requested an IEEE email address
|
||||
}
|
||||
|
||||
/**
|
||||
* Limited User Collection
|
||||
* Represents limited user information for public display
|
||||
* Collection ID: pbc_2802685943
|
||||
*/
|
||||
export interface LimitedUser extends BaseRecord {
|
||||
name: string;
|
||||
major: string;
|
||||
points: string; // JSON string
|
||||
total_events_attended: string; // JSON string
|
||||
}
|
||||
|
||||
/**
|
||||
* Events Collection
|
||||
* Represents events created in the system
|
||||
|
@ -96,7 +108,7 @@ export interface EventRequest extends BaseRecord {
|
|||
event_description: string;
|
||||
flyers_needed: boolean;
|
||||
flyer_type?: string[]; // digital_with_social, digital_no_social, physical_with_advertising, physical_no_advertising, newsletter, other
|
||||
other_flyer_type?: string;
|
||||
other_flyer_type?: string;
|
||||
flyer_advertising_start_date?: string;
|
||||
flyer_additional_requests?: string;
|
||||
photography_needed: boolean;
|
||||
|
@ -217,6 +229,7 @@ export const Collections = {
|
|||
REIMBURSEMENTS: "reimbursement",
|
||||
RECEIPTS: "receipts",
|
||||
SPONSORS: "sponsors",
|
||||
LIMITED_USERS: "limitedUser",
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue