From 1896b3fb443772dbd4d3da6dec44c73815c83223 Mon Sep 17 00:00:00 2001 From: Sharon Dcruz Date: Tue, 23 Sep 2025 12:46:33 +0530 Subject: [PATCH] Cache Updated --- .../membershipStatusNotifications.ts | 189 +++++++++++++++++- 1 file changed, 188 insertions(+), 1 deletion(-) diff --git a/functions/src/notifications/membershipStatusNotifications.ts b/functions/src/notifications/membershipStatusNotifications.ts index cc4f5ae..27f8444 100644 --- a/functions/src/notifications/membershipStatusNotifications.ts +++ b/functions/src/notifications/membershipStatusNotifications.ts @@ -5,6 +5,7 @@ import * as admin from "firebase-admin"; const app = getAdmin(); const logger = getLogger(); const kTrainerRole = "Trainer"; +const CACHE_FOLDER = "gym_member_cache"; interface MembershipData { id?: string; @@ -44,6 +45,183 @@ interface PersonalTrainerAssign { gymId: string; } +interface MinimalCacheEntry { + membershipId: string; + userId: string; + status: string; + displayName: string; + email?: string | null; + phoneNumber?: string | null; + alternateContact?: string | null; + renewalDate?: string | null; + expirationDate?: string | null; + createdAt?: string | null; + daysUntilExpiry?: number | null; + hasPersonalTraining: boolean; + hasPartialPayment: boolean; + remainingAmount: number; + subscriptionId?: string | null; + lastUpdated: string; +} + +interface MinimalJsonCacheData { + gymId: string; + members: MinimalCacheEntry[]; + totalMembers: number; + lastUpdated: string; + cacheVersion: string; +} + +async function updateCacheForMembership(gymId: string, membershipId: string): Promise { + try { + const fileName = `${CACHE_FOLDER}/${gymId}.json`; + const file = app.storage().bucket().file(fileName); + + let existingData: MinimalJsonCacheData | null = null; + + try { + const [fileBuffer] = await file.download(); + existingData = JSON.parse(fileBuffer.toString()); + } catch (error) { + logger.warn(`Could not load existing cache for gym ${gymId}, skipping cache update: ${error}`); + return; + } + + if (!existingData || !existingData.members) { + logger.warn(`Invalid cache data for gym ${gymId}, skipping cache update`); + return; + } + + const membershipDoc = await app + .firestore() + .collection("memberships") + .doc(membershipId) + .get(); + + if (!membershipDoc.exists) { + logger.warn(`Membership ${membershipId} not found, skipping cache update`); + return; + } + + const membershipData = membershipDoc.data(); + const updatedEntry = await generateMinimalCacheEntry( + membershipData!.userId, + membershipId, + membershipData!, + gymId + ); + + const memberIndex = existingData.members.findIndex( + member => member.membershipId === membershipId + ); + + if (memberIndex >= 0) { + existingData.members[memberIndex] = updatedEntry; + } else { + existingData.members.push(updatedEntry); + } + + existingData.lastUpdated = new Date().toISOString(); + existingData.totalMembers = existingData.members.length; + + await file.save(JSON.stringify(existingData, null, 2), { + metadata: { + contentType: "application/json", + metadata: { + gymId: gymId, + totalMembers: existingData.totalMembers.toString(), + generatedAt: existingData.lastUpdated, + cacheVersion: existingData.cacheVersion, + }, + }, + }); + + logger.info(`Cache updated for membership ${membershipId} in gym ${gymId}`); + } catch (error) { + logger.error(`Error updating cache for membership ${membershipId}:`, error); + } +} + +async function generateMinimalCacheEntry( + userId: string, + membershipId: string, + membershipData: any, + gymId: string +): Promise { + try { + let firstName = ""; + let lastName = ""; + let email = ""; + let phoneNumber = ""; + let alternateContact = ""; + + try { + const clientFieldsSnapshot = await app + .firestore() + .collection("client_profiles") + .where("uid", "==", userId) + .get(); + + if (!clientFieldsSnapshot.empty) { + const fieldDoc = clientFieldsSnapshot.docs[0]; + const fieldData = fieldDoc.data(); + + const fields = fieldData.fields || {}; + firstName = fields["first-name"] || fieldData["first-name"] || ""; + lastName = fields["last-name"] || fieldData["last-name"] || ""; + email = fields["email"] || fieldData["email"] || ""; + phoneNumber = fields["phone-number"] || fieldData["phone-number"] || ""; + alternateContact = + fields["alternate-contact"] || fieldData["alternate-contact"] || ""; + } + } catch (error) { + logger.error(`Error getting fields for user ${userId}:`, error); + } + + const daysUntilExpiry = membershipData.daysUntilExpiry || null; + + const displayName = + firstName.length === 0 + ? "Unknown" + : lastName.length === 0 + ? firstName + : `${firstName} ${lastName}`; + + const isPartial = membershipData.isPartialPayment === true; + const remaining = membershipData.remainingAmount || 0; + const hasPartialPayment = isPartial && remaining > 0; + + const minimalEntry: MinimalCacheEntry = { + membershipId, + userId, + status: membershipData.status || "N/A", + displayName, + email: email || null, + phoneNumber: phoneNumber || null, + alternateContact: alternateContact || null, + renewalDate: null, + expirationDate: membershipData.expirationDate + ? membershipData.expirationDate.toDate().toISOString() + : null, + createdAt: membershipData.createdAt + ? membershipData.createdAt.toDate().toISOString() + : null, + daysUntilExpiry, + hasPersonalTraining: + membershipData.subscription?.hasPersonalTraining === true, + hasPartialPayment, + remainingAmount: remaining, + subscriptionId: membershipData.subscriptionId || null, + lastUpdated: new Date().toISOString(), + }; + + return minimalEntry; + } catch (error) { + logger.error("Error generating minimal cache entry:", error); + throw error; + } +} + export const checkExpiredMemberships = onSchedule( { schedule: "0 8,14,20 * * *", @@ -150,6 +328,7 @@ async function findExpiredMembershipsWithoutExpiryDate(): Promise< throw error; } } + async function updateExpiryDateForExpiredMembership( membershipId: string, membershipData: MembershipData @@ -184,6 +363,8 @@ async function updateExpiryDateForExpiredMembership( updatedAt: admin.firestore.FieldValue.serverTimestamp(), }); + await updateCacheForMembership(membershipData.gymId, membershipId); + logger.info( `Updated expiry date for expired membership ${membershipId}: ${expiryDate.toISOString()}` ); @@ -513,6 +694,8 @@ async function updateDaysUntilExpiryForAllMemberships(): Promise { .doc(doc.id) .update(updateData); + await updateCacheForMembership(data.gymId, doc.id); + logger.info( `Updated membership ${doc.id} with daysUntilExpiry: ${daysUntilExpiry}` ); @@ -665,6 +848,8 @@ async function processExpiredMembership( }); } + await updateCacheForMembership(membershipData.gymId, membershipId); + logger.info(`Marked membership ${membershipId} as EXPIRED`); await sendPlanExpiredNotification(membershipId, membershipData); @@ -697,6 +882,8 @@ async function processExpiringMembership( expirationDate: admin.firestore.Timestamp.fromDate(expiryDate), updatedAt: admin.firestore.FieldValue.serverTimestamp(), }); + + await updateCacheForMembership(membershipData.gymId, membershipId); } await sendPlanExpiringNotification(membershipId, membershipData); @@ -1067,4 +1254,4 @@ async function getGymName(gymId: string): Promise { logger.error(`Error getting gym name for gym ${gymId}:`, error); return "Unknown Gym"; } -} +} \ No newline at end of file