From 22b2f2adce2e4a06171008c3fa119ab1899358bc Mon Sep 17 00:00:00 2001 From: Sharon Dcruz Date: Thu, 18 Sep 2025 10:34:42 +0000 Subject: [PATCH 1/2] ClientsLoadingFix (#116) Reviewed-on: https://git.cosqnet.com/cosqnet/fitlien-services/pulls/116 Reviewed-by: Dhansh A S Co-authored-by: Sharon Dcruz Co-committed-by: Sharon Dcruz --- functions/src/index.ts | 1 - functions/src/memberCache/index.ts | 1 - functions/src/memberCache/memberCache.ts | 120 +++++++---------------- 3 files changed, 38 insertions(+), 84 deletions(-) diff --git a/functions/src/index.ts b/functions/src/index.ts index e03316d..b3ee3bf 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -26,7 +26,6 @@ export { export { generateMemberCache, updateTrainerAssignmentCache, - updateTimeSlotCache, getCachedMembers, rebuildGymCachee, batchRebuildCaches diff --git a/functions/src/memberCache/index.ts b/functions/src/memberCache/index.ts index 15ba89b..10c068d 100644 --- a/functions/src/memberCache/index.ts +++ b/functions/src/memberCache/index.ts @@ -1,7 +1,6 @@ export { generateMemberCache, updateTrainerAssignmentCache, - updateTimeSlotCache, getCachedMembers, rebuildGymCachee, batchRebuildCaches diff --git a/functions/src/memberCache/memberCache.ts b/functions/src/memberCache/memberCache.ts index 9a1d5bd..fb3ae5d 100644 --- a/functions/src/memberCache/memberCache.ts +++ b/functions/src/memberCache/memberCache.ts @@ -10,6 +10,7 @@ const CACHE_FOLDER = 'gym_member_cache'; interface MembershipData { gymId?: string; + userId?: string; status?: string; subscription?: { hasPersonalTraining?: boolean; @@ -36,21 +37,14 @@ interface PaymentData { interface TrainerAssignment { id?: string; membershipId?: string; - createdAt?: admin.firestore.Timestamp; - [key: string]: any; -} - -interface TimeSlot { - id?: string; - membershipId?: string; - startTime?: admin.firestore.Timestamp; - endTime?: admin.firestore.Timestamp; + timeSlot?: any[]; createdAt?: admin.firestore.Timestamp; [key: string]: any; } interface CacheEntry { - membershipId: string; + userId: string; + membershipId: string; memberData: { daysUntilExpiry?: number | null; hasPartialPayment: boolean; @@ -81,7 +75,7 @@ export const generateMemberCache = onDocumentWritten( async (event) => { try { const membershipId = event.params.membershipId; - const membershipData = event.data?.after?.exists + const membershipData = event.data?.after?.exists ? event.data.after.data() as MembershipData : null; @@ -144,45 +138,6 @@ export const updateTrainerAssignmentCache = onDocumentWritten( } ); -export const updateTimeSlotCache = onDocumentWritten( - { - region: "#{SERVICES_RGN}#", - document: "scheduled_trainings/{slotId}", - }, - async (event) => { - try { - const slotData = event.data?.after?.exists - ? event.data.after.data() as TimeSlot - : null; - const oldSlotData = event.data?.before?.exists - ? event.data.before.data() as TimeSlot - : null; - - const gymIds = new Set(); - - if (slotData?.membershipId) { - const gymId = await getGymIdFromMembershipId(slotData.membershipId); - if (gymId) gymIds.add(gymId); - } - - if (oldSlotData?.membershipId) { - const gymId = await getGymIdFromMembershipId(oldSlotData.membershipId); - if (gymId) gymIds.add(gymId); - } - - for (const gymId of gymIds) { - await rebuildGymCache(gymId); - } - - if (gymIds.size > 0) { - logger.info(`Updated time slot cache for ${gymIds.size} gym(s)`); - } - } catch (error) { - logger.error('Error updating time slot cache:', error); - } - } -); - export const getCachedMembers = onCall( { region: "#{SERVICES_RGN}#", @@ -302,7 +257,13 @@ async function rebuildGymCache(gymId: string): Promise { const batch = membershipsSnapshot.docs.slice(i, i + batchSize); const batchPromises = batch.map(async (doc) => { try { - return await generateCacheEntry(doc.id, doc.data() as MembershipData); + const membershipData = doc.data() as MembershipData; + const userId = membershipData.userId; + if (!userId) { + logger.warn(`Skipping member with ID ${doc.id} due to missing userId`); + return null; + } + return await generateCacheEntry(userId, doc.id, membershipData); } catch (error) { logger.error(`Error processing member ${doc.id}:`, error); return null; @@ -343,24 +304,30 @@ async function rebuildGymCache(gymId: string): Promise { } } -async function generateCacheEntry(membershipId: string, membershipData: MembershipData): Promise { +async function generateCacheEntry(userId: string, membershipId: string, membershipData: MembershipData): Promise { try { let fields: { [key: string]: string } = {}; try { const clientFieldsSnapshot = await app .firestore() .collection('client_profiles') - .where('membershipId', '==', membershipId) + .where('uid', '==', userId) .get(); if (!clientFieldsSnapshot.empty) { const fieldDoc = clientFieldsSnapshot.docs[0]; const fieldData = fieldDoc.data() as ClientFields; - fields = fieldData.fields || {}; + fields = { + 'first-name': fieldData.fields?.['first-name'] || '', + 'email': fieldData.fields?.['email'] || '', + 'phone-number': fieldData.fields?.['phone-number'] || '', + 'alternate-contact': fieldData.fields?.['alternate-contact'] || '', + }; } } catch (error) { - logger.error(`Error getting fields for ${membershipId}:`, error); + logger.error(`Error getting fields for user ${userId}:`, error); } + let renewalDate: Date | null = null; let daysUntilExpiry: number | null = null; @@ -388,7 +355,7 @@ async function generateCacheEntry(membershipId: string, membershipData: Membersh } } } catch (error) { - logger.error(`Error getting renewal date for ${membershipId}:`, error); + logger.error(`Error getting renewal date for membership ${membershipId}:`, error); } } @@ -407,42 +374,31 @@ async function generateCacheEntry(membershipId: string, membershipData: Membersh .where('membershipId', '==', membershipId) .get(); - trainerAssignments = assignmentsSnapshot.docs.map(doc => { + assignmentsSnapshot.docs.forEach(doc => { const data = doc.data() as TrainerAssignment; - return { - id: doc.id, - ...data, - createdAt: data.createdAt ? data.createdAt.toDate().toISOString() : null - }; - }); - - const timeSlotsSnapshot = await app - .firestore() - .collection('scheduled_trainings') - .where('membershipId', '==', membershipId) - .get(); - - timeSlots = timeSlotsSnapshot.docs.map(doc => { - const data = doc.data() as TimeSlot; - return { - id: doc.id, - ...data, - startTime: data.startTime ? data.startTime.toDate().toISOString() : null, - endTime: data.endTime ? data.endTime.toDate().toISOString() : null, - createdAt: data.createdAt ? data.createdAt.toDate().toISOString() : null - }; + if (data.timeSlot && Array.isArray(data.timeSlot)) { + timeSlots.push(...data.timeSlot); + } + trainerAssignments.push({ id: doc.id }); }); } catch (error) { - logger.error(`Error getting trainer data for ${membershipId}:`, error); + logger.error(`Error getting trainer data for membership ${membershipId}:`, error); } } const cacheEntry: CacheEntry = { + userId, membershipId, memberData: { - ...membershipData, - daysUntilExpiry, + status: membershipData.status, + subscription: { + hasPersonalTraining: membershipData.subscription?.hasPersonalTraining, + frequency: membershipData.subscription?.frequency, + }, + remainingAmount: membershipData.remainingAmount, + isPartialPayment: membershipData.isPartialPayment, hasPartialPayment, + daysUntilExpiry, createdAt: membershipData.createdAt ? membershipData.createdAt.toDate().toISOString() : null, updatedAt: membershipData.updatedAt ? membershipData.updatedAt.toDate().toISOString() : null }, From 2963b23b619ed90cec9e628e337ed811bb758eee Mon Sep 17 00:00:00 2001 From: Sharon Dcruz Date: Thu, 18 Sep 2025 12:21:32 +0000 Subject: [PATCH 2/2] ClientsLoadingFix (#117) Reviewed-on: https://git.cosqnet.com/cosqnet/fitlien-services/pulls/117 Reviewed-by: Dhansh A S Co-authored-by: Sharon Dcruz Co-committed-by: Sharon Dcruz --- functions/src/index.ts | 3 +-- functions/src/memberCache/index.ts | 2 +- functions/src/memberCache/memberCache.ts | 18 +++++++++--------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/functions/src/index.ts b/functions/src/index.ts index b3ee3bf..6163325 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -22,11 +22,10 @@ export { esslDeleteUser, esslGetEmployeePunchLogs } from './dooraccess'; -// Add member cache functions export { generateMemberCache, updateTrainerAssignmentCache, getCachedMembers, - rebuildGymCachee, + rebuildGymCache, batchRebuildCaches } from './memberCache'; \ No newline at end of file diff --git a/functions/src/memberCache/index.ts b/functions/src/memberCache/index.ts index 10c068d..fb4b6bb 100644 --- a/functions/src/memberCache/index.ts +++ b/functions/src/memberCache/index.ts @@ -2,6 +2,6 @@ export { generateMemberCache, updateTrainerAssignmentCache, getCachedMembers, - rebuildGymCachee, + rebuildGymCache, batchRebuildCaches } from './memberCache'; \ No newline at end of file diff --git a/functions/src/memberCache/memberCache.ts b/functions/src/memberCache/memberCache.ts index fb3ae5d..fa3a54c 100644 --- a/functions/src/memberCache/memberCache.ts +++ b/functions/src/memberCache/memberCache.ts @@ -90,7 +90,7 @@ export const generateMemberCache = onDocumentWritten( return; } - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); logger.info(`JSON cache updated for gym ${gymId} after member ${membershipId} change`); } catch (error) { @@ -126,7 +126,7 @@ export const updateTrainerAssignmentCache = onDocumentWritten( } for (const gymId of gymIds) { - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); } if (gymIds.size > 0) { @@ -167,7 +167,7 @@ export const getCachedMembers = onCall( const fiveMinutes = 5 * 60 * 1000; if (forceRefresh || !fileExists || fileAge > fiveMinutes) { logger.info(`Rebuilding cache for gym ${gymId} - forceRefresh: ${forceRefresh}, fileExists: ${fileExists}, fileAge: ${fileAge}ms`); - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); } try { @@ -178,7 +178,7 @@ export const getCachedMembers = onCall( } catch (error) { logger.error(`Error reading cache file for gym ${gymId}:`, error); - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); const [fileBuffer] = await file.download(); const jsonData: JsonCacheData = JSON.parse(fileBuffer.toString()); return jsonData.members || []; @@ -190,7 +190,7 @@ export const getCachedMembers = onCall( } ); -export const rebuildGymCachee = onCall( +export const rebuildGymCache = onCall( { region: "#{SERVICES_RGN}#", }, @@ -201,7 +201,7 @@ export const rebuildGymCachee = onCall( throw new HttpsError('invalid-argument', 'gymId is required'); } - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); return { success: true, message: `JSON cache rebuilt for gym ${gymId}` }; } ); @@ -222,7 +222,7 @@ export const batchRebuildCaches = onCall( for (const gymId of gymIds) { try { - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); results.push({ gymId, status: 'success' }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -239,7 +239,7 @@ export const batchRebuildCaches = onCall( } ); -async function rebuildGymCache(gymId: string): Promise { +async function rebuildGymCachee(gymId: string): Promise { try { logger.info(`Starting JSON cache rebuild for gym: ${gymId}`); @@ -450,7 +450,7 @@ async function rebuildGymCacheFromDeletion(membershipId: string): Promise if (memberExists) { const gymId = file.name.replace(`${CACHE_FOLDER}/`, '').replace('.json', ''); logger.info(`Rebuilding cache for gym ${gymId} after member ${membershipId} deletion`); - await rebuildGymCache(gymId); + await rebuildGymCachee(gymId); break; } } catch (error) {