@blackcode_sa/metaestetics-api 1.12.20 → 1.12.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -132,6 +132,7 @@ interface Review {
132
132
  id: string;
133
133
  appointmentId: string;
134
134
  patientId: string;
135
+ patientName?: string;
135
136
  createdAt: Date;
136
137
  updatedAt: Date;
137
138
  clinicReview?: ClinicReview;
@@ -2560,6 +2561,19 @@ declare class AppointmentAggregationService {
2560
2561
  * @returns {Promise<Clinic | null>} The clinic information or null if not found or an error occurs.
2561
2562
  */
2562
2563
  private fetchClinicInfo;
2564
+ /**
2565
+ * Checks if zone photos have changed between two appointment states
2566
+ * @param before - The appointment state before update
2567
+ * @param after - The appointment state after update
2568
+ * @returns True if zone photos have changed, false otherwise
2569
+ */
2570
+ private hasZonePhotosChanged;
2571
+ /**
2572
+ * Handles zone photos update notifications and logging
2573
+ * @param before - The appointment state before update
2574
+ * @param after - The appointment state after update
2575
+ */
2576
+ private handleZonePhotosUpdate;
2563
2577
  }
2564
2578
 
2565
2579
  /**
@@ -132,6 +132,7 @@ interface Review {
132
132
  id: string;
133
133
  appointmentId: string;
134
134
  patientId: string;
135
+ patientName?: string;
135
136
  createdAt: Date;
136
137
  updatedAt: Date;
137
138
  clinicReview?: ClinicReview;
@@ -2560,6 +2561,19 @@ declare class AppointmentAggregationService {
2560
2561
  * @returns {Promise<Clinic | null>} The clinic information or null if not found or an error occurs.
2561
2562
  */
2562
2563
  private fetchClinicInfo;
2564
+ /**
2565
+ * Checks if zone photos have changed between two appointment states
2566
+ * @param before - The appointment state before update
2567
+ * @param after - The appointment state after update
2568
+ * @returns True if zone photos have changed, false otherwise
2569
+ */
2570
+ private hasZonePhotosChanged;
2571
+ /**
2572
+ * Handles zone photos update notifications and logging
2573
+ * @param before - The appointment state before update
2574
+ * @param after - The appointment state after update
2575
+ */
2576
+ private handleZonePhotosUpdate;
2563
2577
  }
2564
2578
 
