ClientsLoadingFix #117
@ -22,12 +22,10 @@ export {
|
|||||||
esslDeleteUser, esslGetEmployeePunchLogs
|
esslDeleteUser, esslGetEmployeePunchLogs
|
||||||
} from './dooraccess';
|
} from './dooraccess';
|
||||||
|
|
||||||
// Add member cache functions
|
|
||||||
export {
|
export {
|
||||||
generateMemberCache,
|
generateMemberCache,
|
||||||
updateTrainerAssignmentCache,
|
updateTrainerAssignmentCache,
|
||||||
updateTimeSlotCache,
|
|
||||||
getCachedMembers,
|
getCachedMembers,
|
||||||
rebuildGymCachee,
|
rebuildGymCache,
|
||||||
batchRebuildCaches
|
batchRebuildCaches
|
||||||
} from './memberCache';
|
} from './memberCache';
|
||||||
@ -1,8 +1,7 @@
|
|||||||
export {
|
export {
|
||||||
generateMemberCache,
|
generateMemberCache,
|
||||||
updateTrainerAssignmentCache,
|
updateTrainerAssignmentCache,
|
||||||
updateTimeSlotCache,
|
|
||||||
getCachedMembers,
|
getCachedMembers,
|
||||||
rebuildGymCachee,
|
rebuildGymCache,
|
||||||
batchRebuildCaches
|
batchRebuildCaches
|
||||||
} from './memberCache';
|
} from './memberCache';
|
||||||
@ -10,6 +10,7 @@ const CACHE_FOLDER = 'gym_member_cache';
|
|||||||
|
|
||||||
interface MembershipData {
|
interface MembershipData {
|
||||||
gymId?: string;
|
gymId?: string;
|
||||||
|
userId?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
subscription?: {
|
subscription?: {
|
||||||
hasPersonalTraining?: boolean;
|
hasPersonalTraining?: boolean;
|
||||||
@ -36,21 +37,14 @@ interface PaymentData {
|
|||||||
interface TrainerAssignment {
|
interface TrainerAssignment {
|
||||||
id?: string;
|
id?: string;
|
||||||
membershipId?: string;
|
membershipId?: string;
|
||||||
createdAt?: admin.firestore.Timestamp;
|
timeSlot?: any[];
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimeSlot {
|
|
||||||
id?: string;
|
|
||||||
membershipId?: string;
|
|
||||||
startTime?: admin.firestore.Timestamp;
|
|
||||||
endTime?: admin.firestore.Timestamp;
|
|
||||||
createdAt?: admin.firestore.Timestamp;
|
createdAt?: admin.firestore.Timestamp;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CacheEntry {
|
interface CacheEntry {
|
||||||
membershipId: string;
|
userId: string;
|
||||||
|
membershipId: string;
|
||||||
memberData: {
|
memberData: {
|
||||||
daysUntilExpiry?: number | null;
|
daysUntilExpiry?: number | null;
|
||||||
hasPartialPayment: boolean;
|
hasPartialPayment: boolean;
|
||||||
@ -81,7 +75,7 @@ export const generateMemberCache = onDocumentWritten(
|
|||||||
async (event) => {
|
async (event) => {
|
||||||
try {
|
try {
|
||||||
const membershipId = event.params.membershipId;
|
const membershipId = event.params.membershipId;
|
||||||
const membershipData = event.data?.after?.exists
|
const membershipData = event.data?.after?.exists
|
||||||
? event.data.after.data() as MembershipData
|
? event.data.after.data() as MembershipData
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@ -96,7 +90,7 @@ export const generateMemberCache = onDocumentWritten(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
|
|
||||||
logger.info(`JSON cache updated for gym ${gymId} after member ${membershipId} change`);
|
logger.info(`JSON cache updated for gym ${gymId} after member ${membershipId} change`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -132,7 +126,7 @@ export const updateTrainerAssignmentCache = onDocumentWritten(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const gymId of gymIds) {
|
for (const gymId of gymIds) {
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gymIds.size > 0) {
|
if (gymIds.size > 0) {
|
||||||
@ -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<string>();
|
|
||||||
|
|
||||||
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(
|
export const getCachedMembers = onCall(
|
||||||
{
|
{
|
||||||
region: "#{SERVICES_RGN}#",
|
region: "#{SERVICES_RGN}#",
|
||||||
@ -212,7 +167,7 @@ export const getCachedMembers = onCall(
|
|||||||
const fiveMinutes = 5 * 60 * 1000;
|
const fiveMinutes = 5 * 60 * 1000;
|
||||||
if (forceRefresh || !fileExists || fileAge > fiveMinutes) {
|
if (forceRefresh || !fileExists || fileAge > fiveMinutes) {
|
||||||
logger.info(`Rebuilding cache for gym ${gymId} - forceRefresh: ${forceRefresh}, fileExists: ${fileExists}, fileAge: ${fileAge}ms`);
|
logger.info(`Rebuilding cache for gym ${gymId} - forceRefresh: ${forceRefresh}, fileExists: ${fileExists}, fileAge: ${fileAge}ms`);
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -223,7 +178,7 @@ export const getCachedMembers = onCall(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error reading cache file for gym ${gymId}:`, error);
|
logger.error(`Error reading cache file for gym ${gymId}:`, error);
|
||||||
|
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
const [fileBuffer] = await file.download();
|
const [fileBuffer] = await file.download();
|
||||||
const jsonData: JsonCacheData = JSON.parse(fileBuffer.toString());
|
const jsonData: JsonCacheData = JSON.parse(fileBuffer.toString());
|
||||||
return jsonData.members || [];
|
return jsonData.members || [];
|
||||||
@ -235,7 +190,7 @@ export const getCachedMembers = onCall(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const rebuildGymCachee = onCall(
|
export const rebuildGymCache = onCall(
|
||||||
{
|
{
|
||||||
region: "#{SERVICES_RGN}#",
|
region: "#{SERVICES_RGN}#",
|
||||||
},
|
},
|
||||||
@ -246,7 +201,7 @@ export const rebuildGymCachee = onCall(
|
|||||||
throw new HttpsError('invalid-argument', 'gymId is required');
|
throw new HttpsError('invalid-argument', 'gymId is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
return { success: true, message: `JSON cache rebuilt for gym ${gymId}` };
|
return { success: true, message: `JSON cache rebuilt for gym ${gymId}` };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -267,7 +222,7 @@ export const batchRebuildCaches = onCall(
|
|||||||
|
|
||||||
for (const gymId of gymIds) {
|
for (const gymId of gymIds) {
|
||||||
try {
|
try {
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
results.push({ gymId, status: 'success' });
|
results.push({ gymId, status: 'success' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
@ -284,7 +239,7 @@ export const batchRebuildCaches = onCall(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
async function rebuildGymCache(gymId: string): Promise<void> {
|
async function rebuildGymCachee(gymId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
logger.info(`Starting JSON cache rebuild for gym: ${gymId}`);
|
logger.info(`Starting JSON cache rebuild for gym: ${gymId}`);
|
||||||
|
|
||||||
@ -302,7 +257,13 @@ async function rebuildGymCache(gymId: string): Promise<void> {
|
|||||||
const batch = membershipsSnapshot.docs.slice(i, i + batchSize);
|
const batch = membershipsSnapshot.docs.slice(i, i + batchSize);
|
||||||
const batchPromises = batch.map(async (doc) => {
|
const batchPromises = batch.map(async (doc) => {
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
logger.error(`Error processing member ${doc.id}:`, error);
|
logger.error(`Error processing member ${doc.id}:`, error);
|
||||||
return null;
|
return null;
|
||||||
@ -343,24 +304,30 @@ async function rebuildGymCache(gymId: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateCacheEntry(membershipId: string, membershipData: MembershipData): Promise<CacheEntry> {
|
async function generateCacheEntry(userId: string, membershipId: string, membershipData: MembershipData): Promise<CacheEntry> {
|
||||||
try {
|
try {
|
||||||
let fields: { [key: string]: string } = {};
|
let fields: { [key: string]: string } = {};
|
||||||
try {
|
try {
|
||||||
const clientFieldsSnapshot = await app
|
const clientFieldsSnapshot = await app
|
||||||
.firestore()
|
.firestore()
|
||||||
.collection('client_profiles')
|
.collection('client_profiles')
|
||||||
.where('membershipId', '==', membershipId)
|
.where('uid', '==', userId)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (!clientFieldsSnapshot.empty) {
|
if (!clientFieldsSnapshot.empty) {
|
||||||
const fieldDoc = clientFieldsSnapshot.docs[0];
|
const fieldDoc = clientFieldsSnapshot.docs[0];
|
||||||
const fieldData = fieldDoc.data() as ClientFields;
|
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) {
|
} 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 renewalDate: Date | null = null;
|
||||||
let daysUntilExpiry: number | null = null;
|
let daysUntilExpiry: number | null = null;
|
||||||
|
|
||||||
@ -388,7 +355,7 @@ async function generateCacheEntry(membershipId: string, membershipData: Membersh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} 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)
|
.where('membershipId', '==', membershipId)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
trainerAssignments = assignmentsSnapshot.docs.map(doc => {
|
assignmentsSnapshot.docs.forEach(doc => {
|
||||||
const data = doc.data() as TrainerAssignment;
|
const data = doc.data() as TrainerAssignment;
|
||||||
return {
|
if (data.timeSlot && Array.isArray(data.timeSlot)) {
|
||||||
id: doc.id,
|
timeSlots.push(...data.timeSlot);
|
||||||
...data,
|
}
|
||||||
createdAt: data.createdAt ? data.createdAt.toDate().toISOString() : null
|
trainerAssignments.push({ id: doc.id });
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error getting trainer data for ${membershipId}:`, error);
|
logger.error(`Error getting trainer data for membership ${membershipId}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheEntry: CacheEntry = {
|
const cacheEntry: CacheEntry = {
|
||||||
|
userId,
|
||||||
membershipId,
|
membershipId,
|
||||||
memberData: {
|
memberData: {
|
||||||
...membershipData,
|
status: membershipData.status,
|
||||||
daysUntilExpiry,
|
subscription: {
|
||||||
|
hasPersonalTraining: membershipData.subscription?.hasPersonalTraining,
|
||||||
|
frequency: membershipData.subscription?.frequency,
|
||||||
|
},
|
||||||
|
remainingAmount: membershipData.remainingAmount,
|
||||||
|
isPartialPayment: membershipData.isPartialPayment,
|
||||||
hasPartialPayment,
|
hasPartialPayment,
|
||||||
|
daysUntilExpiry,
|
||||||
createdAt: membershipData.createdAt ? membershipData.createdAt.toDate().toISOString() : null,
|
createdAt: membershipData.createdAt ? membershipData.createdAt.toDate().toISOString() : null,
|
||||||
updatedAt: membershipData.updatedAt ? membershipData.updatedAt.toDate().toISOString() : null
|
updatedAt: membershipData.updatedAt ? membershipData.updatedAt.toDate().toISOString() : null
|
||||||
},
|
},
|
||||||
@ -494,7 +450,7 @@ async function rebuildGymCacheFromDeletion(membershipId: string): Promise<void>
|
|||||||
if (memberExists) {
|
if (memberExists) {
|
||||||
const gymId = file.name.replace(`${CACHE_FOLDER}/`, '').replace('.json', '');
|
const gymId = file.name.replace(`${CACHE_FOLDER}/`, '').replace('.json', '');
|
||||||
logger.info(`Rebuilding cache for gym ${gymId} after member ${membershipId} deletion`);
|
logger.info(`Rebuilding cache for gym ${gymId} after member ${membershipId} deletion`);
|
||||||
await rebuildGymCache(gymId);
|
await rebuildGymCachee(gymId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user