@blackcode_sa/metaestetics-api 1.7.12 → 1.7.13
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.
- package/dist/admin/index.d.mts +200 -1
- package/dist/admin/index.d.ts +200 -1
- package/dist/admin/index.js +691 -35
- package/dist/admin/index.mjs +689 -35
- package/package.json +1 -1
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +316 -0
- package/src/admin/index.ts +8 -1
package/dist/admin/index.js
CHANGED
|
@@ -40,6 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
CalendarAdminService: () => CalendarAdminService,
|
|
41
41
|
ClinicAggregationService: () => ClinicAggregationService,
|
|
42
42
|
DocumentManagerAdminService: () => DocumentManagerAdminService,
|
|
43
|
+
FilledFormsAggregationService: () => FilledFormsAggregationService,
|
|
43
44
|
Logger: () => Logger,
|
|
44
45
|
MediaType: () => MediaType,
|
|
45
46
|
NOTIFICATIONS_COLLECTION: () => NOTIFICATIONS_COLLECTION,
|
|
@@ -56,6 +57,7 @@ __export(index_exports, {
|
|
|
56
57
|
PractitionerInviteMailingService: () => PractitionerInviteMailingService,
|
|
57
58
|
PractitionerTokenStatus: () => PractitionerTokenStatus,
|
|
58
59
|
ProcedureAggregationService: () => ProcedureAggregationService,
|
|
60
|
+
ReviewsAggregationService: () => ReviewsAggregationService,
|
|
59
61
|
UserRole: () => UserRole
|
|
60
62
|
});
|
|
61
63
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -203,9 +205,9 @@ var Logger = class {
|
|
|
203
205
|
|
|
204
206
|
// src/admin/notifications/notifications.admin.ts
|
|
205
207
|
var NotificationsAdmin = class {
|
|
206
|
-
constructor(
|
|
208
|
+
constructor(firestore15) {
|
|
207
209
|
this.expo = new import_expo_server_sdk.Expo();
|
|
208
|
-
this.db =
|
|
210
|
+
this.db = firestore15 || admin.firestore();
|
|
209
211
|
}
|
|
210
212
|
/**
|
|
211
213
|
* Dohvata notifikaciju po ID-u
|
|
@@ -924,8 +926,8 @@ var ClinicAggregationService = class {
|
|
|
924
926
|
* Constructor for ClinicAggregationService.
|
|
925
927
|
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
926
928
|
*/
|
|
927
|
-
constructor(
|
|
928
|
-
this.db =
|
|
929
|
+
constructor(firestore15) {
|
|
930
|
+
this.db = firestore15 || admin3.firestore();
|
|
929
931
|
}
|
|
930
932
|
/**
|
|
931
933
|
* Adds clinic information to a clinic group when a new clinic is created
|
|
@@ -1399,8 +1401,8 @@ var ClinicAggregationService = class {
|
|
|
1399
1401
|
var admin4 = __toESM(require("firebase-admin"));
|
|
1400
1402
|
var CALENDAR_SUBCOLLECTION_ID2 = "calendar";
|
|
1401
1403
|
var PractitionerAggregationService = class {
|
|
1402
|
-
constructor(
|
|
1403
|
-
this.db =
|
|
1404
|
+
constructor(firestore15) {
|
|
1405
|
+
this.db = firestore15 || admin4.firestore();
|
|
1404
1406
|
}
|
|
1405
1407
|
/**
|
|
1406
1408
|
* Adds practitioner information to a clinic when a new practitioner is created
|
|
@@ -1735,8 +1737,8 @@ var PractitionerAggregationService = class {
|
|
|
1735
1737
|
var admin5 = __toESM(require("firebase-admin"));
|
|
1736
1738
|
var CALENDAR_SUBCOLLECTION_ID3 = "calendar";
|
|
1737
1739
|
var ProcedureAggregationService = class {
|
|
1738
|
-
constructor(
|
|
1739
|
-
this.db =
|
|
1740
|
+
constructor(firestore15) {
|
|
1741
|
+
this.db = firestore15 || admin5.firestore();
|
|
1740
1742
|
}
|
|
1741
1743
|
/**
|
|
1742
1744
|
* Adds procedure information to a practitioner when a new procedure is created
|
|
@@ -2120,8 +2122,8 @@ var ProcedureAggregationService = class {
|
|
|
2120
2122
|
var admin6 = __toESM(require("firebase-admin"));
|
|
2121
2123
|
var CALENDAR_SUBCOLLECTION_ID4 = "calendar";
|
|
2122
2124
|
var PatientAggregationService = class {
|
|
2123
|
-
constructor(
|
|
2124
|
-
this.db =
|
|
2125
|
+
constructor(firestore15) {
|
|
2126
|
+
this.db = firestore15 || admin6.firestore();
|
|
2125
2127
|
}
|
|
2126
2128
|
// --- Methods for Patient Creation --- >
|
|
2127
2129
|
// No specific aggregations defined for patient creation in the plan.
|
|
@@ -2253,8 +2255,8 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
|
|
|
2253
2255
|
// src/admin/requirements/patient-requirements.admin.service.ts
|
|
2254
2256
|
var admin7 = __toESM(require("firebase-admin"));
|
|
2255
2257
|
var PatientRequirementsAdminService = class {
|
|
2256
|
-
constructor(
|
|
2257
|
-
this.db =
|
|
2258
|
+
constructor(firestore15) {
|
|
2259
|
+
this.db = firestore15 || admin7.firestore();
|
|
2258
2260
|
this.notificationsAdmin = new NotificationsAdmin(this.db);
|
|
2259
2261
|
}
|
|
2260
2262
|
/**
|
|
@@ -2582,8 +2584,8 @@ var PatientRequirementsAdminService = class {
|
|
|
2582
2584
|
// src/admin/calendar/calendar.admin.service.ts
|
|
2583
2585
|
var admin8 = __toESM(require("firebase-admin"));
|
|
2584
2586
|
var CalendarAdminService = class {
|
|
2585
|
-
constructor(
|
|
2586
|
-
this.db =
|
|
2587
|
+
constructor(firestore15) {
|
|
2588
|
+
this.db = firestore15 || admin8.firestore();
|
|
2587
2589
|
Logger.info("[CalendarAdminService] Initialized.");
|
|
2588
2590
|
}
|
|
2589
2591
|
/**
|
|
@@ -2866,9 +2868,9 @@ var BaseMailingService = class {
|
|
|
2866
2868
|
* @param firestore Firestore instance provided by the caller
|
|
2867
2869
|
* @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
|
|
2868
2870
|
*/
|
|
2869
|
-
constructor(
|
|
2871
|
+
constructor(firestore15, mailgunClient) {
|
|
2870
2872
|
var _a;
|
|
2871
|
-
this.db =
|
|
2873
|
+
this.db = firestore15;
|
|
2872
2874
|
this.mailgunClient = mailgunClient;
|
|
2873
2875
|
if (!this.db) {
|
|
2874
2876
|
Logger.error("[BaseMailingService] No Firestore instance provided");
|
|
@@ -3012,8 +3014,8 @@ var BaseMailingService = class {
|
|
|
3012
3014
|
var patientAppointmentConfirmedTemplate = "<h1>Appointment Confirmed</h1><p>Dear {{patientName}},</p><p>Your appointment for {{procedureName}} on {{appointmentDate}} at {{appointmentTime}} with {{practitionerName}} at {{clinicName}} has been confirmed.</p><p>Thank you!</p>";
|
|
3013
3015
|
var clinicAppointmentRequestedTemplate = "<h1>New Appointment Request</h1><p>Hello {{clinicName}} Admin,</p><p>A new appointment for {{procedureName}} has been requested by {{patientName}} for {{appointmentDate}} at {{appointmentTime}} with {{practitionerName}}.</p><p>Please review and confirm in the admin panel.</p>";
|
|
3014
3016
|
var AppointmentMailingService = class extends BaseMailingService {
|
|
3015
|
-
constructor(
|
|
3016
|
-
super(
|
|
3017
|
+
constructor(firestore15, mailgunClient) {
|
|
3018
|
+
super(firestore15, mailgunClient);
|
|
3017
3019
|
this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
|
|
3018
3020
|
Logger.info("[AppointmentMailingService] Initialized.");
|
|
3019
3021
|
}
|
|
@@ -3162,8 +3164,8 @@ var AppointmentAggregationService = class {
|
|
|
3162
3164
|
* @param mailgunClient - An initialized Mailgun client instance.
|
|
3163
3165
|
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
3164
3166
|
*/
|
|
3165
|
-
constructor(mailgunClient,
|
|
3166
|
-
this.db =
|
|
3167
|
+
constructor(mailgunClient, firestore15) {
|
|
3168
|
+
this.db = firestore15 || admin10.firestore();
|
|
3167
3169
|
this.appointmentMailingService = new AppointmentMailingService(
|
|
3168
3170
|
this.db,
|
|
3169
3171
|
mailgunClient
|
|
@@ -4412,6 +4414,658 @@ var AppointmentAggregationService = class {
|
|
|
4412
4414
|
}
|
|
4413
4415
|
};
|
|
4414
4416
|
|
|
4417
|
+
// src/admin/aggregation/forms/filled-forms.aggregation.service.ts
|
|
4418
|
+
var admin11 = __toESM(require("firebase-admin"));
|
|
4419
|
+
var FilledFormsAggregationService = class {
|
|
4420
|
+
/**
|
|
4421
|
+
* Constructor for FilledFormsAggregationService.
|
|
4422
|
+
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
4423
|
+
*/
|
|
4424
|
+
constructor(firestore15) {
|
|
4425
|
+
this.db = firestore15 || admin11.firestore();
|
|
4426
|
+
Logger.info("[FilledFormsAggregationService] Initialized");
|
|
4427
|
+
}
|
|
4428
|
+
/**
|
|
4429
|
+
* Handles side effects when a filled form is created or updated.
|
|
4430
|
+
* This function would typically be called by a Firestore onCreate or onUpdate trigger.
|
|
4431
|
+
* @param filledDocument The filled document that was created or updated.
|
|
4432
|
+
* @returns {Promise<void>}
|
|
4433
|
+
*/
|
|
4434
|
+
async handleFilledFormCreateOrUpdate(filledDocument) {
|
|
4435
|
+
Logger.info(
|
|
4436
|
+
`[FilledFormsAggregationService] Handling CREATE/UPDATE for filled form: ${filledDocument.id}, appointment: ${filledDocument.appointmentId}`
|
|
4437
|
+
);
|
|
4438
|
+
try {
|
|
4439
|
+
const appointmentRef = this.db.collection(APPOINTMENTS_COLLECTION).doc(filledDocument.appointmentId);
|
|
4440
|
+
const appointmentDoc = await appointmentRef.get();
|
|
4441
|
+
if (!appointmentDoc.exists) {
|
|
4442
|
+
Logger.error(
|
|
4443
|
+
`[FilledFormsAggregationService] Appointment ${filledDocument.appointmentId} not found.`
|
|
4444
|
+
);
|
|
4445
|
+
return;
|
|
4446
|
+
}
|
|
4447
|
+
const appointment = appointmentDoc.data();
|
|
4448
|
+
const formSubcollection = filledDocument.isUserForm ? USER_FORMS_SUBCOLLECTION : DOCTOR_FORMS_SUBCOLLECTION;
|
|
4449
|
+
const linkedFormInfo = {
|
|
4450
|
+
formId: filledDocument.id,
|
|
4451
|
+
templateId: filledDocument.templateId,
|
|
4452
|
+
templateVersion: filledDocument.templateVersion,
|
|
4453
|
+
title: filledDocument.isUserForm ? "User Form" : "Doctor Form",
|
|
4454
|
+
// Default title if not available
|
|
4455
|
+
isUserForm: filledDocument.isUserForm,
|
|
4456
|
+
isRequired: filledDocument.isRequired,
|
|
4457
|
+
status: filledDocument.status,
|
|
4458
|
+
path: `${APPOINTMENTS_COLLECTION}/${filledDocument.appointmentId}/${formSubcollection}/${filledDocument.id}`
|
|
4459
|
+
};
|
|
4460
|
+
if (filledDocument.updatedAt) {
|
|
4461
|
+
linkedFormInfo.submittedAt = admin11.firestore.Timestamp.fromMillis(
|
|
4462
|
+
filledDocument.updatedAt
|
|
4463
|
+
);
|
|
4464
|
+
}
|
|
4465
|
+
if (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */) {
|
|
4466
|
+
linkedFormInfo.completedAt = admin11.firestore.Timestamp.fromMillis(
|
|
4467
|
+
filledDocument.updatedAt
|
|
4468
|
+
);
|
|
4469
|
+
}
|
|
4470
|
+
let updateData = {};
|
|
4471
|
+
let existingFormIndex = -1;
|
|
4472
|
+
if (appointment.linkedForms && appointment.linkedForms.length > 0) {
|
|
4473
|
+
existingFormIndex = appointment.linkedForms.findIndex(
|
|
4474
|
+
(form) => form.formId === filledDocument.id
|
|
4475
|
+
);
|
|
4476
|
+
}
|
|
4477
|
+
if (existingFormIndex >= 0 && appointment.linkedForms) {
|
|
4478
|
+
updateData = {
|
|
4479
|
+
linkedForms: admin11.firestore.FieldValue.arrayRemove(
|
|
4480
|
+
appointment.linkedForms[existingFormIndex]
|
|
4481
|
+
),
|
|
4482
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4483
|
+
};
|
|
4484
|
+
await appointmentRef.update(updateData);
|
|
4485
|
+
updateData = {
|
|
4486
|
+
linkedForms: admin11.firestore.FieldValue.arrayUnion(linkedFormInfo),
|
|
4487
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4488
|
+
};
|
|
4489
|
+
} else {
|
|
4490
|
+
updateData = {
|
|
4491
|
+
linkedForms: appointment.linkedForms ? admin11.firestore.FieldValue.arrayUnion(linkedFormInfo) : [linkedFormInfo],
|
|
4492
|
+
// If linkedForms doesn't exist, create a new array
|
|
4493
|
+
linkedFormIds: appointment.linkedFormIds ? admin11.firestore.FieldValue.arrayUnion(filledDocument.id) : [filledDocument.id],
|
|
4494
|
+
// If linkedFormIds doesn't exist, create a new array
|
|
4495
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4496
|
+
};
|
|
4497
|
+
}
|
|
4498
|
+
if (filledDocument.isUserForm && filledDocument.isRequired && (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */)) {
|
|
4499
|
+
if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.includes(filledDocument.id)) {
|
|
4500
|
+
updateData.pendingUserFormsIds = admin11.firestore.FieldValue.arrayRemove(filledDocument.id);
|
|
4501
|
+
Logger.info(
|
|
4502
|
+
`[FilledFormsAggregationService] Removing form ${filledDocument.id} from pendingUserFormsIds`
|
|
4503
|
+
);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
await appointmentRef.update(updateData);
|
|
4507
|
+
Logger.info(
|
|
4508
|
+
`[FilledFormsAggregationService] Successfully updated appointment ${filledDocument.appointmentId} with form info for ${filledDocument.id}`
|
|
4509
|
+
);
|
|
4510
|
+
} catch (error) {
|
|
4511
|
+
Logger.error(
|
|
4512
|
+
`[FilledFormsAggregationService] Error updating appointment for filled form ${filledDocument.id}:`,
|
|
4513
|
+
error
|
|
4514
|
+
);
|
|
4515
|
+
throw error;
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
/**
|
|
4519
|
+
* Handles side effects when a filled form is deleted.
|
|
4520
|
+
* This function would typically be called by a Firestore onDelete trigger.
|
|
4521
|
+
* @param filledDocument The filled document that was deleted.
|
|
4522
|
+
* @returns {Promise<void>}
|
|
4523
|
+
*/
|
|
4524
|
+
async handleFilledFormDelete(filledDocument) {
|
|
4525
|
+
Logger.info(
|
|
4526
|
+
`[FilledFormsAggregationService] Handling DELETE for filled form: ${filledDocument.id}, appointment: ${filledDocument.appointmentId}`
|
|
4527
|
+
);
|
|
4528
|
+
try {
|
|
4529
|
+
const appointmentRef = this.db.collection(APPOINTMENTS_COLLECTION).doc(filledDocument.appointmentId);
|
|
4530
|
+
const appointmentDoc = await appointmentRef.get();
|
|
4531
|
+
if (!appointmentDoc.exists) {
|
|
4532
|
+
Logger.error(
|
|
4533
|
+
`[FilledFormsAggregationService] Appointment ${filledDocument.appointmentId} not found.`
|
|
4534
|
+
);
|
|
4535
|
+
return;
|
|
4536
|
+
}
|
|
4537
|
+
const appointment = appointmentDoc.data();
|
|
4538
|
+
let updateData = {};
|
|
4539
|
+
if (appointment.linkedForms && appointment.linkedForms.length > 0) {
|
|
4540
|
+
const formToRemove = appointment.linkedForms.find(
|
|
4541
|
+
(form) => form.formId === filledDocument.id
|
|
4542
|
+
);
|
|
4543
|
+
if (formToRemove) {
|
|
4544
|
+
updateData.linkedForms = admin11.firestore.FieldValue.arrayRemove(formToRemove);
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
if (appointment.linkedFormIds && appointment.linkedFormIds.includes(filledDocument.id)) {
|
|
4548
|
+
updateData.linkedFormIds = admin11.firestore.FieldValue.arrayRemove(
|
|
4549
|
+
filledDocument.id
|
|
4550
|
+
);
|
|
4551
|
+
}
|
|
4552
|
+
if (filledDocument.isUserForm && filledDocument.isRequired) {
|
|
4553
|
+
if (filledDocument.status !== "completed" /* COMPLETED */ && filledDocument.status !== "signed" /* SIGNED */) {
|
|
4554
|
+
if (!appointment.pendingUserFormsIds || !appointment.pendingUserFormsIds.includes(filledDocument.id)) {
|
|
4555
|
+
updateData.pendingUserFormsIds = appointment.pendingUserFormsIds ? admin11.firestore.FieldValue.arrayUnion(filledDocument.id) : [filledDocument.id];
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
if (Object.keys(updateData).length > 0) {
|
|
4560
|
+
updateData.updatedAt = admin11.firestore.FieldValue.serverTimestamp();
|
|
4561
|
+
await appointmentRef.update(updateData);
|
|
4562
|
+
Logger.info(
|
|
4563
|
+
`[FilledFormsAggregationService] Successfully updated appointment ${filledDocument.appointmentId} after form deletion`
|
|
4564
|
+
);
|
|
4565
|
+
} else {
|
|
4566
|
+
Logger.info(
|
|
4567
|
+
`[FilledFormsAggregationService] No updates needed for appointment ${filledDocument.appointmentId}`
|
|
4568
|
+
);
|
|
4569
|
+
}
|
|
4570
|
+
} catch (error) {
|
|
4571
|
+
Logger.error(
|
|
4572
|
+
`[FilledFormsAggregationService] Error updating appointment after form deletion for ${filledDocument.id}:`,
|
|
4573
|
+
error
|
|
4574
|
+
);
|
|
4575
|
+
throw error;
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4578
|
+
/**
|
|
4579
|
+
* Updates the appointment's pendingUserFormsIds for a new required user form.
|
|
4580
|
+
* This should be called when a required user form is first created.
|
|
4581
|
+
* @param filledDocument The newly created filled document.
|
|
4582
|
+
* @returns {Promise<void>}
|
|
4583
|
+
*/
|
|
4584
|
+
async handleRequiredUserFormCreate(filledDocument) {
|
|
4585
|
+
if (!filledDocument.isUserForm || !filledDocument.isRequired) {
|
|
4586
|
+
return;
|
|
4587
|
+
}
|
|
4588
|
+
Logger.info(
|
|
4589
|
+
`[FilledFormsAggregationService] Handling new required user form creation: ${filledDocument.id}`
|
|
4590
|
+
);
|
|
4591
|
+
try {
|
|
4592
|
+
const appointmentRef = this.db.collection(APPOINTMENTS_COLLECTION).doc(filledDocument.appointmentId);
|
|
4593
|
+
if (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */) {
|
|
4594
|
+
Logger.info(
|
|
4595
|
+
`[FilledFormsAggregationService] Form ${filledDocument.id} is already completed/signed, not adding to pendingUserFormsIds`
|
|
4596
|
+
);
|
|
4597
|
+
return;
|
|
4598
|
+
}
|
|
4599
|
+
await appointmentRef.update({
|
|
4600
|
+
pendingUserFormsIds: admin11.firestore.FieldValue.arrayUnion(
|
|
4601
|
+
filledDocument.id
|
|
4602
|
+
),
|
|
4603
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4604
|
+
});
|
|
4605
|
+
Logger.info(
|
|
4606
|
+
`[FilledFormsAggregationService] Successfully added form ${filledDocument.id} to pendingUserFormsIds for appointment ${filledDocument.appointmentId}`
|
|
4607
|
+
);
|
|
4608
|
+
} catch (error) {
|
|
4609
|
+
Logger.error(
|
|
4610
|
+
`[FilledFormsAggregationService] Error handling required user form creation for ${filledDocument.id}:`,
|
|
4611
|
+
error
|
|
4612
|
+
);
|
|
4613
|
+
throw error;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
};
|
|
4617
|
+
|
|
4618
|
+
// src/admin/aggregation/reviews/reviews.aggregation.service.ts
|
|
4619
|
+
var admin12 = __toESM(require("firebase-admin"));
|
|
4620
|
+
|
|
4621
|
+
// src/types/reviews/index.ts
|
|
4622
|
+
var REVIEWS_COLLECTION = "reviews";
|
|
4623
|
+
|
|
4624
|
+
// src/admin/aggregation/reviews/reviews.aggregation.service.ts
|
|
4625
|
+
var ReviewsAggregationService = class {
|
|
4626
|
+
/**
|
|
4627
|
+
* Constructor for ReviewsAggregationService.
|
|
4628
|
+
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
4629
|
+
*/
|
|
4630
|
+
constructor(firestore15) {
|
|
4631
|
+
this.db = firestore15 || admin12.firestore();
|
|
4632
|
+
}
|
|
4633
|
+
/**
|
|
4634
|
+
* Process a newly created review and update all related entities
|
|
4635
|
+
* @param review The newly created review
|
|
4636
|
+
* @returns Promise resolving when all updates are complete
|
|
4637
|
+
*/
|
|
4638
|
+
async processNewReview(review) {
|
|
4639
|
+
console.log(
|
|
4640
|
+
`[ReviewsAggregationService] Processing new review: ${review.id}`
|
|
4641
|
+
);
|
|
4642
|
+
const updatePromises = [];
|
|
4643
|
+
if (review.clinicReview) {
|
|
4644
|
+
updatePromises.push(
|
|
4645
|
+
this.updateClinicReviewInfo(review.clinicReview.clinicId)
|
|
4646
|
+
);
|
|
4647
|
+
}
|
|
4648
|
+
if (review.practitionerReview) {
|
|
4649
|
+
updatePromises.push(
|
|
4650
|
+
this.updatePractitionerReviewInfo(
|
|
4651
|
+
review.practitionerReview.practitionerId
|
|
4652
|
+
)
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4655
|
+
if (review.procedureReview) {
|
|
4656
|
+
updatePromises.push(
|
|
4657
|
+
this.updateProcedureReviewInfo(review.procedureReview.procedureId)
|
|
4658
|
+
);
|
|
4659
|
+
}
|
|
4660
|
+
await Promise.all(updatePromises);
|
|
4661
|
+
console.log(
|
|
4662
|
+
`[ReviewsAggregationService] Successfully processed review: ${review.id}`
|
|
4663
|
+
);
|
|
4664
|
+
}
|
|
4665
|
+
/**
|
|
4666
|
+
* Process a deleted review and update all related entities
|
|
4667
|
+
* @param review The deleted review
|
|
4668
|
+
* @returns Promise resolving when all updates are complete
|
|
4669
|
+
*/
|
|
4670
|
+
async processDeletedReview(review) {
|
|
4671
|
+
console.log(
|
|
4672
|
+
`[ReviewsAggregationService] Processing deleted review: ${review.id}`
|
|
4673
|
+
);
|
|
4674
|
+
const updatePromises = [];
|
|
4675
|
+
if (review.clinicReview) {
|
|
4676
|
+
updatePromises.push(
|
|
4677
|
+
this.updateClinicReviewInfo(
|
|
4678
|
+
review.clinicReview.clinicId,
|
|
4679
|
+
review.clinicReview,
|
|
4680
|
+
true
|
|
4681
|
+
)
|
|
4682
|
+
);
|
|
4683
|
+
}
|
|
4684
|
+
if (review.practitionerReview) {
|
|
4685
|
+
updatePromises.push(
|
|
4686
|
+
this.updatePractitionerReviewInfo(
|
|
4687
|
+
review.practitionerReview.practitionerId,
|
|
4688
|
+
review.practitionerReview,
|
|
4689
|
+
true
|
|
4690
|
+
)
|
|
4691
|
+
);
|
|
4692
|
+
}
|
|
4693
|
+
if (review.procedureReview) {
|
|
4694
|
+
updatePromises.push(
|
|
4695
|
+
this.updateProcedureReviewInfo(
|
|
4696
|
+
review.procedureReview.procedureId,
|
|
4697
|
+
review.procedureReview,
|
|
4698
|
+
true
|
|
4699
|
+
)
|
|
4700
|
+
);
|
|
4701
|
+
}
|
|
4702
|
+
await Promise.all(updatePromises);
|
|
4703
|
+
console.log(
|
|
4704
|
+
`[ReviewsAggregationService] Successfully processed deleted review: ${review.id}`
|
|
4705
|
+
);
|
|
4706
|
+
}
|
|
4707
|
+
/**
|
|
4708
|
+
* Updates the review info for a clinic
|
|
4709
|
+
* @param clinicId The ID of the clinic to update
|
|
4710
|
+
* @param removedReview Optional review being removed
|
|
4711
|
+
* @param isRemoval Whether this update is for a review removal
|
|
4712
|
+
* @returns The updated clinic review info
|
|
4713
|
+
*/
|
|
4714
|
+
async updateClinicReviewInfo(clinicId, removedReview, isRemoval = false) {
|
|
4715
|
+
console.log(
|
|
4716
|
+
`[ReviewsAggregationService] Updating review info for clinic: ${clinicId}`
|
|
4717
|
+
);
|
|
4718
|
+
const clinicDoc = await this.db.collection(CLINICS_COLLECTION).doc(clinicId).get();
|
|
4719
|
+
if (!clinicDoc.exists) {
|
|
4720
|
+
console.error(
|
|
4721
|
+
`[ReviewsAggregationService] Clinic with ID ${clinicId} not found`
|
|
4722
|
+
);
|
|
4723
|
+
throw new Error(`Clinic with ID ${clinicId} not found`);
|
|
4724
|
+
}
|
|
4725
|
+
const clinicData = clinicDoc.data();
|
|
4726
|
+
const currentReviewInfo = (clinicData == null ? void 0 : clinicData.reviewInfo) || {
|
|
4727
|
+
totalReviews: 0,
|
|
4728
|
+
averageRating: 0,
|
|
4729
|
+
cleanliness: 0,
|
|
4730
|
+
facilities: 0,
|
|
4731
|
+
staffFriendliness: 0,
|
|
4732
|
+
waitingTime: 0,
|
|
4733
|
+
accessibility: 0,
|
|
4734
|
+
recommendationPercentage: 0
|
|
4735
|
+
};
|
|
4736
|
+
const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("clinicReview.clinicId", "==", clinicId).get();
|
|
4737
|
+
if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
|
|
4738
|
+
const updatedReviewInfo2 = {
|
|
4739
|
+
totalReviews: 0,
|
|
4740
|
+
averageRating: 0,
|
|
4741
|
+
cleanliness: 0,
|
|
4742
|
+
facilities: 0,
|
|
4743
|
+
staffFriendliness: 0,
|
|
4744
|
+
waitingTime: 0,
|
|
4745
|
+
accessibility: 0,
|
|
4746
|
+
recommendationPercentage: 0
|
|
4747
|
+
};
|
|
4748
|
+
await this.db.collection(CLINICS_COLLECTION).doc(clinicId).update({
|
|
4749
|
+
reviewInfo: updatedReviewInfo2,
|
|
4750
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4751
|
+
});
|
|
4752
|
+
console.log(
|
|
4753
|
+
`[ReviewsAggregationService] Reset review info for clinic: ${clinicId}`
|
|
4754
|
+
);
|
|
4755
|
+
return updatedReviewInfo2;
|
|
4756
|
+
}
|
|
4757
|
+
const reviews = reviewsQuery.docs.map((doc) => doc.data());
|
|
4758
|
+
const clinicReviews = reviews.map((review) => review.clinicReview).filter((review) => review !== void 0);
|
|
4759
|
+
let totalRating = 0;
|
|
4760
|
+
let totalCleanliness = 0;
|
|
4761
|
+
let totalFacilities = 0;
|
|
4762
|
+
let totalStaffFriendliness = 0;
|
|
4763
|
+
let totalWaitingTime = 0;
|
|
4764
|
+
let totalAccessibility = 0;
|
|
4765
|
+
let totalRecommendations = 0;
|
|
4766
|
+
clinicReviews.forEach((review) => {
|
|
4767
|
+
totalRating += review.overallRating;
|
|
4768
|
+
totalCleanliness += review.cleanliness;
|
|
4769
|
+
totalFacilities += review.facilities;
|
|
4770
|
+
totalStaffFriendliness += review.staffFriendliness;
|
|
4771
|
+
totalWaitingTime += review.waitingTime;
|
|
4772
|
+
totalAccessibility += review.accessibility;
|
|
4773
|
+
if (review.wouldRecommend) totalRecommendations++;
|
|
4774
|
+
});
|
|
4775
|
+
const count = clinicReviews.length;
|
|
4776
|
+
const roundToOneDecimal = (value) => Math.round(value / count * 10) / 10;
|
|
4777
|
+
const updatedReviewInfo = {
|
|
4778
|
+
totalReviews: count,
|
|
4779
|
+
averageRating: roundToOneDecimal(totalRating),
|
|
4780
|
+
cleanliness: roundToOneDecimal(totalCleanliness),
|
|
4781
|
+
facilities: roundToOneDecimal(totalFacilities),
|
|
4782
|
+
staffFriendliness: roundToOneDecimal(totalStaffFriendliness),
|
|
4783
|
+
waitingTime: roundToOneDecimal(totalWaitingTime),
|
|
4784
|
+
accessibility: roundToOneDecimal(totalAccessibility),
|
|
4785
|
+
recommendationPercentage: Math.round(totalRecommendations / count * 1e3) / 10
|
|
4786
|
+
};
|
|
4787
|
+
await this.db.collection(CLINICS_COLLECTION).doc(clinicId).update({
|
|
4788
|
+
reviewInfo: updatedReviewInfo,
|
|
4789
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4790
|
+
});
|
|
4791
|
+
console.log(
|
|
4792
|
+
`[ReviewsAggregationService] Updated review info for clinic: ${clinicId}`
|
|
4793
|
+
);
|
|
4794
|
+
return updatedReviewInfo;
|
|
4795
|
+
}
|
|
4796
|
+
/**
|
|
4797
|
+
* Updates the review info for a practitioner
|
|
4798
|
+
* @param practitionerId The ID of the practitioner to update
|
|
4799
|
+
* @param removedReview Optional review being removed
|
|
4800
|
+
* @param isRemoval Whether this update is for a review removal
|
|
4801
|
+
* @returns The updated practitioner review info
|
|
4802
|
+
*/
|
|
4803
|
+
async updatePractitionerReviewInfo(practitionerId, removedReview, isRemoval = false) {
|
|
4804
|
+
console.log(
|
|
4805
|
+
`[ReviewsAggregationService] Updating review info for practitioner: ${practitionerId}`
|
|
4806
|
+
);
|
|
4807
|
+
const practitionerDoc = await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).get();
|
|
4808
|
+
if (!practitionerDoc.exists) {
|
|
4809
|
+
console.error(
|
|
4810
|
+
`[ReviewsAggregationService] Practitioner with ID ${practitionerId} not found`
|
|
4811
|
+
);
|
|
4812
|
+
throw new Error(`Practitioner with ID ${practitionerId} not found`);
|
|
4813
|
+
}
|
|
4814
|
+
const practitionerData = practitionerDoc.data();
|
|
4815
|
+
const currentReviewInfo = (practitionerData == null ? void 0 : practitionerData.reviewInfo) || {
|
|
4816
|
+
totalReviews: 0,
|
|
4817
|
+
averageRating: 0,
|
|
4818
|
+
knowledgeAndExpertise: 0,
|
|
4819
|
+
communicationSkills: 0,
|
|
4820
|
+
bedSideManner: 0,
|
|
4821
|
+
thoroughness: 0,
|
|
4822
|
+
trustworthiness: 0,
|
|
4823
|
+
recommendationPercentage: 0
|
|
4824
|
+
};
|
|
4825
|
+
const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("practitionerReview.practitionerId", "==", practitionerId).get();
|
|
4826
|
+
if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
|
|
4827
|
+
const updatedReviewInfo2 = {
|
|
4828
|
+
totalReviews: 0,
|
|
4829
|
+
averageRating: 0,
|
|
4830
|
+
knowledgeAndExpertise: 0,
|
|
4831
|
+
communicationSkills: 0,
|
|
4832
|
+
bedSideManner: 0,
|
|
4833
|
+
thoroughness: 0,
|
|
4834
|
+
trustworthiness: 0,
|
|
4835
|
+
recommendationPercentage: 0
|
|
4836
|
+
};
|
|
4837
|
+
await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).update({
|
|
4838
|
+
reviewInfo: updatedReviewInfo2,
|
|
4839
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4840
|
+
});
|
|
4841
|
+
await this.updateDoctorInfoInProcedures(practitionerId, 0);
|
|
4842
|
+
console.log(
|
|
4843
|
+
`[ReviewsAggregationService] Reset review info for practitioner: ${practitionerId}`
|
|
4844
|
+
);
|
|
4845
|
+
return updatedReviewInfo2;
|
|
4846
|
+
}
|
|
4847
|
+
const reviews = reviewsQuery.docs.map((doc) => doc.data());
|
|
4848
|
+
const practitionerReviews = reviews.map((review) => review.practitionerReview).filter((review) => review !== void 0);
|
|
4849
|
+
let totalRating = 0;
|
|
4850
|
+
let totalKnowledgeAndExpertise = 0;
|
|
4851
|
+
let totalCommunicationSkills = 0;
|
|
4852
|
+
let totalBedSideManner = 0;
|
|
4853
|
+
let totalThoroughness = 0;
|
|
4854
|
+
let totalTrustworthiness = 0;
|
|
4855
|
+
let totalRecommendations = 0;
|
|
4856
|
+
practitionerReviews.forEach((review) => {
|
|
4857
|
+
totalRating += review.overallRating;
|
|
4858
|
+
totalKnowledgeAndExpertise += review.knowledgeAndExpertise;
|
|
4859
|
+
totalCommunicationSkills += review.communicationSkills;
|
|
4860
|
+
totalBedSideManner += review.bedSideManner;
|
|
4861
|
+
totalThoroughness += review.thoroughness;
|
|
4862
|
+
totalTrustworthiness += review.trustworthiness;
|
|
4863
|
+
if (review.wouldRecommend) totalRecommendations++;
|
|
4864
|
+
});
|
|
4865
|
+
const count = practitionerReviews.length;
|
|
4866
|
+
const roundToOneDecimal = (value) => Math.round(value / count * 10) / 10;
|
|
4867
|
+
const updatedReviewInfo = {
|
|
4868
|
+
totalReviews: count,
|
|
4869
|
+
averageRating: roundToOneDecimal(totalRating),
|
|
4870
|
+
knowledgeAndExpertise: roundToOneDecimal(totalKnowledgeAndExpertise),
|
|
4871
|
+
communicationSkills: roundToOneDecimal(totalCommunicationSkills),
|
|
4872
|
+
bedSideManner: roundToOneDecimal(totalBedSideManner),
|
|
4873
|
+
thoroughness: roundToOneDecimal(totalThoroughness),
|
|
4874
|
+
trustworthiness: roundToOneDecimal(totalTrustworthiness),
|
|
4875
|
+
recommendationPercentage: Math.round(totalRecommendations / count * 1e3) / 10
|
|
4876
|
+
};
|
|
4877
|
+
await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).update({
|
|
4878
|
+
reviewInfo: updatedReviewInfo,
|
|
4879
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4880
|
+
});
|
|
4881
|
+
await this.updateDoctorInfoInProcedures(
|
|
4882
|
+
practitionerId,
|
|
4883
|
+
updatedReviewInfo.averageRating
|
|
4884
|
+
);
|
|
4885
|
+
console.log(
|
|
4886
|
+
`[ReviewsAggregationService] Updated review info for practitioner: ${practitionerId}`
|
|
4887
|
+
);
|
|
4888
|
+
return updatedReviewInfo;
|
|
4889
|
+
}
|
|
4890
|
+
/**
|
|
4891
|
+
* Updates the review info for a procedure
|
|
4892
|
+
* @param procedureId The ID of the procedure to update
|
|
4893
|
+
* @param removedReview Optional review being removed
|
|
4894
|
+
* @param isRemoval Whether this update is for a review removal
|
|
4895
|
+
* @returns The updated procedure review info
|
|
4896
|
+
*/
|
|
4897
|
+
async updateProcedureReviewInfo(procedureId, removedReview, isRemoval = false) {
|
|
4898
|
+
console.log(
|
|
4899
|
+
`[ReviewsAggregationService] Updating review info for procedure: ${procedureId}`
|
|
4900
|
+
);
|
|
4901
|
+
const procedureDoc = await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).get();
|
|
4902
|
+
if (!procedureDoc.exists) {
|
|
4903
|
+
console.error(
|
|
4904
|
+
`[ReviewsAggregationService] Procedure with ID ${procedureId} not found`
|
|
4905
|
+
);
|
|
4906
|
+
throw new Error(`Procedure with ID ${procedureId} not found`);
|
|
4907
|
+
}
|
|
4908
|
+
const procedureData = procedureDoc.data();
|
|
4909
|
+
const currentReviewInfo = (procedureData == null ? void 0 : procedureData.reviewInfo) || {
|
|
4910
|
+
totalReviews: 0,
|
|
4911
|
+
averageRating: 0,
|
|
4912
|
+
effectivenessOfTreatment: 0,
|
|
4913
|
+
outcomeExplanation: 0,
|
|
4914
|
+
painManagement: 0,
|
|
4915
|
+
followUpCare: 0,
|
|
4916
|
+
valueForMoney: 0,
|
|
4917
|
+
recommendationPercentage: 0
|
|
4918
|
+
};
|
|
4919
|
+
const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("procedureReview.procedureId", "==", procedureId).get();
|
|
4920
|
+
if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
|
|
4921
|
+
const updatedReviewInfo2 = {
|
|
4922
|
+
totalReviews: 0,
|
|
4923
|
+
averageRating: 0,
|
|
4924
|
+
effectivenessOfTreatment: 0,
|
|
4925
|
+
outcomeExplanation: 0,
|
|
4926
|
+
painManagement: 0,
|
|
4927
|
+
followUpCare: 0,
|
|
4928
|
+
valueForMoney: 0,
|
|
4929
|
+
recommendationPercentage: 0
|
|
4930
|
+
};
|
|
4931
|
+
await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).update({
|
|
4932
|
+
reviewInfo: updatedReviewInfo2,
|
|
4933
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4934
|
+
});
|
|
4935
|
+
console.log(
|
|
4936
|
+
`[ReviewsAggregationService] Reset review info for procedure: ${procedureId}`
|
|
4937
|
+
);
|
|
4938
|
+
return updatedReviewInfo2;
|
|
4939
|
+
}
|
|
4940
|
+
const reviews = reviewsQuery.docs.map((doc) => doc.data());
|
|
4941
|
+
const procedureReviews = reviews.map((review) => review.procedureReview).filter((review) => review !== void 0);
|
|
4942
|
+
let totalRating = 0;
|
|
4943
|
+
let totalEffectivenessOfTreatment = 0;
|
|
4944
|
+
let totalOutcomeExplanation = 0;
|
|
4945
|
+
let totalPainManagement = 0;
|
|
4946
|
+
let totalFollowUpCare = 0;
|
|
4947
|
+
let totalValueForMoney = 0;
|
|
4948
|
+
let totalRecommendations = 0;
|
|
4949
|
+
procedureReviews.forEach((review) => {
|
|
4950
|
+
totalRating += review.overallRating;
|
|
4951
|
+
totalEffectivenessOfTreatment += review.effectivenessOfTreatment;
|
|
4952
|
+
totalOutcomeExplanation += review.outcomeExplanation;
|
|
4953
|
+
totalPainManagement += review.painManagement;
|
|
4954
|
+
totalFollowUpCare += review.followUpCare;
|
|
4955
|
+
totalValueForMoney += review.valueForMoney;
|
|
4956
|
+
if (review.wouldRecommend) totalRecommendations++;
|
|
4957
|
+
});
|
|
4958
|
+
const count = procedureReviews.length;
|
|
4959
|
+
const roundToOneDecimal = (value) => Math.round(value / count * 10) / 10;
|
|
4960
|
+
const updatedReviewInfo = {
|
|
4961
|
+
totalReviews: count,
|
|
4962
|
+
averageRating: roundToOneDecimal(totalRating),
|
|
4963
|
+
effectivenessOfTreatment: roundToOneDecimal(
|
|
4964
|
+
totalEffectivenessOfTreatment
|
|
4965
|
+
),
|
|
4966
|
+
outcomeExplanation: roundToOneDecimal(totalOutcomeExplanation),
|
|
4967
|
+
painManagement: roundToOneDecimal(totalPainManagement),
|
|
4968
|
+
followUpCare: roundToOneDecimal(totalFollowUpCare),
|
|
4969
|
+
valueForMoney: roundToOneDecimal(totalValueForMoney),
|
|
4970
|
+
recommendationPercentage: Math.round(totalRecommendations / count * 1e3) / 10
|
|
4971
|
+
};
|
|
4972
|
+
await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).update({
|
|
4973
|
+
reviewInfo: updatedReviewInfo,
|
|
4974
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4975
|
+
});
|
|
4976
|
+
console.log(
|
|
4977
|
+
`[ReviewsAggregationService] Updated review info for procedure: ${procedureId}`
|
|
4978
|
+
);
|
|
4979
|
+
return updatedReviewInfo;
|
|
4980
|
+
}
|
|
4981
|
+
/**
|
|
4982
|
+
* Updates doctorInfo rating in all procedures for a practitioner
|
|
4983
|
+
* @param practitionerId The ID of the practitioner
|
|
4984
|
+
* @param rating The new rating to set
|
|
4985
|
+
*/
|
|
4986
|
+
async updateDoctorInfoInProcedures(practitionerId, rating) {
|
|
4987
|
+
console.log(
|
|
4988
|
+
`[ReviewsAggregationService] Updating doctor info in procedures for practitioner: ${practitionerId}`
|
|
4989
|
+
);
|
|
4990
|
+
const proceduresQuery = await this.db.collection(PROCEDURES_COLLECTION).where("practitionerId", "==", practitionerId).get();
|
|
4991
|
+
if (proceduresQuery.empty) {
|
|
4992
|
+
console.log(
|
|
4993
|
+
`[ReviewsAggregationService] No procedures found for practitioner: ${practitionerId}`
|
|
4994
|
+
);
|
|
4995
|
+
return;
|
|
4996
|
+
}
|
|
4997
|
+
const batch = this.db.batch();
|
|
4998
|
+
proceduresQuery.docs.forEach((docSnapshot) => {
|
|
4999
|
+
const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(docSnapshot.id);
|
|
5000
|
+
batch.update(procedureRef, {
|
|
5001
|
+
"doctorInfo.rating": rating,
|
|
5002
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
5003
|
+
});
|
|
5004
|
+
});
|
|
5005
|
+
await batch.commit();
|
|
5006
|
+
console.log(
|
|
5007
|
+
`[ReviewsAggregationService] Updated doctor info in ${proceduresQuery.size} procedures for practitioner: ${practitionerId}`
|
|
5008
|
+
);
|
|
5009
|
+
}
|
|
5010
|
+
/**
|
|
5011
|
+
* Verifies a review as checked by admin/staff
|
|
5012
|
+
* @param reviewId The ID of the review to verify
|
|
5013
|
+
*/
|
|
5014
|
+
async verifyReview(reviewId) {
|
|
5015
|
+
console.log(`[ReviewsAggregationService] Verifying review: ${reviewId}`);
|
|
5016
|
+
const reviewDoc = await this.db.collection(REVIEWS_COLLECTION).doc(reviewId).get();
|
|
5017
|
+
if (!reviewDoc.exists) {
|
|
5018
|
+
console.error(
|
|
5019
|
+
`[ReviewsAggregationService] Review with ID ${reviewId} not found`
|
|
5020
|
+
);
|
|
5021
|
+
throw new Error(`Review with ID ${reviewId} not found`);
|
|
5022
|
+
}
|
|
5023
|
+
const review = reviewDoc.data();
|
|
5024
|
+
const batch = this.db.batch();
|
|
5025
|
+
const reviewRef = this.db.collection(REVIEWS_COLLECTION).doc(reviewId);
|
|
5026
|
+
if (review.clinicReview) {
|
|
5027
|
+
review.clinicReview.isVerified = true;
|
|
5028
|
+
}
|
|
5029
|
+
if (review.practitionerReview) {
|
|
5030
|
+
review.practitionerReview.isVerified = true;
|
|
5031
|
+
}
|
|
5032
|
+
if (review.procedureReview) {
|
|
5033
|
+
review.procedureReview.isVerified = true;
|
|
5034
|
+
}
|
|
5035
|
+
batch.update(reviewRef, {
|
|
5036
|
+
clinicReview: review.clinicReview,
|
|
5037
|
+
practitionerReview: review.practitionerReview,
|
|
5038
|
+
procedureReview: review.procedureReview,
|
|
5039
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
5040
|
+
});
|
|
5041
|
+
await batch.commit();
|
|
5042
|
+
console.log(
|
|
5043
|
+
`[ReviewsAggregationService] Successfully verified review: ${reviewId}`
|
|
5044
|
+
);
|
|
5045
|
+
}
|
|
5046
|
+
/**
|
|
5047
|
+
* Calculate the average of all reviews for an entity
|
|
5048
|
+
* @param entityId The entity ID
|
|
5049
|
+
* @param entityType The type of entity ('clinic', 'practitioner', or 'procedure')
|
|
5050
|
+
* @returns Promise that resolves to the calculated review info
|
|
5051
|
+
*/
|
|
5052
|
+
async calculateEntityReviewInfo(entityId, entityType) {
|
|
5053
|
+
console.log(
|
|
5054
|
+
`[ReviewsAggregationService] Calculating review info for ${entityType}: ${entityId}`
|
|
5055
|
+
);
|
|
5056
|
+
switch (entityType) {
|
|
5057
|
+
case "clinic":
|
|
5058
|
+
return this.updateClinicReviewInfo(entityId);
|
|
5059
|
+
case "practitioner":
|
|
5060
|
+
return this.updatePractitionerReviewInfo(entityId);
|
|
5061
|
+
case "procedure":
|
|
5062
|
+
return this.updateProcedureReviewInfo(entityId);
|
|
5063
|
+
default:
|
|
5064
|
+
throw new Error(`Invalid entity type: ${entityType}`);
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
};
|
|
5068
|
+
|
|
4415
5069
|
// src/admin/mailing/practitionerInvite/templates/invitation.template.ts
|
|
4416
5070
|
var practitionerInvitationTemplate = `
|
|
4417
5071
|
<!DOCTYPE html>
|
|
@@ -4519,8 +5173,8 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
4519
5173
|
* @param firestore Firestore instance provided by the caller
|
|
4520
5174
|
* @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
|
|
4521
5175
|
*/
|
|
4522
|
-
constructor(
|
|
4523
|
-
super(
|
|
5176
|
+
constructor(firestore15, mailgunClient) {
|
|
5177
|
+
super(firestore15, mailgunClient);
|
|
4524
5178
|
this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
|
|
4525
5179
|
this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
|
|
4526
5180
|
this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
|
|
@@ -4758,7 +5412,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
4758
5412
|
};
|
|
4759
5413
|
|
|
4760
5414
|
// src/admin/booking/booking.admin.ts
|
|
4761
|
-
var
|
|
5415
|
+
var admin14 = __toESM(require("firebase-admin"));
|
|
4762
5416
|
|
|
4763
5417
|
// src/admin/booking/booking.calculator.ts
|
|
4764
5418
|
var import_firestore2 = require("firebase/firestore");
|
|
@@ -5192,10 +5846,10 @@ var BookingAvailabilityCalculator = class {
|
|
|
5192
5846
|
BookingAvailabilityCalculator.DEFAULT_INTERVAL_MINUTES = 15;
|
|
5193
5847
|
|
|
5194
5848
|
// src/admin/documentation-templates/document-manager.admin.ts
|
|
5195
|
-
var
|
|
5849
|
+
var admin13 = __toESM(require("firebase-admin"));
|
|
5196
5850
|
var DocumentManagerAdminService = class {
|
|
5197
|
-
constructor(
|
|
5198
|
-
this.db =
|
|
5851
|
+
constructor(firestore15) {
|
|
5852
|
+
this.db = firestore15;
|
|
5199
5853
|
}
|
|
5200
5854
|
/**
|
|
5201
5855
|
* Adds operations to a Firestore batch to initialize all linked forms for a new appointment
|
|
@@ -5298,7 +5952,7 @@ var DocumentManagerAdminService = class {
|
|
|
5298
5952
|
};
|
|
5299
5953
|
}
|
|
5300
5954
|
const templateIds = technologyTemplates.map((t) => t.templateId);
|
|
5301
|
-
const templatesSnapshot = await this.db.collection(DOCUMENTATION_TEMPLATES_COLLECTION).where(
|
|
5955
|
+
const templatesSnapshot = await this.db.collection(DOCUMENTATION_TEMPLATES_COLLECTION).where(admin13.firestore.FieldPath.documentId(), "in", templateIds).get();
|
|
5302
5956
|
const templatesMap = /* @__PURE__ */ new Map();
|
|
5303
5957
|
templatesSnapshot.forEach((doc) => {
|
|
5304
5958
|
templatesMap.set(doc.id, doc.data());
|
|
@@ -5364,8 +6018,8 @@ var BookingAdmin = class {
|
|
|
5364
6018
|
* Creates a new BookingAdmin instance
|
|
5365
6019
|
* @param firestore - Firestore instance provided by the caller
|
|
5366
6020
|
*/
|
|
5367
|
-
constructor(
|
|
5368
|
-
this.db =
|
|
6021
|
+
constructor(firestore15) {
|
|
6022
|
+
this.db = firestore15 || admin14.firestore();
|
|
5369
6023
|
this.documentManagerAdmin = new DocumentManagerAdminService(this.db);
|
|
5370
6024
|
}
|
|
5371
6025
|
/**
|
|
@@ -5382,8 +6036,8 @@ var BookingAdmin = class {
|
|
|
5382
6036
|
console.log(
|
|
5383
6037
|
`[BookingAdmin] Getting available slots for clinic ${clinicId}, practitioner ${practitionerId}, procedure ${procedureId}`
|
|
5384
6038
|
);
|
|
5385
|
-
const start = timeframe.start instanceof Date ?
|
|
5386
|
-
const end = timeframe.end instanceof Date ?
|
|
6039
|
+
const start = timeframe.start instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.start) : timeframe.start;
|
|
6040
|
+
const end = timeframe.end instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.end) : timeframe.end;
|
|
5387
6041
|
const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
|
|
5388
6042
|
if (!clinicDoc.exists) {
|
|
5389
6043
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
@@ -5422,7 +6076,7 @@ var BookingAdmin = class {
|
|
|
5422
6076
|
const result = BookingAvailabilityCalculator.calculateSlots(request);
|
|
5423
6077
|
return {
|
|
5424
6078
|
availableSlots: result.availableSlots.map((slot) => ({
|
|
5425
|
-
start:
|
|
6079
|
+
start: admin14.firestore.Timestamp.fromMillis(slot.start.toMillis())
|
|
5426
6080
|
}))
|
|
5427
6081
|
};
|
|
5428
6082
|
} catch (error) {
|
|
@@ -5524,8 +6178,8 @@ var BookingAdmin = class {
|
|
|
5524
6178
|
`[BookingAdmin] Orchestrating appointment creation for patient ${data.patientId} by user ${authenticatedUserId}`
|
|
5525
6179
|
);
|
|
5526
6180
|
const batch = this.db.batch();
|
|
5527
|
-
const adminTsNow =
|
|
5528
|
-
const serverTimestampValue =
|
|
6181
|
+
const adminTsNow = admin14.firestore.Timestamp.now();
|
|
6182
|
+
const serverTimestampValue = admin14.firestore.FieldValue.serverTimestamp();
|
|
5529
6183
|
try {
|
|
5530
6184
|
if (!data.patientId || !data.procedureId || !data.appointmentStartTime || !data.appointmentEndTime) {
|
|
5531
6185
|
return {
|
|
@@ -5622,7 +6276,7 @@ var BookingAdmin = class {
|
|
|
5622
6276
|
fullName: `${(patientSensitiveData == null ? void 0 : patientSensitiveData.firstName) || ""} ${(patientSensitiveData == null ? void 0 : patientSensitiveData.lastName) || ""}`.trim() || patientProfileData.displayName,
|
|
5623
6277
|
email: (patientSensitiveData == null ? void 0 : patientSensitiveData.email) || "",
|
|
5624
6278
|
phone: (patientSensitiveData == null ? void 0 : patientSensitiveData.phoneNumber) || patientProfileData.phoneNumber || null,
|
|
5625
|
-
dateOfBirth: (patientSensitiveData == null ? void 0 : patientSensitiveData.dateOfBirth) || patientProfileData.dateOfBirth ||
|
|
6279
|
+
dateOfBirth: (patientSensitiveData == null ? void 0 : patientSensitiveData.dateOfBirth) || patientProfileData.dateOfBirth || admin14.firestore.Timestamp.now(),
|
|
5626
6280
|
gender: (patientSensitiveData == null ? void 0 : patientSensitiveData.gender) || "other" /* OTHER */
|
|
5627
6281
|
};
|
|
5628
6282
|
const newAppointmentId = this.db.collection(APPOINTMENTS_COLLECTION).doc().id;
|
|
@@ -5873,6 +6527,7 @@ TimestampUtils.enableServerMode();
|
|
|
5873
6527
|
CalendarAdminService,
|
|
5874
6528
|
ClinicAggregationService,
|
|
5875
6529
|
DocumentManagerAdminService,
|
|
6530
|
+
FilledFormsAggregationService,
|
|
5876
6531
|
Logger,
|
|
5877
6532
|
MediaType,
|
|
5878
6533
|
NOTIFICATIONS_COLLECTION,
|
|
@@ -5889,5 +6544,6 @@ TimestampUtils.enableServerMode();
|
|
|
5889
6544
|
PractitionerInviteMailingService,
|
|
5890
6545
|
PractitionerTokenStatus,
|
|
5891
6546
|
ProcedureAggregationService,
|
|
6547
|
+
ReviewsAggregationService,
|
|
5892
6548
|
UserRole
|
|
5893
6549
|
});
|