2565
2579
  /**
@@ -2079,9 +2079,7 @@ var AppointmentAggregationService = class {
2079
2079
  );
2080
2080
  this.notificationsAdmin = new NotificationsAdmin(this.db);
2081
2081
  this.calendarAdminService = new CalendarAdminService(this.db);
2082
- this.patientRequirementsAdminService = new PatientRequirementsAdminService(
2083
- this.db
2084
- );
2082
+ this.patientRequirementsAdminService = new PatientRequirementsAdminService(this.db);
2085
2083
  Logger.info("[AppointmentAggregationService] Initialized.");
2086
2084
  }
2087
2085
  /**
@@ -2096,12 +2094,7 @@ var AppointmentAggregationService = class {
2096
2094
  `[AggService] Handling CREATE for appointment: ${appointment.id}, patient: ${appointment.patientId}, status: ${appointment.status}`
2097
2095
  );
2098
2096
  try {
2099
- const [
2100
- patientProfile,
2101
- patientSensitiveInfo,
2102
- practitionerProfile,
2103
- clinicInfo
2104
- ] = await Promise.all([
2097
+ const [patientProfile, patientSensitiveInfo, practitionerProfile, clinicInfo] = await Promise.all([
2105
2098
  this.fetchPatientProfile(appointment.patientId),
2106
2099
  this.fetchPatientSensitiveInfo(appointment.patientId),
2107
2100
  this.fetchPractitionerProfile(appointment.practitionerId),
@@ -2118,9 +2111,7 @@ var AppointmentAggregationService = class {
2118
2111
  );
2119
2112
  }
2120
2113
  if (appointment.status === "confirmed" /* CONFIRMED */) {
2121
- Logger.info(
2122
- `[AggService] Appt ${appointment.id} created as CONFIRMED.`
2123
- );
2114
+ Logger.info(`[AggService] Appt ${appointment.id} created as CONFIRMED.`);
2124
2115
  await this.createPreAppointmentRequirementInstances(appointment);
2125
2116
  if ((patientSensitiveInfo == null ? void 0 : patientSensitiveInfo.email) && patientProfile) {
2126
2117
  Logger.info(
@@ -2188,9 +2179,7 @@ var AppointmentAggregationService = class {
2188
2179
  );
2189
2180
  }
2190
2181
  }
2191
- Logger.info(
2192
- `[AggService] Successfully processed CREATE for appointment: ${appointment.id}`
2193
- );
2182
+ Logger.info(`[AggService] Successfully processed CREATE for appointment: ${appointment.id}`);
2194
2183
  } catch (error) {
2195
2184
  Logger.error(
2196
2185
  `[AggService] Critical error in handleAppointmentCreate for appointment ${appointment.id}:`,
@@ -2213,12 +2202,8 @@ var AppointmentAggregationService = class {
2213
2202
  try {
2214
2203
  const statusChanged = before.status !== after.status;
2215
2204
  const timeChanged = before.appointmentStartTime.toMillis() !== after.appointmentStartTime.toMillis() || before.appointmentEndTime.toMillis() !== after.appointmentEndTime.toMillis();
2216
- const [
2217
- patientProfile,
2218
- patientSensitiveInfo,
2219
- practitionerProfile,
2220
- clinicInfo
2221
- ] = await Promise.all([
2205
+ const zonePhotosChanged = this.hasZonePhotosChanged(before, after);
2206
+ const [patientProfile, patientSensitiveInfo, practitionerProfile, clinicInfo] = await Promise.all([
2222
2207
  this.fetchPatientProfile(after.patientId),
2223
2208
  this.fetchPatientSensitiveInfo(after.patientId),
2224
2209
  this.fetchPractitionerProfile(after.practitionerId),
@@ -2244,9 +2229,7 @@ var AppointmentAggregationService = class {
2244
2229
  recipientProfile: after.patientInfo,
2245
2230
  recipientRole: "patient"
2246
2231
  };
2247
- await this.appointmentMailingService.sendAppointmentConfirmedEmail(
2248
- emailData
2249
- );
2232
+ await this.appointmentMailingService.sendAppointmentConfirmedEmail(emailData);
2250
2233
  } else {
2251
2234
  Logger.warn(
2252
2235
  `[AggService] Cannot send confirmation email to patient ${after.patientId}: email missing.`
@@ -2277,9 +2260,7 @@ var AppointmentAggregationService = class {
2277
2260
  );
2278
2261
  }
2279
2262
  } else if (before.status === "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */ && after.status === "confirmed" /* CONFIRMED */) {
2280
- Logger.info(
2281
- `[AggService] Appt ${after.id} RESCHEDULED_BY_CLINIC -> CONFIRMED.`
2282
- );
2263
+ Logger.info(`[AggService] Appt ${after.id} RESCHEDULED_BY_CLINIC -> CONFIRMED.`);
2283
2264
  await this.updateRelatedPatientRequirementInstances(
2284
2265
  before,
2285
2266
  "supersededReschedule" /* SUPERSEDED_RESCHEDULE */
@@ -2298,9 +2279,7 @@ var AppointmentAggregationService = class {
2298
2279
  recipientProfile: after.patientInfo,
2299
2280
  recipientRole: "patient"
2300
2281
  };
2301
- await this.appointmentMailingService.sendAppointmentConfirmedEmail(
2302
- emailData
2303
- );
2282
+ await this.appointmentMailingService.sendAppointmentConfirmedEmail(emailData);
2304
2283
  }
2305
2284
  if ((patientProfile == null ? void 0 : patientProfile.expoTokens) && patientProfile.expoTokens.length > 0) {
2306
2285
  await this.notificationsAdmin.sendAppointmentConfirmedPush(
@@ -2411,21 +2390,16 @@ var AppointmentAggregationService = class {
2411
2390
  );
2412
2391
  }
2413
2392
  } else if (after.status === "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
2414
- Logger.info(
2415
- `[AggService] Appt ${after.id} status -> RESCHEDULED_BY_CLINIC.`
2416
- );
2393
+ Logger.info(`[AggService] Appt ${after.id} status -> RESCHEDULED_BY_CLINIC.`);
2417
2394
  await this.updateRelatedPatientRequirementInstances(
2418
2395
  before,
2419
2396
  // Pass the 'before' state for old requirements
2420
2397
  "supersededReschedule" /* SUPERSEDED_RESCHEDULE */
2421
2398
  );
2422
- await this.calendarAdminService.updateAppointmentCalendarEventsTime(
2423
- after,
2424
- {
2425
- start: after.appointmentStartTime,
2426
- end: after.appointmentEndTime
2427
- }
2428
- );
2399
+ await this.calendarAdminService.updateAppointmentCalendarEventsTime(after, {
2400
+ start: after.appointmentStartTime,
2401
+ end: after.appointmentEndTime
2402
+ });
2429
2403
  await this.calendarAdminService.updateAppointmentCalendarEventsStatus(
2430
2404
  after,
2431
2405
  "pending" /* PENDING */
@@ -2459,22 +2433,21 @@ var AppointmentAggregationService = class {
2459
2433
  "supersededReschedule" /* SUPERSEDED_RESCHEDULE */
2460
2434
  );
2461
2435
  await this.createPreAppointmentRequirementInstances(after);
2462
- await this.calendarAdminService.updateAppointmentCalendarEventsTime(
2463
- after,
2464
- {
2465
- start: after.appointmentStartTime,
2466
- end: after.appointmentEndTime
2467
- }
2468
- );
2436
+ await this.calendarAdminService.updateAppointmentCalendarEventsTime(after, {
2437
+ start: after.appointmentStartTime,
2438
+ end: after.appointmentEndTime
2439
+ });
2469
2440
  } else {
2470
2441
  Logger.warn(
2471
2442
  `[AggService] Independent time change detected for ${after.id} with status ${after.status}. Review implications for requirements and calendar.`
2472
2443
  );
2473
2444
  }
2474
2445
  }
2475
- Logger.info(
2476
- `[AggService] Successfully processed UPDATE for appointment: ${after.id}`
2477
- );
2446
+ if (zonePhotosChanged) {
2447
+ Logger.info(`[AggService] Zone photos changed for appointment ${after.id}`);
2448
+ await this.handleZonePhotosUpdate(before, after);
2449
+ }
2450
+ Logger.info(`[AggService] Successfully processed UPDATE for appointment: ${after.id}`);
2478
2451
  } catch (error) {
2479
2452
  Logger.error(
2480
2453
  `[AggService] Critical error in handleAppointmentUpdate for appointment ${after.id}:`,
@@ -2488,16 +2461,12 @@ var AppointmentAggregationService = class {
2488
2461
  * @returns {Promise<void>}
2489
2462
  */
2490
2463
  async handleAppointmentDelete(deletedAppointment) {
2491
- Logger.info(
2492
- `[AggService] Handling DELETE for appointment: ${deletedAppointment.id}`
2493
- );
2464
+ Logger.info(`[AggService] Handling DELETE for appointment: ${deletedAppointment.id}`);
2494
2465
  await this.updateRelatedPatientRequirementInstances(
2495
2466
  deletedAppointment,
2496
2467
  "cancelledAppointment" /* CANCELLED_APPOINTMENT */
2497
2468
  );
2498
- const patientProfile = await this.fetchPatientProfile(
2499
- deletedAppointment.patientId
2500
- );
2469
+ const patientProfile = await this.fetchPatientProfile(deletedAppointment.patientId);
2501
2470
  if (patientProfile) {
2502
2471
  await this.managePatientClinicPractitionerLinks(
2503
2472
  patientProfile,
@@ -2506,9 +2475,7 @@ var AppointmentAggregationService = class {
2506
2475
  "cancel"
2507
2476
  );
2508
2477
  }
2509
- await this.calendarAdminService.deleteAppointmentCalendarEvents(
2510
- deletedAppointment
2511
- );
2478
+ await this.calendarAdminService.deleteAppointmentCalendarEvents(deletedAppointment);
2512
2479
  }
2513
2480
  // --- Helper Methods for Aggregation Logic ---
2514
2481
  /**
@@ -2578,15 +2545,11 @@ var AppointmentAggregationService = class {
2578
2545
  `[AggService] Processing template ${template.id} (${template.name}) for appt ${appointment.id}`
2579
2546
  );
2580
2547
  const newInstanceRef = this.db.collection(PATIENTS_COLLECTION).doc(appointment.patientId).collection(PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME).doc();
2581
- Logger.debug(
2582
- `[AggService] Created doc reference: ${newInstanceRef.path}`
2583
- );
2548
+ Logger.debug(`[AggService] Created doc reference: ${newInstanceRef.path}`);
2584
2549
  const instructions = (((_a = template.timeframe) == null ? void 0 : _a.notifyAt) || []).map((notifyAtValue) => {
2585
2550
  let dueTime = appointment.appointmentStartTime;
2586
2551
  if (template.timeframe && typeof notifyAtValue === "number") {
2587
- const dueDateTime = new Date(
2588
- appointment.appointmentStartTime.toMillis()
2589
- );
2552
+ const dueDateTime = new Date(appointment.appointmentStartTime.toMillis());
2590
2553
  if (template.timeframe.unit === "days" /* DAYS */) {
2591
2554
  dueDateTime.setDate(dueDateTime.getDate() - notifyAtValue);
2592
2555
  } else if (template.timeframe.unit === "hours" /* HOURS */) {
@@ -2660,26 +2623,20 @@ var AppointmentAggregationService = class {
2660
2623
  Logger.warn(
2661
2624
  `[AggService] Batch commit reported success but documents not found! Attempting direct creation as fallback...`
2662
2625
  );
2663
- const fallbackPromises = createdInstances.map(
2664
- async ({ ref, data }) => {
2665
- try {
2666
- await ref.set(data);
2667
- Logger.info(
2668
- `[AggService] Fallback direct creation success for ${ref.id}`
2669
- );
2670
- return true;
2671
- } catch (fallbackError) {
2672
- Logger.error(
2673
- `[AggService] Fallback direct creation failed for ${ref.id}:`,
2674
- fallbackError
2675
- );
2676
- return false;
2677
- }
2626
+ const fallbackPromises = createdInstances.map(async ({ ref, data }) => {
2627
+ try {
2628
+ await ref.set(data);
2629
+ Logger.info(`[AggService] Fallback direct creation success for ${ref.id}`);
2630
+ return true;
2631
+ } catch (fallbackError) {
2632
+ Logger.error(
2633
+ `[AggService] Fallback direct creation failed for ${ref.id}:`,
2634
+ fallbackError
2635
+ );
2636
+ return false;
2678
2637
  }
2679
- );
2680
- const fallbackResults = await Promise.allSettled(
2681
- fallbackPromises
2682
- );
2638
+ });
2639
+ const fallbackResults = await Promise.allSettled(fallbackPromises);
2683
2640
  const successCount = fallbackResults.filter(
2684
2641
  (r) => r.status === "fulfilled" && r.value === true
2685
2642
  ).length;
@@ -2712,23 +2669,19 @@ var AppointmentAggregationService = class {
2712
2669
  commitError
2713
2670
  );
2714
2671
  Logger.info(`[AggService] Attempting direct creation as fallback...`);
2715
- const fallbackPromises = createdInstances.map(
2716
- async ({ ref, data }) => {
2717
- try {
2718
- await ref.set(data);
2719
- Logger.info(
2720
- `[AggService] Fallback direct creation success for ${ref.id}`
2721
- );
2722
- return true;
2723
- } catch (fallbackError) {
2724
- Logger.error(
2725
- `[AggService] Fallback direct creation failed for ${ref.id}:`,
2726
- fallbackError
2727
- );
2728
- return false;
2729
- }
2672
+ const fallbackPromises = createdInstances.map(async ({ ref, data }) => {
2673
+ try {
2674
+ await ref.set(data);
2675
+ Logger.info(`[AggService] Fallback direct creation success for ${ref.id}`);
2676
+ return true;
2677
+ } catch (fallbackError) {
2678
+ Logger.error(
2679
+ `[AggService] Fallback direct creation failed for ${ref.id}:`,
2680
+ fallbackError
2681
+ );
2682
+ return false;
2730
2683
  }
2731
- );
2684
+ });
2732
2685
  const fallbackResults = await Promise.allSettled(fallbackPromises);
2733
2686
  const successCount = fallbackResults.filter(
2734
2687
  (r) => r.status === "fulfilled" && r.value === true
@@ -2826,15 +2779,11 @@ var AppointmentAggregationService = class {
2826
2779
  `[AggService] Processing template ${template.id} (${template.name}) for appt ${appointment.id}`
2827
2780
  );
2828
2781
  const newInstanceRef = this.db.collection(PATIENTS_COLLECTION).doc(appointment.patientId).collection(PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME).doc();
2829
- Logger.debug(
2830
- `[AggService] Created doc reference: ${newInstanceRef.path}`
2831
- );
2782
+ Logger.debug(`[AggService] Created doc reference: ${newInstanceRef.path}`);
2832
2783
  const instructions = (((_a = template.timeframe) == null ? void 0 : _a.notifyAt) || []).map((notifyAtValue) => {
2833
2784
  let dueTime = appointment.appointmentEndTime;
2834
2785
  if (template.timeframe && typeof notifyAtValue === "number") {
2835
- const dueDateTime = new Date(
2836
- appointment.appointmentEndTime.toMillis()
2837
- );
2786
+ const dueDateTime = new Date(appointment.appointmentEndTime.toMillis());
2838
2787
  if (template.timeframe.unit === "days" /* DAYS */) {
2839
2788
  dueDateTime.setDate(dueDateTime.getDate() + notifyAtValue);
2840
2789
  } else if (template.timeframe.unit === "hours" /* HOURS */) {
@@ -2905,26 +2854,20 @@ var AppointmentAggregationService = class {
2905
2854
  Logger.warn(
2906
2855
  `[AggService] Batch commit reported success but documents not found! Attempting direct creation as fallback...`
2907
2856
  );
2908
- const fallbackPromises = createdInstances.map(
2909
- async ({ ref, data }) => {
2910
- try {
2911
- await ref.set(data);
2912
- Logger.info(
2913
- `[AggService] Fallback direct creation success for ${ref.id}`
2914
- );
2915
- return true;
2916
- } catch (fallbackError) {
2917
- Logger.error(
2918
- `[AggService] Fallback direct creation failed for ${ref.id}:`,
2919
- fallbackError
2920
- );
2921
- return false;
2922
- }
2857
+ const fallbackPromises = createdInstances.map(async ({ ref, data }) => {
2858
+ try {
2859
+ await ref.set(data);
2860
+ Logger.info(`[AggService] Fallback direct creation success for ${ref.id}`);
2861
+ return true;
2862
+ } catch (fallbackError) {
2863
+ Logger.error(
2864
+ `[AggService] Fallback direct creation failed for ${ref.id}:`,
2865
+ fallbackError
2866
+ );
2867
+ return false;
2923
2868
  }
2924
- );
2925
- const fallbackResults = await Promise.allSettled(
2926
- fallbackPromises
2927
- );
2869
+ });
2870
+ const fallbackResults = await Promise.allSettled(fallbackPromises);
2928
2871
  const successCount = fallbackResults.filter(
2929
2872
  (r) => r.status === "fulfilled" && r.value === true
2930
2873
  ).length;
@@ -2957,23 +2900,19 @@ var AppointmentAggregationService = class {
2957
2900
  commitError
2958
2901
  );
2959
2902
  Logger.info(`[AggService] Attempting direct creation as fallback...`);
2960
- const fallbackPromises = createdInstances.map(
2961
- async ({ ref, data }) => {
2962
- try {
2963
- await ref.set(data);
2964
- Logger.info(
2965
- `[AggService] Fallback direct creation success for ${ref.id}`
2966
- );
2967
- return true;
2968
- } catch (fallbackError) {
2969
- Logger.error(
2970
- `[AggService] Fallback direct creation failed for ${ref.id}:`,
2971
- fallbackError
2972
- );
2973
- return false;
2974
- }
2903
+ const fallbackPromises = createdInstances.map(async ({ ref, data }) => {
2904
+ try {
2905
+ await ref.set(data);
2906
+ Logger.info(`[AggService] Fallback direct creation success for ${ref.id}`);
2907
+ return true;
2908
+ } catch (fallbackError) {
2909
+ Logger.error(
2910
+ `[AggService] Fallback direct creation failed for ${ref.id}:`,
2911
+ fallbackError
2912
+ );
2913
+ return false;
2975
2914
  }
2976
- );
2915
+ });
2977
2916
  const fallbackResults = await Promise.allSettled(fallbackPromises);
2978
2917
  const successCount = fallbackResults.filter(
2979
2918
  (r) => r.status === "fulfilled" && r.value === true
@@ -3123,9 +3062,7 @@ var AppointmentAggregationService = class {
3123
3062
  updateData.doctorIds = admin6.firestore.FieldValue.arrayUnion(practitionerId);
3124
3063
  }
3125
3064
  if (!hasClinic) {
3126
- Logger.debug(
3127
- `[AggService] Adding clinic ${clinicId} to patient ${patientProfile.id}`
3128
- );
3065
+ Logger.debug(`[AggService] Adding clinic ${clinicId} to patient ${patientProfile.id}`);
3129
3066
  updateData.clinicIds = admin6.firestore.FieldValue.arrayUnion(clinicId);
3130
3067
  }
3131
3068
  await patientRef.update(updateData);
@@ -3180,28 +3117,19 @@ var AppointmentAggregationService = class {
3180
3117
  updateNeeded = true;
3181
3118
  }
3182
3119
  if (activeClinicAppointments === 0 && ((_b = patientProfile.clinicIds) == null ? void 0 : _b.includes(clinicId))) {
3183
- Logger.debug(
3184
- `[AggService] Removing clinic ${clinicId} from patient ${patientProfile.id}`
3185
- );
3120
+ Logger.debug(`[AggService] Removing clinic ${clinicId} from patient ${patientProfile.id}`);
3186
3121
  updateData.clinicIds = admin6.firestore.FieldValue.arrayRemove(clinicId);
3187
3122
  updateNeeded = true;
3188
3123
  }
3189
3124
  if (updateNeeded) {
3190
3125
  updateData.updatedAt = admin6.firestore.FieldValue.serverTimestamp();
3191
3126
  await patientRef.update(updateData);
3192
- Logger.info(
3193
- `[AggService] Successfully removed links from patient ${patientProfile.id}`
3194
- );
3127
+ Logger.info(`[AggService] Successfully removed links from patient ${patientProfile.id}`);
3195
3128
  } else {
3196
- Logger.info(
3197
- `[AggService] No links need to be removed from patient ${patientProfile.id}`
3198
- );
3129
+ Logger.info(`[AggService] No links need to be removed from patient ${patientProfile.id}`);
3199
3130
  }
3200
3131
  } catch (error) {
3201
- Logger.error(
3202
- `[AggService] Error removing links from patient profile:`,
3203
- error
3204
- );
3132
+ Logger.error(`[AggService] Error removing links from patient profile:`, error);
3205
3133
  throw error;
3206
3134
  }
3207
3135
  }
@@ -3234,10 +3162,7 @@ var AppointmentAggregationService = class {
3234
3162
  const doc = await this.db.collection(PATIENTS_COLLECTION).doc(patientId).get();
3235
3163
  return doc.exists ? doc.data() : null;
3236
3164
  } catch (error) {
3237
- Logger.error(
3238
- `[AggService] Error fetching patient profile ${patientId}:`,
3239
- error
3240
- );
3165
+ Logger.error(`[AggService] Error fetching patient profile ${patientId}:`, error);
3241
3166
  return null;
3242
3167
  }
3243
3168
  }
@@ -3250,17 +3175,12 @@ var AppointmentAggregationService = class {
3250
3175
  try {
3251
3176
  const doc = await this.db.collection(PATIENTS_COLLECTION).doc(patientId).collection(PATIENT_SENSITIVE_INFO_COLLECTION).doc(patientId).get();
3252
3177
  if (!doc.exists) {
3253
- Logger.warn(
3254
- `[AggService] No sensitive info found for patient ${patientId}`
3255
- );
3178
+ Logger.warn(`[AggService] No sensitive info found for patient ${patientId}`);
3256
3179
  return null;
3257
3180
  }
3258
3181
  return doc.data();
3259
3182
  } catch (error) {
3260
- Logger.error(
3261
- `[AggService] Error fetching patient sensitive info ${patientId}:`,
3262
- error
3263
- );
3183
+ Logger.error(`[AggService] Error fetching patient sensitive info ${patientId}:`, error);
3264
3184
  return null;
3265
3185
  }
3266
3186
  }
@@ -3271,25 +3191,18 @@ var AppointmentAggregationService = class {
3271
3191
  */
3272
3192
  async fetchPractitionerProfile(practitionerId) {
3273
3193
  if (!practitionerId) {
3274
- Logger.warn(
3275
- "[AggService] fetchPractitionerProfile called with no practitionerId."
3276
- );
3194
+ Logger.warn("[AggService] fetchPractitionerProfile called with no practitionerId.");
3277
3195
  return null;
3278
3196
  }
3279
3197
  try {
3280
3198
  const doc = await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).get();
3281
3199
  if (!doc.exists) {
3282
- Logger.warn(
3283
- `[AggService] No practitioner profile found for ID ${practitionerId}`
3284
- );
3200
+ Logger.warn(`[AggService] No practitioner profile found for ID ${practitionerId}`);
3285
3201
  return null;
3286
3202
  }
3287
3203
  return doc.data();
3288
3204
  } catch (error) {
3289
- Logger.error(
3290
- `[AggService] Error fetching practitioner profile ${practitionerId}:`,
3291
- error
3292
- );
3205
+ Logger.error(`[AggService] Error fetching practitioner profile ${practitionerId}:`, error);
3293
3206
  return null;
3294
3207
  }
3295
3208
  }
@@ -3310,12 +3223,113 @@ var AppointmentAggregationService = class {
3310
3223
  return null;
3311
3224
  }
3312
3225
  return doc.data();
3226
+ } catch (error) {
3227
+ Logger.error(`[AggService] Error fetching clinic info ${clinicId}:`, error);
3228
+ return null;
3229
+ }
3230
+ }
3231
+ /**
3232
+ * Checks if zone photos have changed between two appointment states
3233
+ * @param before - The appointment state before update
3234
+ * @param after - The appointment state after update
3235
+ * @returns True if zone photos have changed, false otherwise
3236
+ */
3237
+ hasZonePhotosChanged(before, after) {
3238
+ var _a, _b;
3239
+ const beforePhotos = (_a = before.metadata) == null ? void 0 : _a.zonePhotos;
3240
+ const afterPhotos = (_b = after.metadata) == null ? void 0 : _b.zonePhotos;
3241
+ if (!beforePhotos && !afterPhotos) {
3242
+ return false;
3243
+ }
3244
+ if (!beforePhotos || !afterPhotos) {
3245
+ return true;
3246
+ }
3247
+ const beforeZones = Object.keys(beforePhotos);
3248
+ const afterZones = Object.keys(afterPhotos);
3249
+ if (beforeZones.length !== afterZones.length) {
3250
+ return true;
3251
+ }
3252
+ for (const zoneId of afterZones) {
3253
+ const beforeZonePhotos = beforePhotos[zoneId];
3254
+ const afterZonePhotos = afterPhotos[zoneId];
3255
+ if (!beforeZonePhotos && !afterZonePhotos) {
3256
+ continue;
3257
+ }
3258
+ if (!beforeZonePhotos || !afterZonePhotos) {
3259
+ return true;
3260
+ }
3261
+ if (beforeZonePhotos.before !== afterZonePhotos.before || beforeZonePhotos.after !== afterZonePhotos.after || beforeZonePhotos.beforeNote !== afterZonePhotos.beforeNote || beforeZonePhotos.afterNote !== afterZonePhotos.afterNote) {
3262
+ return true;
3263
+ }
3264
+ }
3265
+ return false;
3266
+ }
3267
+ /**
3268
+ * Handles zone photos update notifications and logging
3269
+ * @param before - The appointment state before update
3270
+ * @param after - The appointment state after update
3271
+ */
3272
+ async handleZonePhotosUpdate(before, after) {
3273
+ var _a, _b, _c;
3274
+ try {
3275
+ Logger.info(`[AggService] Processing zone photos update for appointment ${after.id}`);
3276
+ const beforePhotos = ((_a = before.metadata) == null ? void 0 : _a.zonePhotos) || {};
3277
+ const afterPhotos = ((_b = after.metadata) == null ? void 0 : _b.zonePhotos) || {};
3278
+ const updatedZones = [];
3279
+ const newPhotoTypes = [];
3280
+ for (const zoneId of Object.keys(afterPhotos)) {
3281
+ const beforeZonePhotos = beforePhotos[zoneId];
3282
+ const afterZonePhotos = afterPhotos[zoneId];
3283
+ if (!beforeZonePhotos) {
3284
+ updatedZones.push(zoneId);
3285
+ if (afterZonePhotos.before) {
3286
+ newPhotoTypes.push({ zoneId, photoType: "before" });
3287
+ }
3288
+ if (afterZonePhotos.after) {
3289
+ newPhotoTypes.push({ zoneId, photoType: "after" });
3290
+ }
3291
+ } else {
3292
+ if (beforeZonePhotos.before !== afterZonePhotos.before && afterZonePhotos.before) {
3293
+ updatedZones.push(zoneId);
3294
+ newPhotoTypes.push({ zoneId, photoType: "before" });
3295
+ }
3296
+ if (beforeZonePhotos.after !== afterZonePhotos.after && afterZonePhotos.after) {
3297
+ updatedZones.push(zoneId);
3298
+ newPhotoTypes.push({ zoneId, photoType: "after" });
3299
+ }
3300
+ }
3301
+ }
3302
+ if (updatedZones.length > 0) {
3303
+ Logger.info(
3304
+ `[AggService] Zone photos updated for appointment ${after.id}: ${updatedZones.join(
3305
+ ", "
3306
+ )}`
3307
+ );
3308
+ for (const { zoneId, photoType } of newPhotoTypes) {
3309
+ Logger.info(
3310
+ `[AggService] New ${photoType} photo added for zone ${zoneId} in appointment ${after.id}`
3311
+ );
3312
+ }
3313
+ }
3314
+ const selectedZones = ((_c = after.metadata) == null ? void 0 : _c.selectedZones) || [];
3315
+ if (selectedZones.length > 0) {
3316
+ const completedZones = selectedZones.filter((zoneId) => {
3317
+ const zonePhotos = afterPhotos[zoneId];
3318
+ return zonePhotos && (zonePhotos.before || zonePhotos.after);
3319
+ });
3320
+ const completionPercentage = completedZones.length / selectedZones.length * 100;
3321
+ Logger.info(
3322
+ `[AggService] Photo completion for appointment ${after.id}: ${completionPercentage.toFixed(1)}% (${completedZones.length}/${selectedZones.length} zones)`
3323
+ );
3324
+ if (completionPercentage === 100) {
3325
+ Logger.info(`[AggService] All zone photos completed for appointment ${after.id}`);
3326
+ }
3327
+ }
3313
3328
  } catch (error) {
3314
3329
  Logger.error(
3315
- `[AggService] Error fetching clinic info ${clinicId}:`,
3330
+ `[AggService] Error handling zone photos update for appointment ${after.id}:`,
3316
3331
  error
3317
3332
  );
3318
- return null;
3319
3333
  }
3320
3334
  }
3321
3335
  };