@blackcode_sa/metaestetics-api 1.7.11 → 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/dist/index.d.mts +138 -37
- package/dist/index.d.ts +138 -37
- package/dist/index.js +543 -875
- package/dist/index.mjs +588 -922
- package/package.json +1 -1
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +316 -0
- package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +641 -0
- package/src/admin/index.ts +8 -1
- package/src/services/auth.service.ts +4 -0
- package/src/services/clinic/clinic-group.service.ts +49 -0
- package/src/services/clinic/clinic.service.ts +12 -0
- package/src/services/reviews/reviews.service.ts +6 -572
- package/src/types/clinic/index.ts +16 -0
- package/src/validations/clinic.schema.ts +25 -9
- package/src/validations/media.schema.ts +10 -0
package/dist/admin/index.mjs
CHANGED
|
@@ -148,9 +148,9 @@ var Logger = class {
|
|
|
148
148
|
|
|
149
149
|
// src/admin/notifications/notifications.admin.ts
|
|
150
150
|
var NotificationsAdmin = class {
|
|
151
|
-
constructor(
|
|
151
|
+
constructor(firestore15) {
|
|
152
152
|
this.expo = new Expo();
|
|
153
|
-
this.db =
|
|
153
|
+
this.db = firestore15 || admin.firestore();
|
|
154
154
|
}
|
|
155
155
|
/**
|
|
156
156
|
* Dohvata notifikaciju po ID-u
|
|
@@ -869,8 +869,8 @@ var ClinicAggregationService = class {
|
|
|
869
869
|
* Constructor for ClinicAggregationService.
|
|
870
870
|
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
871
871
|
*/
|
|
872
|
-
constructor(
|
|
873
|
-
this.db =
|
|
872
|
+
constructor(firestore15) {
|
|
873
|
+
this.db = firestore15 || admin3.firestore();
|
|
874
874
|
}
|
|
875
875
|
/**
|
|
876
876
|
* Adds clinic information to a clinic group when a new clinic is created
|
|
@@ -1344,8 +1344,8 @@ var ClinicAggregationService = class {
|
|
|
1344
1344
|
import * as admin4 from "firebase-admin";
|
|
1345
1345
|
var CALENDAR_SUBCOLLECTION_ID2 = "calendar";
|
|
1346
1346
|
var PractitionerAggregationService = class {
|
|
1347
|
-
constructor(
|
|
1348
|
-
this.db =
|
|
1347
|
+
constructor(firestore15) {
|
|
1348
|
+
this.db = firestore15 || admin4.firestore();
|
|
1349
1349
|
}
|
|
1350
1350
|
/**
|
|
1351
1351
|
* Adds practitioner information to a clinic when a new practitioner is created
|
|
@@ -1680,8 +1680,8 @@ var PractitionerAggregationService = class {
|
|
|
1680
1680
|
import * as admin5 from "firebase-admin";
|
|
1681
1681
|
var CALENDAR_SUBCOLLECTION_ID3 = "calendar";
|
|
1682
1682
|
var ProcedureAggregationService = class {
|
|
1683
|
-
constructor(
|
|
1684
|
-
this.db =
|
|
1683
|
+
constructor(firestore15) {
|
|
1684
|
+
this.db = firestore15 || admin5.firestore();
|
|
1685
1685
|
}
|
|
1686
1686
|
/**
|
|
1687
1687
|
* Adds procedure information to a practitioner when a new procedure is created
|
|
@@ -2065,8 +2065,8 @@ var ProcedureAggregationService = class {
|
|
|
2065
2065
|
import * as admin6 from "firebase-admin";
|
|
2066
2066
|
var CALENDAR_SUBCOLLECTION_ID4 = "calendar";
|
|
2067
2067
|
var PatientAggregationService = class {
|
|
2068
|
-
constructor(
|
|
2069
|
-
this.db =
|
|
2068
|
+
constructor(firestore15) {
|
|
2069
|
+
this.db = firestore15 || admin6.firestore();
|
|
2070
2070
|
}
|
|
2071
2071
|
// --- Methods for Patient Creation --- >
|
|
2072
2072
|
// No specific aggregations defined for patient creation in the plan.
|
|
@@ -2198,8 +2198,8 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
|
|
|
2198
2198
|
// src/admin/requirements/patient-requirements.admin.service.ts
|
|
2199
2199
|
import * as admin7 from "firebase-admin";
|
|
2200
2200
|
var PatientRequirementsAdminService = class {
|
|
2201
|
-
constructor(
|
|
2202
|
-
this.db =
|
|
2201
|
+
constructor(firestore15) {
|
|
2202
|
+
this.db = firestore15 || admin7.firestore();
|
|
2203
2203
|
this.notificationsAdmin = new NotificationsAdmin(this.db);
|
|
2204
2204
|
}
|
|
2205
2205
|
/**
|
|
@@ -2527,8 +2527,8 @@ var PatientRequirementsAdminService = class {
|
|
|
2527
2527
|
// src/admin/calendar/calendar.admin.service.ts
|
|
2528
2528
|
import * as admin8 from "firebase-admin";
|
|
2529
2529
|
var CalendarAdminService = class {
|
|
2530
|
-
constructor(
|
|
2531
|
-
this.db =
|
|
2530
|
+
constructor(firestore15) {
|
|
2531
|
+
this.db = firestore15 || admin8.firestore();
|
|
2532
2532
|
Logger.info("[CalendarAdminService] Initialized.");
|
|
2533
2533
|
}
|
|
2534
2534
|
/**
|
|
@@ -2811,9 +2811,9 @@ var BaseMailingService = class {
|
|
|
2811
2811
|
* @param firestore Firestore instance provided by the caller
|
|
2812
2812
|
* @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
|
|
2813
2813
|
*/
|
|
2814
|
-
constructor(
|
|
2814
|
+
constructor(firestore15, mailgunClient) {
|
|
2815
2815
|
var _a;
|
|
2816
|
-
this.db =
|
|
2816
|
+
this.db = firestore15;
|
|
2817
2817
|
this.mailgunClient = mailgunClient;
|
|
2818
2818
|
if (!this.db) {
|
|
2819
2819
|
Logger.error("[BaseMailingService] No Firestore instance provided");
|
|
@@ -2957,8 +2957,8 @@ var BaseMailingService = class {
|
|
|
2957
2957
|
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>";
|
|
2958
2958
|
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>";
|
|
2959
2959
|
var AppointmentMailingService = class extends BaseMailingService {
|
|
2960
|
-
constructor(
|
|
2961
|
-
super(
|
|
2960
|
+
constructor(firestore15, mailgunClient) {
|
|
2961
|
+
super(firestore15, mailgunClient);
|
|
2962
2962
|
this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
|
|
2963
2963
|
Logger.info("[AppointmentMailingService] Initialized.");
|
|
2964
2964
|
}
|
|
@@ -3107,8 +3107,8 @@ var AppointmentAggregationService = class {
|
|
|
3107
3107
|
* @param mailgunClient - An initialized Mailgun client instance.
|
|
3108
3108
|
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
3109
3109
|
*/
|
|
3110
|
-
constructor(mailgunClient,
|
|
3111
|
-
this.db =
|
|
3110
|
+
constructor(mailgunClient, firestore15) {
|
|
3111
|
+
this.db = firestore15 || admin10.firestore();
|
|
3112
3112
|
this.appointmentMailingService = new AppointmentMailingService(
|
|
3113
3113
|
this.db,
|
|
3114
3114
|
mailgunClient
|
|
@@ -4357,6 +4357,658 @@ var AppointmentAggregationService = class {
|
|
|
4357
4357
|
}
|
|
4358
4358
|
};
|
|
4359
4359
|
|
|
4360
|
+
// src/admin/aggregation/forms/filled-forms.aggregation.service.ts
|
|
4361
|
+
import * as admin11 from "firebase-admin";
|
|
4362
|
+
var FilledFormsAggregationService = class {
|
|
4363
|
+
/**
|
|
4364
|
+
* Constructor for FilledFormsAggregationService.
|
|
4365
|
+
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
4366
|
+
*/
|
|
4367
|
+
constructor(firestore15) {
|
|
4368
|
+
this.db = firestore15 || admin11.firestore();
|
|
4369
|
+
Logger.info("[FilledFormsAggregationService] Initialized");
|
|
4370
|
+
}
|
|
4371
|
+
/**
|
|
4372
|
+
* Handles side effects when a filled form is created or updated.
|
|
4373
|
+
* This function would typically be called by a Firestore onCreate or onUpdate trigger.
|
|
4374
|
+
* @param filledDocument The filled document that was created or updated.
|
|
4375
|
+
* @returns {Promise<void>}
|
|
4376
|
+
*/
|
|
4377
|
+
async handleFilledFormCreateOrUpdate(filledDocument) {
|
|
4378
|
+
Logger.info(
|
|
4379
|
+
`[FilledFormsAggregationService] Handling CREATE/UPDATE for filled form: ${filledDocument.id}, appointment: ${filledDocument.appointmentId}`
|
|
4380
|
+
);
|
|
4381
|
+
try {
|
|
4382
|
+
const appointmentRef = this.db.collection(APPOINTMENTS_COLLECTION).doc(filledDocument.appointmentId);
|
|
4383
|
+
const appointmentDoc = await appointmentRef.get();
|
|
4384
|
+
if (!appointmentDoc.exists) {
|
|
4385
|
+
Logger.error(
|
|
4386
|
+
`[FilledFormsAggregationService] Appointment ${filledDocument.appointmentId} not found.`
|
|
4387
|
+
);
|
|
4388
|
+
return;
|
|
4389
|
+
}
|
|
4390
|
+
const appointment = appointmentDoc.data();
|
|
4391
|
+
const formSubcollection = filledDocument.isUserForm ? USER_FORMS_SUBCOLLECTION : DOCTOR_FORMS_SUBCOLLECTION;
|
|
4392
|
+
const linkedFormInfo = {
|
|
4393
|
+
formId: filledDocument.id,
|
|
4394
|
+
templateId: filledDocument.templateId,
|
|
4395
|
+
templateVersion: filledDocument.templateVersion,
|
|
4396
|
+
title: filledDocument.isUserForm ? "User Form" : "Doctor Form",
|
|
4397
|
+
// Default title if not available
|
|
4398
|
+
isUserForm: filledDocument.isUserForm,
|
|
4399
|
+
isRequired: filledDocument.isRequired,
|
|
4400
|
+
status: filledDocument.status,
|
|
4401
|
+
path: `${APPOINTMENTS_COLLECTION}/${filledDocument.appointmentId}/${formSubcollection}/${filledDocument.id}`
|
|
4402
|
+
};
|
|
4403
|
+
if (filledDocument.updatedAt) {
|
|
4404
|
+
linkedFormInfo.submittedAt = admin11.firestore.Timestamp.fromMillis(
|
|
4405
|
+
filledDocument.updatedAt
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
if (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */) {
|
|
4409
|
+
linkedFormInfo.completedAt = admin11.firestore.Timestamp.fromMillis(
|
|
4410
|
+
filledDocument.updatedAt
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
let updateData = {};
|
|
4414
|
+
let existingFormIndex = -1;
|
|
4415
|
+
if (appointment.linkedForms && appointment.linkedForms.length > 0) {
|
|
4416
|
+
existingFormIndex = appointment.linkedForms.findIndex(
|
|
4417
|
+
(form) => form.formId === filledDocument.id
|
|
4418
|
+
);
|
|
4419
|
+
}
|
|
4420
|
+
if (existingFormIndex >= 0 && appointment.linkedForms) {
|
|
4421
|
+
updateData = {
|
|
4422
|
+
linkedForms: admin11.firestore.FieldValue.arrayRemove(
|
|
4423
|
+
appointment.linkedForms[existingFormIndex]
|
|
4424
|
+
),
|
|
4425
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4426
|
+
};
|
|
4427
|
+
await appointmentRef.update(updateData);
|
|
4428
|
+
updateData = {
|
|
4429
|
+
linkedForms: admin11.firestore.FieldValue.arrayUnion(linkedFormInfo),
|
|
4430
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4431
|
+
};
|
|
4432
|
+
} else {
|
|
4433
|
+
updateData = {
|
|
4434
|
+
linkedForms: appointment.linkedForms ? admin11.firestore.FieldValue.arrayUnion(linkedFormInfo) : [linkedFormInfo],
|
|
4435
|
+
// If linkedForms doesn't exist, create a new array
|
|
4436
|
+
linkedFormIds: appointment.linkedFormIds ? admin11.firestore.FieldValue.arrayUnion(filledDocument.id) : [filledDocument.id],
|
|
4437
|
+
// If linkedFormIds doesn't exist, create a new array
|
|
4438
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4439
|
+
};
|
|
4440
|
+
}
|
|
4441
|
+
if (filledDocument.isUserForm && filledDocument.isRequired && (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */)) {
|
|
4442
|
+
if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.includes(filledDocument.id)) {
|
|
4443
|
+
updateData.pendingUserFormsIds = admin11.firestore.FieldValue.arrayRemove(filledDocument.id);
|
|
4444
|
+
Logger.info(
|
|
4445
|
+
`[FilledFormsAggregationService] Removing form ${filledDocument.id} from pendingUserFormsIds`
|
|
4446
|
+
);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
await appointmentRef.update(updateData);
|
|
4450
|
+
Logger.info(
|
|
4451
|
+
`[FilledFormsAggregationService] Successfully updated appointment ${filledDocument.appointmentId} with form info for ${filledDocument.id}`
|
|
4452
|
+
);
|
|
4453
|
+
} catch (error) {
|
|
4454
|
+
Logger.error(
|
|
4455
|
+
`[FilledFormsAggregationService] Error updating appointment for filled form ${filledDocument.id}:`,
|
|
4456
|
+
error
|
|
4457
|
+
);
|
|
4458
|
+
throw error;
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
/**
|
|
4462
|
+
* Handles side effects when a filled form is deleted.
|
|
4463
|
+
* This function would typically be called by a Firestore onDelete trigger.
|
|
4464
|
+
* @param filledDocument The filled document that was deleted.
|
|
4465
|
+
* @returns {Promise<void>}
|
|
4466
|
+
*/
|
|
4467
|
+
async handleFilledFormDelete(filledDocument) {
|
|
4468
|
+
Logger.info(
|
|
4469
|
+
`[FilledFormsAggregationService] Handling DELETE for filled form: ${filledDocument.id}, appointment: ${filledDocument.appointmentId}`
|
|
4470
|
+
);
|
|
4471
|
+
try {
|
|
4472
|
+
const appointmentRef = this.db.collection(APPOINTMENTS_COLLECTION).doc(filledDocument.appointmentId);
|
|
4473
|
+
const appointmentDoc = await appointmentRef.get();
|
|
4474
|
+
if (!appointmentDoc.exists) {
|
|
4475
|
+
Logger.error(
|
|
4476
|
+
`[FilledFormsAggregationService] Appointment ${filledDocument.appointmentId} not found.`
|
|
4477
|
+
);
|
|
4478
|
+
return;
|
|
4479
|
+
}
|
|
4480
|
+
const appointment = appointmentDoc.data();
|
|
4481
|
+
let updateData = {};
|
|
4482
|
+
if (appointment.linkedForms && appointment.linkedForms.length > 0) {
|
|
4483
|
+
const formToRemove = appointment.linkedForms.find(
|
|
4484
|
+
(form) => form.formId === filledDocument.id
|
|
4485
|
+
);
|
|
4486
|
+
if (formToRemove) {
|
|
4487
|
+
updateData.linkedForms = admin11.firestore.FieldValue.arrayRemove(formToRemove);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
if (appointment.linkedFormIds && appointment.linkedFormIds.includes(filledDocument.id)) {
|
|
4491
|
+
updateData.linkedFormIds = admin11.firestore.FieldValue.arrayRemove(
|
|
4492
|
+
filledDocument.id
|
|
4493
|
+
);
|
|
4494
|
+
}
|
|
4495
|
+
if (filledDocument.isUserForm && filledDocument.isRequired) {
|
|
4496
|
+
if (filledDocument.status !== "completed" /* COMPLETED */ && filledDocument.status !== "signed" /* SIGNED */) {
|
|
4497
|
+
if (!appointment.pendingUserFormsIds || !appointment.pendingUserFormsIds.includes(filledDocument.id)) {
|
|
4498
|
+
updateData.pendingUserFormsIds = appointment.pendingUserFormsIds ? admin11.firestore.FieldValue.arrayUnion(filledDocument.id) : [filledDocument.id];
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
if (Object.keys(updateData).length > 0) {
|
|
4503
|
+
updateData.updatedAt = admin11.firestore.FieldValue.serverTimestamp();
|
|
4504
|
+
await appointmentRef.update(updateData);
|
|
4505
|
+
Logger.info(
|
|
4506
|
+
`[FilledFormsAggregationService] Successfully updated appointment ${filledDocument.appointmentId} after form deletion`
|
|
4507
|
+
);
|
|
4508
|
+
} else {
|
|
4509
|
+
Logger.info(
|
|
4510
|
+
`[FilledFormsAggregationService] No updates needed for appointment ${filledDocument.appointmentId}`
|
|
4511
|
+
);
|
|
4512
|
+
}
|
|
4513
|
+
} catch (error) {
|
|
4514
|
+
Logger.error(
|
|
4515
|
+
`[FilledFormsAggregationService] Error updating appointment after form deletion for ${filledDocument.id}:`,
|
|
4516
|
+
error
|
|
4517
|
+
);
|
|
4518
|
+
throw error;
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
/**
|
|
4522
|
+
* Updates the appointment's pendingUserFormsIds for a new required user form.
|
|
4523
|
+
* This should be called when a required user form is first created.
|
|
4524
|
+
* @param filledDocument The newly created filled document.
|
|
4525
|
+
* @returns {Promise<void>}
|
|
4526
|
+
*/
|
|
4527
|
+
async handleRequiredUserFormCreate(filledDocument) {
|
|
4528
|
+
if (!filledDocument.isUserForm || !filledDocument.isRequired) {
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
Logger.info(
|
|
4532
|
+
`[FilledFormsAggregationService] Handling new required user form creation: ${filledDocument.id}`
|
|
4533
|
+
);
|
|
4534
|
+
try {
|
|
4535
|
+
const appointmentRef = this.db.collection(APPOINTMENTS_COLLECTION).doc(filledDocument.appointmentId);
|
|
4536
|
+
if (filledDocument.status === "completed" /* COMPLETED */ || filledDocument.status === "signed" /* SIGNED */) {
|
|
4537
|
+
Logger.info(
|
|
4538
|
+
`[FilledFormsAggregationService] Form ${filledDocument.id} is already completed/signed, not adding to pendingUserFormsIds`
|
|
4539
|
+
);
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
await appointmentRef.update({
|
|
4543
|
+
pendingUserFormsIds: admin11.firestore.FieldValue.arrayUnion(
|
|
4544
|
+
filledDocument.id
|
|
4545
|
+
),
|
|
4546
|
+
updatedAt: admin11.firestore.FieldValue.serverTimestamp()
|
|
4547
|
+
});
|
|
4548
|
+
Logger.info(
|
|
4549
|
+
`[FilledFormsAggregationService] Successfully added form ${filledDocument.id} to pendingUserFormsIds for appointment ${filledDocument.appointmentId}`
|
|
4550
|
+
);
|
|
4551
|
+
} catch (error) {
|
|
4552
|
+
Logger.error(
|
|
4553
|
+
`[FilledFormsAggregationService] Error handling required user form creation for ${filledDocument.id}:`,
|
|
4554
|
+
error
|
|
4555
|
+
);
|
|
4556
|
+
throw error;
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
};
|
|
4560
|
+
|
|
4561
|
+
// src/admin/aggregation/reviews/reviews.aggregation.service.ts
|
|
4562
|
+
import * as admin12 from "firebase-admin";
|
|
4563
|
+
|
|
4564
|
+
// src/types/reviews/index.ts
|
|
4565
|
+
var REVIEWS_COLLECTION = "reviews";
|
|
4566
|
+
|
|
4567
|
+
// src/admin/aggregation/reviews/reviews.aggregation.service.ts
|
|
4568
|
+
var ReviewsAggregationService = class {
|
|
4569
|
+
/**
|
|
4570
|
+
* Constructor for ReviewsAggregationService.
|
|
4571
|
+
* @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
|
|
4572
|
+
*/
|
|
4573
|
+
constructor(firestore15) {
|
|
4574
|
+
this.db = firestore15 || admin12.firestore();
|
|
4575
|
+
}
|
|
4576
|
+
/**
|
|
4577
|
+
* Process a newly created review and update all related entities
|
|
4578
|
+
* @param review The newly created review
|
|
4579
|
+
* @returns Promise resolving when all updates are complete
|
|
4580
|
+
*/
|
|
4581
|
+
async processNewReview(review) {
|
|
4582
|
+
console.log(
|
|
4583
|
+
`[ReviewsAggregationService] Processing new review: ${review.id}`
|
|
4584
|
+
);
|
|
4585
|
+
const updatePromises = [];
|
|
4586
|
+
if (review.clinicReview) {
|
|
4587
|
+
updatePromises.push(
|
|
4588
|
+
this.updateClinicReviewInfo(review.clinicReview.clinicId)
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
if (review.practitionerReview) {
|
|
4592
|
+
updatePromises.push(
|
|
4593
|
+
this.updatePractitionerReviewInfo(
|
|
4594
|
+
review.practitionerReview.practitionerId
|
|
4595
|
+
)
|
|
4596
|
+
);
|
|
4597
|
+
}
|
|
4598
|
+
if (review.procedureReview) {
|
|
4599
|
+
updatePromises.push(
|
|
4600
|
+
this.updateProcedureReviewInfo(review.procedureReview.procedureId)
|
|
4601
|
+
);
|
|
4602
|
+
}
|
|
4603
|
+
await Promise.all(updatePromises);
|
|
4604
|
+
console.log(
|
|
4605
|
+
`[ReviewsAggregationService] Successfully processed review: ${review.id}`
|
|
4606
|
+
);
|
|
4607
|
+
}
|
|
4608
|
+
/**
|
|
4609
|
+
* Process a deleted review and update all related entities
|
|
4610
|
+
* @param review The deleted review
|
|
4611
|
+
* @returns Promise resolving when all updates are complete
|
|
4612
|
+
*/
|
|
4613
|
+
async processDeletedReview(review) {
|
|
4614
|
+
console.log(
|
|
4615
|
+
`[ReviewsAggregationService] Processing deleted review: ${review.id}`
|
|
4616
|
+
);
|
|
4617
|
+
const updatePromises = [];
|
|
4618
|
+
if (review.clinicReview) {
|
|
4619
|
+
updatePromises.push(
|
|
4620
|
+
this.updateClinicReviewInfo(
|
|
4621
|
+
review.clinicReview.clinicId,
|
|
4622
|
+
review.clinicReview,
|
|
4623
|
+
true
|
|
4624
|
+
)
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
if (review.practitionerReview) {
|
|
4628
|
+
updatePromises.push(
|
|
4629
|
+
this.updatePractitionerReviewInfo(
|
|
4630
|
+
review.practitionerReview.practitionerId,
|
|
4631
|
+
review.practitionerReview,
|
|
4632
|
+
true
|
|
4633
|
+
)
|
|
4634
|
+
);
|
|
4635
|
+
}
|
|
4636
|
+
if (review.procedureReview) {
|
|
4637
|
+
updatePromises.push(
|
|
4638
|
+
this.updateProcedureReviewInfo(
|
|
4639
|
+
review.procedureReview.procedureId,
|
|
4640
|
+
review.procedureReview,
|
|
4641
|
+
true
|
|
4642
|
+
)
|
|
4643
|
+
);
|
|
4644
|
+
}
|
|
4645
|
+
await Promise.all(updatePromises);
|
|
4646
|
+
console.log(
|
|
4647
|
+
`[ReviewsAggregationService] Successfully processed deleted review: ${review.id}`
|
|
4648
|
+
);
|
|
4649
|
+
}
|
|
4650
|
+
/**
|
|
4651
|
+
* Updates the review info for a clinic
|
|
4652
|
+
* @param clinicId The ID of the clinic to update
|
|
4653
|
+
* @param removedReview Optional review being removed
|
|
4654
|
+
* @param isRemoval Whether this update is for a review removal
|
|
4655
|
+
* @returns The updated clinic review info
|
|
4656
|
+
*/
|
|
4657
|
+
async updateClinicReviewInfo(clinicId, removedReview, isRemoval = false) {
|
|
4658
|
+
console.log(
|
|
4659
|
+
`[ReviewsAggregationService] Updating review info for clinic: ${clinicId}`
|
|
4660
|
+
);
|
|
4661
|
+
const clinicDoc = await this.db.collection(CLINICS_COLLECTION).doc(clinicId).get();
|
|
4662
|
+
if (!clinicDoc.exists) {
|
|
4663
|
+
console.error(
|
|
4664
|
+
`[ReviewsAggregationService] Clinic with ID ${clinicId} not found`
|
|
4665
|
+
);
|
|
4666
|
+
throw new Error(`Clinic with ID ${clinicId} not found`);
|
|
4667
|
+
}
|
|
4668
|
+
const clinicData = clinicDoc.data();
|
|
4669
|
+
const currentReviewInfo = (clinicData == null ? void 0 : clinicData.reviewInfo) || {
|
|
4670
|
+
totalReviews: 0,
|
|
4671
|
+
averageRating: 0,
|
|
4672
|
+
cleanliness: 0,
|
|
4673
|
+
facilities: 0,
|
|
4674
|
+
staffFriendliness: 0,
|
|
4675
|
+
waitingTime: 0,
|
|
4676
|
+
accessibility: 0,
|
|
4677
|
+
recommendationPercentage: 0
|
|
4678
|
+
};
|
|
4679
|
+
const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("clinicReview.clinicId", "==", clinicId).get();
|
|
4680
|
+
if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
|
|
4681
|
+
const updatedReviewInfo2 = {
|
|
4682
|
+
totalReviews: 0,
|
|
4683
|
+
averageRating: 0,
|
|
4684
|
+
cleanliness: 0,
|
|
4685
|
+
facilities: 0,
|
|
4686
|
+
staffFriendliness: 0,
|
|
4687
|
+
waitingTime: 0,
|
|
4688
|
+
accessibility: 0,
|
|
4689
|
+
recommendationPercentage: 0
|
|
4690
|
+
};
|
|
4691
|
+
await this.db.collection(CLINICS_COLLECTION).doc(clinicId).update({
|
|
4692
|
+
reviewInfo: updatedReviewInfo2,
|
|
4693
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4694
|
+
});
|
|
4695
|
+
console.log(
|
|
4696
|
+
`[ReviewsAggregationService] Reset review info for clinic: ${clinicId}`
|
|
4697
|
+
);
|
|
4698
|
+
return updatedReviewInfo2;
|
|
4699
|
+
}
|
|
4700
|
+
const reviews = reviewsQuery.docs.map((doc) => doc.data());
|
|
4701
|
+
const clinicReviews = reviews.map((review) => review.clinicReview).filter((review) => review !== void 0);
|
|
4702
|
+
let totalRating = 0;
|
|
4703
|
+
let totalCleanliness = 0;
|
|
4704
|
+
let totalFacilities = 0;
|
|
4705
|
+
let totalStaffFriendliness = 0;
|
|
4706
|
+
let totalWaitingTime = 0;
|
|
4707
|
+
let totalAccessibility = 0;
|
|
4708
|
+
let totalRecommendations = 0;
|
|
4709
|
+
clinicReviews.forEach((review) => {
|
|
4710
|
+
totalRating += review.overallRating;
|
|
4711
|
+
totalCleanliness += review.cleanliness;
|
|
4712
|
+
totalFacilities += review.facilities;
|
|
4713
|
+
totalStaffFriendliness += review.staffFriendliness;
|
|
4714
|
+
totalWaitingTime += review.waitingTime;
|
|
4715
|
+
totalAccessibility += review.accessibility;
|
|
4716
|
+
if (review.wouldRecommend) totalRecommendations++;
|
|
4717
|
+
});
|
|
4718
|
+
const count = clinicReviews.length;
|
|
4719
|
+
const roundToOneDecimal = (value) => Math.round(value / count * 10) / 10;
|
|
4720
|
+
const updatedReviewInfo = {
|
|
4721
|
+
totalReviews: count,
|
|
4722
|
+
averageRating: roundToOneDecimal(totalRating),
|
|
4723
|
+
cleanliness: roundToOneDecimal(totalCleanliness),
|
|
4724
|
+
facilities: roundToOneDecimal(totalFacilities),
|
|
4725
|
+
staffFriendliness: roundToOneDecimal(totalStaffFriendliness),
|
|
4726
|
+
waitingTime: roundToOneDecimal(totalWaitingTime),
|
|
4727
|
+
accessibility: roundToOneDecimal(totalAccessibility),
|
|
4728
|
+
recommendationPercentage: Math.round(totalRecommendations / count * 1e3) / 10
|
|
4729
|
+
};
|
|
4730
|
+
await this.db.collection(CLINICS_COLLECTION).doc(clinicId).update({
|
|
4731
|
+
reviewInfo: updatedReviewInfo,
|
|
4732
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4733
|
+
});
|
|
4734
|
+
console.log(
|
|
4735
|
+
`[ReviewsAggregationService] Updated review info for clinic: ${clinicId}`
|
|
4736
|
+
);
|
|
4737
|
+
return updatedReviewInfo;
|
|
4738
|
+
}
|
|
4739
|
+
/**
|
|
4740
|
+
* Updates the review info for a practitioner
|
|
4741
|
+
* @param practitionerId The ID of the practitioner to update
|
|
4742
|
+
* @param removedReview Optional review being removed
|
|
4743
|
+
* @param isRemoval Whether this update is for a review removal
|
|
4744
|
+
* @returns The updated practitioner review info
|
|
4745
|
+
*/
|
|
4746
|
+
async updatePractitionerReviewInfo(practitionerId, removedReview, isRemoval = false) {
|
|
4747
|
+
console.log(
|
|
4748
|
+
`[ReviewsAggregationService] Updating review info for practitioner: ${practitionerId}`
|
|
4749
|
+
);
|
|
4750
|
+
const practitionerDoc = await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).get();
|
|
4751
|
+
if (!practitionerDoc.exists) {
|
|
4752
|
+
console.error(
|
|
4753
|
+
`[ReviewsAggregationService] Practitioner with ID ${practitionerId} not found`
|
|
4754
|
+
);
|
|
4755
|
+
throw new Error(`Practitioner with ID ${practitionerId} not found`);
|
|
4756
|
+
}
|
|
4757
|
+
const practitionerData = practitionerDoc.data();
|
|
4758
|
+
const currentReviewInfo = (practitionerData == null ? void 0 : practitionerData.reviewInfo) || {
|
|
4759
|
+
totalReviews: 0,
|
|
4760
|
+
averageRating: 0,
|
|
4761
|
+
knowledgeAndExpertise: 0,
|
|
4762
|
+
communicationSkills: 0,
|
|
4763
|
+
bedSideManner: 0,
|
|
4764
|
+
thoroughness: 0,
|
|
4765
|
+
trustworthiness: 0,
|
|
4766
|
+
recommendationPercentage: 0
|
|
4767
|
+
};
|
|
4768
|
+
const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("practitionerReview.practitionerId", "==", practitionerId).get();
|
|
4769
|
+
if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
|
|
4770
|
+
const updatedReviewInfo2 = {
|
|
4771
|
+
totalReviews: 0,
|
|
4772
|
+
averageRating: 0,
|
|
4773
|
+
knowledgeAndExpertise: 0,
|
|
4774
|
+
communicationSkills: 0,
|
|
4775
|
+
bedSideManner: 0,
|
|
4776
|
+
thoroughness: 0,
|
|
4777
|
+
trustworthiness: 0,
|
|
4778
|
+
recommendationPercentage: 0
|
|
4779
|
+
};
|
|
4780
|
+
await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).update({
|
|
4781
|
+
reviewInfo: updatedReviewInfo2,
|
|
4782
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4783
|
+
});
|
|
4784
|
+
await this.updateDoctorInfoInProcedures(practitionerId, 0);
|
|
4785
|
+
console.log(
|
|
4786
|
+
`[ReviewsAggregationService] Reset review info for practitioner: ${practitionerId}`
|
|
4787
|
+
);
|
|
4788
|
+
return updatedReviewInfo2;
|
|
4789
|
+
}
|
|
4790
|
+
const reviews = reviewsQuery.docs.map((doc) => doc.data());
|
|
4791
|
+
const practitionerReviews = reviews.map((review) => review.practitionerReview).filter((review) => review !== void 0);
|
|
4792
|
+
let totalRating = 0;
|
|
4793
|
+
let totalKnowledgeAndExpertise = 0;
|
|
4794
|
+
let totalCommunicationSkills = 0;
|
|
4795
|
+
let totalBedSideManner = 0;
|
|
4796
|
+
let totalThoroughness = 0;
|
|
4797
|
+
let totalTrustworthiness = 0;
|
|
4798
|
+
let totalRecommendations = 0;
|
|
4799
|
+
practitionerReviews.forEach((review) => {
|
|
4800
|
+
totalRating += review.overallRating;
|
|
4801
|
+
totalKnowledgeAndExpertise += review.knowledgeAndExpertise;
|
|
4802
|
+
totalCommunicationSkills += review.communicationSkills;
|
|
4803
|
+
totalBedSideManner += review.bedSideManner;
|
|
4804
|
+
totalThoroughness += review.thoroughness;
|
|
4805
|
+
totalTrustworthiness += review.trustworthiness;
|
|
4806
|
+
if (review.wouldRecommend) totalRecommendations++;
|
|
4807
|
+
});
|
|
4808
|
+
const count = practitionerReviews.length;
|
|
4809
|
+
const roundToOneDecimal = (value) => Math.round(value / count * 10) / 10;
|
|
4810
|
+
const updatedReviewInfo = {
|
|
4811
|
+
totalReviews: count,
|
|
4812
|
+
averageRating: roundToOneDecimal(totalRating),
|
|
4813
|
+
knowledgeAndExpertise: roundToOneDecimal(totalKnowledgeAndExpertise),
|
|
4814
|
+
communicationSkills: roundToOneDecimal(totalCommunicationSkills),
|
|
4815
|
+
bedSideManner: roundToOneDecimal(totalBedSideManner),
|
|
4816
|
+
thoroughness: roundToOneDecimal(totalThoroughness),
|
|
4817
|
+
trustworthiness: roundToOneDecimal(totalTrustworthiness),
|
|
4818
|
+
recommendationPercentage: Math.round(totalRecommendations / count * 1e3) / 10
|
|
4819
|
+
};
|
|
4820
|
+
await this.db.collection(PRACTITIONERS_COLLECTION).doc(practitionerId).update({
|
|
4821
|
+
reviewInfo: updatedReviewInfo,
|
|
4822
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4823
|
+
});
|
|
4824
|
+
await this.updateDoctorInfoInProcedures(
|
|
4825
|
+
practitionerId,
|
|
4826
|
+
updatedReviewInfo.averageRating
|
|
4827
|
+
);
|
|
4828
|
+
console.log(
|
|
4829
|
+
`[ReviewsAggregationService] Updated review info for practitioner: ${practitionerId}`
|
|
4830
|
+
);
|
|
4831
|
+
return updatedReviewInfo;
|
|
4832
|
+
}
|
|
4833
|
+
/**
|
|
4834
|
+
* Updates the review info for a procedure
|
|
4835
|
+
* @param procedureId The ID of the procedure to update
|
|
4836
|
+
* @param removedReview Optional review being removed
|
|
4837
|
+
* @param isRemoval Whether this update is for a review removal
|
|
4838
|
+
* @returns The updated procedure review info
|
|
4839
|
+
*/
|
|
4840
|
+
async updateProcedureReviewInfo(procedureId, removedReview, isRemoval = false) {
|
|
4841
|
+
console.log(
|
|
4842
|
+
`[ReviewsAggregationService] Updating review info for procedure: ${procedureId}`
|
|
4843
|
+
);
|
|
4844
|
+
const procedureDoc = await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).get();
|
|
4845
|
+
if (!procedureDoc.exists) {
|
|
4846
|
+
console.error(
|
|
4847
|
+
`[ReviewsAggregationService] Procedure with ID ${procedureId} not found`
|
|
4848
|
+
);
|
|
4849
|
+
throw new Error(`Procedure with ID ${procedureId} not found`);
|
|
4850
|
+
}
|
|
4851
|
+
const procedureData = procedureDoc.data();
|
|
4852
|
+
const currentReviewInfo = (procedureData == null ? void 0 : procedureData.reviewInfo) || {
|
|
4853
|
+
totalReviews: 0,
|
|
4854
|
+
averageRating: 0,
|
|
4855
|
+
effectivenessOfTreatment: 0,
|
|
4856
|
+
outcomeExplanation: 0,
|
|
4857
|
+
painManagement: 0,
|
|
4858
|
+
followUpCare: 0,
|
|
4859
|
+
valueForMoney: 0,
|
|
4860
|
+
recommendationPercentage: 0
|
|
4861
|
+
};
|
|
4862
|
+
const reviewsQuery = await this.db.collection(REVIEWS_COLLECTION).where("procedureReview.procedureId", "==", procedureId).get();
|
|
4863
|
+
if (isRemoval && reviewsQuery.size <= 1 || reviewsQuery.empty) {
|
|
4864
|
+
const updatedReviewInfo2 = {
|
|
4865
|
+
totalReviews: 0,
|
|
4866
|
+
averageRating: 0,
|
|
4867
|
+
effectivenessOfTreatment: 0,
|
|
4868
|
+
outcomeExplanation: 0,
|
|
4869
|
+
painManagement: 0,
|
|
4870
|
+
followUpCare: 0,
|
|
4871
|
+
valueForMoney: 0,
|
|
4872
|
+
recommendationPercentage: 0
|
|
4873
|
+
};
|
|
4874
|
+
await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).update({
|
|
4875
|
+
reviewInfo: updatedReviewInfo2,
|
|
4876
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4877
|
+
});
|
|
4878
|
+
console.log(
|
|
4879
|
+
`[ReviewsAggregationService] Reset review info for procedure: ${procedureId}`
|
|
4880
|
+
);
|
|
4881
|
+
return updatedReviewInfo2;
|
|
4882
|
+
}
|
|
4883
|
+
const reviews = reviewsQuery.docs.map((doc) => doc.data());
|
|
4884
|
+
const procedureReviews = reviews.map((review) => review.procedureReview).filter((review) => review !== void 0);
|
|
4885
|
+
let totalRating = 0;
|
|
4886
|
+
let totalEffectivenessOfTreatment = 0;
|
|
4887
|
+
let totalOutcomeExplanation = 0;
|
|
4888
|
+
let totalPainManagement = 0;
|
|
4889
|
+
let totalFollowUpCare = 0;
|
|
4890
|
+
let totalValueForMoney = 0;
|
|
4891
|
+
let totalRecommendations = 0;
|
|
4892
|
+
procedureReviews.forEach((review) => {
|
|
4893
|
+
totalRating += review.overallRating;
|
|
4894
|
+
totalEffectivenessOfTreatment += review.effectivenessOfTreatment;
|
|
4895
|
+
totalOutcomeExplanation += review.outcomeExplanation;
|
|
4896
|
+
totalPainManagement += review.painManagement;
|
|
4897
|
+
totalFollowUpCare += review.followUpCare;
|
|
4898
|
+
totalValueForMoney += review.valueForMoney;
|
|
4899
|
+
if (review.wouldRecommend) totalRecommendations++;
|
|
4900
|
+
});
|
|
4901
|
+
const count = procedureReviews.length;
|
|
4902
|
+
const roundToOneDecimal = (value) => Math.round(value / count * 10) / 10;
|
|
4903
|
+
const updatedReviewInfo = {
|
|
4904
|
+
totalReviews: count,
|
|
4905
|
+
averageRating: roundToOneDecimal(totalRating),
|
|
4906
|
+
effectivenessOfTreatment: roundToOneDecimal(
|
|
4907
|
+
totalEffectivenessOfTreatment
|
|
4908
|
+
),
|
|
4909
|
+
outcomeExplanation: roundToOneDecimal(totalOutcomeExplanation),
|
|
4910
|
+
painManagement: roundToOneDecimal(totalPainManagement),
|
|
4911
|
+
followUpCare: roundToOneDecimal(totalFollowUpCare),
|
|
4912
|
+
valueForMoney: roundToOneDecimal(totalValueForMoney),
|
|
4913
|
+
recommendationPercentage: Math.round(totalRecommendations / count * 1e3) / 10
|
|
4914
|
+
};
|
|
4915
|
+
await this.db.collection(PROCEDURES_COLLECTION).doc(procedureId).update({
|
|
4916
|
+
reviewInfo: updatedReviewInfo,
|
|
4917
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4918
|
+
});
|
|
4919
|
+
console.log(
|
|
4920
|
+
`[ReviewsAggregationService] Updated review info for procedure: ${procedureId}`
|
|
4921
|
+
);
|
|
4922
|
+
return updatedReviewInfo;
|
|
4923
|
+
}
|
|
4924
|
+
/**
|
|
4925
|
+
* Updates doctorInfo rating in all procedures for a practitioner
|
|
4926
|
+
* @param practitionerId The ID of the practitioner
|
|
4927
|
+
* @param rating The new rating to set
|
|
4928
|
+
*/
|
|
4929
|
+
async updateDoctorInfoInProcedures(practitionerId, rating) {
|
|
4930
|
+
console.log(
|
|
4931
|
+
`[ReviewsAggregationService] Updating doctor info in procedures for practitioner: ${practitionerId}`
|
|
4932
|
+
);
|
|
4933
|
+
const proceduresQuery = await this.db.collection(PROCEDURES_COLLECTION).where("practitionerId", "==", practitionerId).get();
|
|
4934
|
+
if (proceduresQuery.empty) {
|
|
4935
|
+
console.log(
|
|
4936
|
+
`[ReviewsAggregationService] No procedures found for practitioner: ${practitionerId}`
|
|
4937
|
+
);
|
|
4938
|
+
return;
|
|
4939
|
+
}
|
|
4940
|
+
const batch = this.db.batch();
|
|
4941
|
+
proceduresQuery.docs.forEach((docSnapshot) => {
|
|
4942
|
+
const procedureRef = this.db.collection(PROCEDURES_COLLECTION).doc(docSnapshot.id);
|
|
4943
|
+
batch.update(procedureRef, {
|
|
4944
|
+
"doctorInfo.rating": rating,
|
|
4945
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4946
|
+
});
|
|
4947
|
+
});
|
|
4948
|
+
await batch.commit();
|
|
4949
|
+
console.log(
|
|
4950
|
+
`[ReviewsAggregationService] Updated doctor info in ${proceduresQuery.size} procedures for practitioner: ${practitionerId}`
|
|
4951
|
+
);
|
|
4952
|
+
}
|
|
4953
|
+
/**
|
|
4954
|
+
* Verifies a review as checked by admin/staff
|
|
4955
|
+
* @param reviewId The ID of the review to verify
|
|
4956
|
+
*/
|
|
4957
|
+
async verifyReview(reviewId) {
|
|
4958
|
+
console.log(`[ReviewsAggregationService] Verifying review: ${reviewId}`);
|
|
4959
|
+
const reviewDoc = await this.db.collection(REVIEWS_COLLECTION).doc(reviewId).get();
|
|
4960
|
+
if (!reviewDoc.exists) {
|
|
4961
|
+
console.error(
|
|
4962
|
+
`[ReviewsAggregationService] Review with ID ${reviewId} not found`
|
|
4963
|
+
);
|
|
4964
|
+
throw new Error(`Review with ID ${reviewId} not found`);
|
|
4965
|
+
}
|
|
4966
|
+
const review = reviewDoc.data();
|
|
4967
|
+
const batch = this.db.batch();
|
|
4968
|
+
const reviewRef = this.db.collection(REVIEWS_COLLECTION).doc(reviewId);
|
|
4969
|
+
if (review.clinicReview) {
|
|
4970
|
+
review.clinicReview.isVerified = true;
|
|
4971
|
+
}
|
|
4972
|
+
if (review.practitionerReview) {
|
|
4973
|
+
review.practitionerReview.isVerified = true;
|
|
4974
|
+
}
|
|
4975
|
+
if (review.procedureReview) {
|
|
4976
|
+
review.procedureReview.isVerified = true;
|
|
4977
|
+
}
|
|
4978
|
+
batch.update(reviewRef, {
|
|
4979
|
+
clinicReview: review.clinicReview,
|
|
4980
|
+
practitionerReview: review.practitionerReview,
|
|
4981
|
+
procedureReview: review.procedureReview,
|
|
4982
|
+
updatedAt: admin12.firestore.FieldValue.serverTimestamp()
|
|
4983
|
+
});
|
|
4984
|
+
await batch.commit();
|
|
4985
|
+
console.log(
|
|
4986
|
+
`[ReviewsAggregationService] Successfully verified review: ${reviewId}`
|
|
4987
|
+
);
|
|
4988
|
+
}
|
|
4989
|
+
/**
|
|
4990
|
+
* Calculate the average of all reviews for an entity
|
|
4991
|
+
* @param entityId The entity ID
|
|
4992
|
+
* @param entityType The type of entity ('clinic', 'practitioner', or 'procedure')
|
|
4993
|
+
* @returns Promise that resolves to the calculated review info
|
|
4994
|
+
*/
|
|
4995
|
+
async calculateEntityReviewInfo(entityId, entityType) {
|
|
4996
|
+
console.log(
|
|
4997
|
+
`[ReviewsAggregationService] Calculating review info for ${entityType}: ${entityId}`
|
|
4998
|
+
);
|
|
4999
|
+
switch (entityType) {
|
|
5000
|
+
case "clinic":
|
|
5001
|
+
return this.updateClinicReviewInfo(entityId);
|
|
5002
|
+
case "practitioner":
|
|
5003
|
+
return this.updatePractitionerReviewInfo(entityId);
|
|
5004
|
+
case "procedure":
|
|
5005
|
+
return this.updateProcedureReviewInfo(entityId);
|
|
5006
|
+
default:
|
|
5007
|
+
throw new Error(`Invalid entity type: ${entityType}`);
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
};
|
|
5011
|
+
|
|
4360
5012
|
// src/admin/mailing/practitionerInvite/templates/invitation.template.ts
|
|
4361
5013
|
var practitionerInvitationTemplate = `
|
|
4362
5014
|
<!DOCTYPE html>
|
|
@@ -4464,8 +5116,8 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
4464
5116
|
* @param firestore Firestore instance provided by the caller
|
|
4465
5117
|
* @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
|
|
4466
5118
|
*/
|
|
4467
|
-
constructor(
|
|
4468
|
-
super(
|
|
5119
|
+
constructor(firestore15, mailgunClient) {
|
|
5120
|
+
super(firestore15, mailgunClient);
|
|
4469
5121
|
this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
|
|
4470
5122
|
this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
|
|
4471
5123
|
this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
|
|
@@ -4703,7 +5355,7 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
|
|
|
4703
5355
|
};
|
|
4704
5356
|
|
|
4705
5357
|
// src/admin/booking/booking.admin.ts
|
|
4706
|
-
import * as
|
|
5358
|
+
import * as admin14 from "firebase-admin";
|
|
4707
5359
|
|
|
4708
5360
|
// src/admin/booking/booking.calculator.ts
|
|
4709
5361
|
import { Timestamp } from "firebase/firestore";
|
|
@@ -5137,10 +5789,10 @@ var BookingAvailabilityCalculator = class {
|
|
|
5137
5789
|
BookingAvailabilityCalculator.DEFAULT_INTERVAL_MINUTES = 15;
|
|
5138
5790
|
|
|
5139
5791
|
// src/admin/documentation-templates/document-manager.admin.ts
|
|
5140
|
-
import * as
|
|
5792
|
+
import * as admin13 from "firebase-admin";
|
|
5141
5793
|
var DocumentManagerAdminService = class {
|
|
5142
|
-
constructor(
|
|
5143
|
-
this.db =
|
|
5794
|
+
constructor(firestore15) {
|
|
5795
|
+
this.db = firestore15;
|
|
5144
5796
|
}
|
|
5145
5797
|
/**
|
|
5146
5798
|
* Adds operations to a Firestore batch to initialize all linked forms for a new appointment
|
|
@@ -5243,7 +5895,7 @@ var DocumentManagerAdminService = class {
|
|
|
5243
5895
|
};
|
|
5244
5896
|
}
|
|
5245
5897
|
const templateIds = technologyTemplates.map((t) => t.templateId);
|
|
5246
|
-
const templatesSnapshot = await this.db.collection(DOCUMENTATION_TEMPLATES_COLLECTION).where(
|
|
5898
|
+
const templatesSnapshot = await this.db.collection(DOCUMENTATION_TEMPLATES_COLLECTION).where(admin13.firestore.FieldPath.documentId(), "in", templateIds).get();
|
|
5247
5899
|
const templatesMap = /* @__PURE__ */ new Map();
|
|
5248
5900
|
templatesSnapshot.forEach((doc) => {
|
|
5249
5901
|
templatesMap.set(doc.id, doc.data());
|
|
@@ -5309,8 +5961,8 @@ var BookingAdmin = class {
|
|
|
5309
5961
|
* Creates a new BookingAdmin instance
|
|
5310
5962
|
* @param firestore - Firestore instance provided by the caller
|
|
5311
5963
|
*/
|
|
5312
|
-
constructor(
|
|
5313
|
-
this.db =
|
|
5964
|
+
constructor(firestore15) {
|
|
5965
|
+
this.db = firestore15 || admin14.firestore();
|
|
5314
5966
|
this.documentManagerAdmin = new DocumentManagerAdminService(this.db);
|
|
5315
5967
|
}
|
|
5316
5968
|
/**
|
|
@@ -5327,8 +5979,8 @@ var BookingAdmin = class {
|
|
|
5327
5979
|
console.log(
|
|
5328
5980
|
`[BookingAdmin] Getting available slots for clinic ${clinicId}, practitioner ${practitionerId}, procedure ${procedureId}`
|
|
5329
5981
|
);
|
|
5330
|
-
const start = timeframe.start instanceof Date ?
|
|
5331
|
-
const end = timeframe.end instanceof Date ?
|
|
5982
|
+
const start = timeframe.start instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.start) : timeframe.start;
|
|
5983
|
+
const end = timeframe.end instanceof Date ? admin14.firestore.Timestamp.fromDate(timeframe.end) : timeframe.end;
|
|
5332
5984
|
const clinicDoc = await this.db.collection("clinics").doc(clinicId).get();
|
|
5333
5985
|
if (!clinicDoc.exists) {
|
|
5334
5986
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
@@ -5367,7 +6019,7 @@ var BookingAdmin = class {
|
|
|
5367
6019
|
const result = BookingAvailabilityCalculator.calculateSlots(request);
|
|
5368
6020
|
return {
|
|
5369
6021
|
availableSlots: result.availableSlots.map((slot) => ({
|
|
5370
|
-
start:
|
|
6022
|
+
start: admin14.firestore.Timestamp.fromMillis(slot.start.toMillis())
|
|
5371
6023
|
}))
|
|
5372
6024
|
};
|
|
5373
6025
|
} catch (error) {
|
|
@@ -5469,8 +6121,8 @@ var BookingAdmin = class {
|
|
|
5469
6121
|
`[BookingAdmin] Orchestrating appointment creation for patient ${data.patientId} by user ${authenticatedUserId}`
|
|
5470
6122
|
);
|
|
5471
6123
|
const batch = this.db.batch();
|
|
5472
|
-
const adminTsNow =
|
|
5473
|
-
const serverTimestampValue =
|
|
6124
|
+
const adminTsNow = admin14.firestore.Timestamp.now();
|
|
6125
|
+
const serverTimestampValue = admin14.firestore.FieldValue.serverTimestamp();
|
|
5474
6126
|
try {
|
|
5475
6127
|
if (!data.patientId || !data.procedureId || !data.appointmentStartTime || !data.appointmentEndTime) {
|
|
5476
6128
|
return {
|
|
@@ -5567,7 +6219,7 @@ var BookingAdmin = class {
|
|
|
5567
6219
|
fullName: `${(patientSensitiveData == null ? void 0 : patientSensitiveData.firstName) || ""} ${(patientSensitiveData == null ? void 0 : patientSensitiveData.lastName) || ""}`.trim() || patientProfileData.displayName,
|
|
5568
6220
|
email: (patientSensitiveData == null ? void 0 : patientSensitiveData.email) || "",
|
|
5569
6221
|
phone: (patientSensitiveData == null ? void 0 : patientSensitiveData.phoneNumber) || patientProfileData.phoneNumber || null,
|
|
5570
|
-
dateOfBirth: (patientSensitiveData == null ? void 0 : patientSensitiveData.dateOfBirth) || patientProfileData.dateOfBirth ||
|
|
6222
|
+
dateOfBirth: (patientSensitiveData == null ? void 0 : patientSensitiveData.dateOfBirth) || patientProfileData.dateOfBirth || admin14.firestore.Timestamp.now(),
|
|
5571
6223
|
gender: (patientSensitiveData == null ? void 0 : patientSensitiveData.gender) || "other" /* OTHER */
|
|
5572
6224
|
};
|
|
5573
6225
|
const newAppointmentId = this.db.collection(APPOINTMENTS_COLLECTION).doc().id;
|
|
@@ -5817,6 +6469,7 @@ export {
|
|
|
5817
6469
|
CalendarAdminService,
|
|
5818
6470
|
ClinicAggregationService,
|
|
5819
6471
|
DocumentManagerAdminService,
|
|
6472
|
+
FilledFormsAggregationService,
|
|
5820
6473
|
Logger,
|
|
5821
6474
|
MediaType,
|
|
5822
6475
|
NOTIFICATIONS_COLLECTION,
|
|
@@ -5833,5 +6486,6 @@ export {
|
|
|
5833
6486
|
PractitionerInviteMailingService,
|
|
5834
6487
|
PractitionerTokenStatus,
|
|
5835
6488
|
ProcedureAggregationService,
|
|
6489
|
+
ReviewsAggregationService,
|
|
5836
6490
|
UserRole
|
|
5837
6491
|
};
|