@blackcode_sa/metaestetics-api 1.7.29 → 1.7.31

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.
@@ -2896,4 +2896,12 @@ declare class AppointmentMailingService extends BaseMailingService {
2896
2896
  sendReviewAddedEmail(data: ReviewAddedEmailData): Promise<any>;
2897
2897
  }
2898
2898
 
2899
- export { APPOINTMENTS_COLLECTION, type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentMediaItem, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateAppointmentData, type CreateAppointmentHttpData, type DoctorInfo, DocumentManagerAdminService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, type LinkedFormInfo, Logger, MediaType, NOTIFICATIONS_COLLECTION, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientReviewInfo, PaymentStatus, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, PractitionerInviteMailingService, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureExtendedInfo, type ProcedureSummaryInfo, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type SearchAppointmentsParams, type TimeInterval, type UpdateAppointmentData, UserRole };
2899
+ /**
2900
+ * Ensures that the free consultation infrastructure exists in the database
2901
+ * Creates category, subcategory, and technology if they don't exist
2902
+ * @param db - Firestore database instance (optional, defaults to admin.firestore())
2903
+ * @returns Promise<boolean> - Always returns true after ensuring infrastructure exists
2904
+ */
2905
+ declare function freeConsultationInfrastructure(db?: admin.firestore.Firestore): Promise<boolean>;
2906
+
2907
+ export { APPOINTMENTS_COLLECTION, type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentMediaItem, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateAppointmentData, type CreateAppointmentHttpData, type DoctorInfo, DocumentManagerAdminService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, type LinkedFormInfo, Logger, MediaType, NOTIFICATIONS_COLLECTION, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientReviewInfo, PaymentStatus, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, PractitionerInviteMailingService, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureExtendedInfo, type ProcedureSummaryInfo, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type SearchAppointmentsParams, type TimeInterval, type UpdateAppointmentData, UserRole, freeConsultationInfrastructure };
@@ -2896,4 +2896,12 @@ declare class AppointmentMailingService extends BaseMailingService {
2896
2896
  sendReviewAddedEmail(data: ReviewAddedEmailData): Promise<any>;
2897
2897
  }
2898
2898
 
2899
- export { APPOINTMENTS_COLLECTION, type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentMediaItem, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateAppointmentData, type CreateAppointmentHttpData, type DoctorInfo, DocumentManagerAdminService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, type LinkedFormInfo, Logger, MediaType, NOTIFICATIONS_COLLECTION, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientReviewInfo, PaymentStatus, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, PractitionerInviteMailingService, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureExtendedInfo, type ProcedureSummaryInfo, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type SearchAppointmentsParams, type TimeInterval, type UpdateAppointmentData, UserRole };
2899
+ /**
2900
+ * Ensures that the free consultation infrastructure exists in the database
2901
+ * Creates category, subcategory, and technology if they don't exist
2902
+ * @param db - Firestore database instance (optional, defaults to admin.firestore())
2903
+ * @returns Promise<boolean> - Always returns true after ensuring infrastructure exists
2904
+ */
2905
+ declare function freeConsultationInfrastructure(db?: admin.firestore.Firestore): Promise<boolean>;
2906
+
2907
+ export { APPOINTMENTS_COLLECTION, type Appointment, AppointmentAggregationService, type AppointmentCancellationEmailData, type AppointmentConfirmationEmailData, type AppointmentEmailDataBase, AppointmentMailingService, type AppointmentMediaItem, type AppointmentReminderNotification, type AppointmentRequestedEmailData, type AppointmentRescheduledProposalEmailData, AppointmentStatus, type AvailableSlot, BaseMailingService, type BaseNotification, BookingAdmin, BookingAvailabilityCalculator, type BookingAvailabilityRequest, type BookingAvailabilityResponse, CalendarAdminService, type Clinic, ClinicAggregationService, type ClinicInfo, type ClinicLocation, type CreateAppointmentData, type CreateAppointmentHttpData, type DoctorInfo, DocumentManagerAdminService, type FilledDocument, FilledFormsAggregationService, type InitializeAppointmentFormsResult, type LinkedFormInfo, Logger, MediaType, NOTIFICATIONS_COLLECTION, type Notification, NotificationStatus, NotificationType, NotificationsAdmin, type OrchestrateAppointmentCreationData, PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME, type PatientProfile as Patient, PatientAggregationService, PatientInstructionStatus, type PatientRequirementInstance, type PatientRequirementInstruction, PatientRequirementOverallStatus, PatientRequirementsAdminService, type PatientReviewInfo, PaymentStatus, type PostRequirementNotification, type Practitioner, PractitionerAggregationService, PractitionerInviteMailingService, type PractitionerToken, PractitionerTokenStatus, type PreRequirementNotification, type Procedure, ProcedureAggregationService, type ProcedureExtendedInfo, type ProcedureSummaryInfo, type Review, type ReviewAddedEmailData, type ReviewRequestEmailData, ReviewsAggregationService, type SearchAppointmentsParams, type TimeInterval, type UpdateAppointmentData, UserRole, freeConsultationInfrastructure };
@@ -58,7 +58,8 @@ __export(index_exports, {
58
58
  PractitionerTokenStatus: () => PractitionerTokenStatus,
59
59
  ProcedureAggregationService: () => ProcedureAggregationService,
60
60
  ReviewsAggregationService: () => ReviewsAggregationService,
61
- UserRole: () => UserRole
61
+ UserRole: () => UserRole,
62
+ freeConsultationInfrastructure: () => freeConsultationInfrastructure
62
63
  });
63
64
  module.exports = __toCommonJS(index_exports);
64
65
 
@@ -205,9 +206,9 @@ var Logger = class {
205
206
 
206
207
  // src/admin/notifications/notifications.admin.ts
207
208
  var NotificationsAdmin = class {
208
- constructor(firestore15) {
209
+ constructor(firestore16) {
209
210
  this.expo = new import_expo_server_sdk.Expo();
210
- this.db = firestore15 || admin.firestore();
211
+ this.db = firestore16 || admin.firestore();
211
212
  }
212
213
  /**
213
214
  * Dohvata notifikaciju po ID-u
@@ -926,8 +927,8 @@ var ClinicAggregationService = class {
926
927
  * Constructor for ClinicAggregationService.
927
928
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
928
929
  */
929
- constructor(firestore15) {
930
- this.db = firestore15 || admin3.firestore();
930
+ constructor(firestore16) {
931
+ this.db = firestore16 || admin3.firestore();
931
932
  }
932
933
  /**
933
934
  * Adds clinic information to a clinic group when a new clinic is created
@@ -1439,8 +1440,8 @@ var ClinicAggregationService = class {
1439
1440
  var admin4 = __toESM(require("firebase-admin"));
1440
1441
  var CALENDAR_SUBCOLLECTION_ID2 = "calendar";
1441
1442
  var PractitionerAggregationService = class {
1442
- constructor(firestore15) {
1443
- this.db = firestore15 || admin4.firestore();
1443
+ constructor(firestore16) {
1444
+ this.db = firestore16 || admin4.firestore();
1444
1445
  }
1445
1446
  /**
1446
1447
  * Adds practitioner information to a clinic when a new practitioner is created
@@ -1775,8 +1776,8 @@ var PractitionerAggregationService = class {
1775
1776
  var admin5 = __toESM(require("firebase-admin"));
1776
1777
  var CALENDAR_SUBCOLLECTION_ID3 = "calendar";
1777
1778
  var ProcedureAggregationService = class {
1778
- constructor(firestore15) {
1779
- this.db = firestore15 || admin5.firestore();
1779
+ constructor(firestore16) {
1780
+ this.db = firestore16 || admin5.firestore();
1780
1781
  }
1781
1782
  /**
1782
1783
  * Adds procedure information to a practitioner when a new procedure is created
@@ -2291,8 +2292,8 @@ var ProcedureAggregationService = class {
2291
2292
  var admin6 = __toESM(require("firebase-admin"));
2292
2293
  var CALENDAR_SUBCOLLECTION_ID4 = "calendar";
2293
2294
  var PatientAggregationService = class {
2294
- constructor(firestore15) {
2295
- this.db = firestore15 || admin6.firestore();
2295
+ constructor(firestore16) {
2296
+ this.db = firestore16 || admin6.firestore();
2296
2297
  }
2297
2298
  // --- Methods for Patient Creation --- >
2298
2299
  // No specific aggregations defined for patient creation in the plan.
@@ -2424,8 +2425,8 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
2424
2425
  // src/admin/requirements/patient-requirements.admin.service.ts
2425
2426
  var admin7 = __toESM(require("firebase-admin"));
2426
2427
  var PatientRequirementsAdminService = class {
2427
- constructor(firestore15) {
2428
- this.db = firestore15 || admin7.firestore();
2428
+ constructor(firestore16) {
2429
+ this.db = firestore16 || admin7.firestore();
2429
2430
  this.notificationsAdmin = new NotificationsAdmin(this.db);
2430
2431
  }
2431
2432
  /**
@@ -2753,8 +2754,8 @@ var PatientRequirementsAdminService = class {
2753
2754
  // src/admin/calendar/calendar.admin.service.ts
2754
2755
  var admin8 = __toESM(require("firebase-admin"));
2755
2756
  var CalendarAdminService = class {
2756
- constructor(firestore15) {
2757
- this.db = firestore15 || admin8.firestore();
2757
+ constructor(firestore16) {
2758
+ this.db = firestore16 || admin8.firestore();
2758
2759
  Logger.info("[CalendarAdminService] Initialized.");
2759
2760
  }
2760
2761
  /**
@@ -3037,9 +3038,9 @@ var BaseMailingService = class {
3037
3038
  * @param firestore Firestore instance provided by the caller
3038
3039
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
3039
3040
  */
3040
- constructor(firestore15, mailgunClient) {
3041
+ constructor(firestore16, mailgunClient) {
3041
3042
  var _a;
3042
- this.db = firestore15;
3043
+ this.db = firestore16;
3043
3044
  this.mailgunClient = mailgunClient;
3044
3045
  if (!this.db) {
3045
3046
  Logger.error("[BaseMailingService] No Firestore instance provided");
@@ -3183,8 +3184,8 @@ var BaseMailingService = class {
3183
3184
  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>";
3184
3185
  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>";
3185
3186
  var AppointmentMailingService = class extends BaseMailingService {
3186
- constructor(firestore15, mailgunClient) {
3187
- super(firestore15, mailgunClient);
3187
+ constructor(firestore16, mailgunClient) {
3188
+ super(firestore16, mailgunClient);
3188
3189
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
3189
3190
  Logger.info("[AppointmentMailingService] Initialized.");
3190
3191
  }
@@ -3333,8 +3334,8 @@ var AppointmentAggregationService = class {
3333
3334
  * @param mailgunClient - An initialized Mailgun client instance.
3334
3335
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
3335
3336
  */
3336
- constructor(mailgunClient, firestore15) {
3337
- this.db = firestore15 || admin10.firestore();
3337
+ constructor(mailgunClient, firestore16) {
3338
+ this.db = firestore16 || admin10.firestore();
3338
3339
  this.appointmentMailingService = new AppointmentMailingService(
3339
3340
  this.db,
3340
3341
  mailgunClient
@@ -4590,8 +4591,8 @@ var FilledFormsAggregationService = class {
4590
4591
  * Constructor for FilledFormsAggregationService.
4591
4592
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
4592
4593
  */
4593
- constructor(firestore15) {
4594
- this.db = firestore15 || admin11.firestore();
4594
+ constructor(firestore16) {
4595
+ this.db = firestore16 || admin11.firestore();
4595
4596
  Logger.info("[FilledFormsAggregationService] Initialized");
4596
4597
  }
4597
4598
  /**
@@ -4785,8 +4786,8 @@ var ReviewsAggregationService = class {
4785
4786
  * Constructor for ReviewsAggregationService.
4786
4787
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
4787
4788
  */
4788
- constructor(firestore15) {
4789
- this.db = firestore15 || admin12.firestore();
4789
+ constructor(firestore16) {
4790
+ this.db = firestore16 || admin12.firestore();
4790
4791
  }
4791
4792
  /**
4792
4793
  * Process a newly created review and update all related entities
@@ -5331,8 +5332,8 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
5331
5332
  * @param firestore Firestore instance provided by the caller
5332
5333
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
5333
5334
  */
5334
- constructor(firestore15, mailgunClient) {
5335
- super(firestore15, mailgunClient);
5335
+ constructor(firestore16, mailgunClient) {
5336
+ super(firestore16, mailgunClient);
5336
5337
  this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
5337
5338
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
5338
5339
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
@@ -6006,8 +6007,8 @@ BookingAvailabilityCalculator.DEFAULT_INTERVAL_MINUTES = 15;
6006
6007
  // src/admin/documentation-templates/document-manager.admin.ts
6007
6008
  var admin13 = __toESM(require("firebase-admin"));
6008
6009
  var DocumentManagerAdminService = class {
6009
- constructor(firestore15) {
6010
- this.db = firestore15;
6010
+ constructor(firestore16) {
6011
+ this.db = firestore16;
6011
6012
  }
6012
6013
  /**
6013
6014
  * Adds operations to a Firestore batch to initialize all linked forms for a new appointment
@@ -6176,8 +6177,8 @@ var BookingAdmin = class {
6176
6177
  * Creates a new BookingAdmin instance
6177
6178
  * @param firestore - Firestore instance provided by the caller
6178
6179
  */
6179
- constructor(firestore15) {
6180
- this.db = firestore15 || admin14.firestore();
6180
+ constructor(firestore16) {
6181
+ this.db = firestore16 || admin14.firestore();
6181
6182
  this.documentManagerAdmin = new DocumentManagerAdminService(this.db);
6182
6183
  }
6183
6184
  /**
@@ -6779,6 +6780,116 @@ var BookingAdmin = class {
6779
6780
  }
6780
6781
  };
6781
6782
 
6783
+ // src/admin/free-consultation/free-consultation-utils.admin.ts
6784
+ var admin15 = __toESM(require("firebase-admin"));
6785
+
6786
+ // src/backoffice/types/category.types.ts
6787
+ var CATEGORIES_COLLECTION = "backoffice_categories";
6788
+
6789
+ // src/backoffice/types/subcategory.types.ts
6790
+ var SUBCATEGORIES_COLLECTION = "subcategories";
6791
+
6792
+ // src/backoffice/types/technology.types.ts
6793
+ var TECHNOLOGIES_COLLECTION = "technologies";
6794
+
6795
+ // src/admin/free-consultation/free-consultation-utils.admin.ts
6796
+ async function freeConsultationInfrastructure(db) {
6797
+ const firestore16 = db || admin15.firestore();
6798
+ try {
6799
+ console.log(
6800
+ "[freeConsultationInfrastructure] Checking free consultation infrastructure..."
6801
+ );
6802
+ const technologyRef = firestore16.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
6803
+ const technologyDoc = await technologyRef.get();
6804
+ if (technologyDoc.exists) {
6805
+ console.log(
6806
+ "[freeConsultationInfrastructure] Free consultation infrastructure already exists"
6807
+ );
6808
+ return true;
6809
+ }
6810
+ console.log(
6811
+ "[freeConsultationInfrastructure] Creating free consultation infrastructure..."
6812
+ );
6813
+ await createFreeConsultationInfrastructure(firestore16);
6814
+ console.log(
6815
+ "[freeConsultationInfrastructure] Successfully created free consultation infrastructure"
6816
+ );
6817
+ return true;
6818
+ } catch (error) {
6819
+ console.error(
6820
+ "[freeConsultationInfrastructure] Error ensuring infrastructure:",
6821
+ error
6822
+ );
6823
+ throw error;
6824
+ }
6825
+ }
6826
+ async function createFreeConsultationInfrastructure(db) {
6827
+ const batch = db.batch();
6828
+ const now = /* @__PURE__ */ new Date();
6829
+ const categoryRef = db.collection(CATEGORIES_COLLECTION).doc("consultation");
6830
+ const categoryData = {
6831
+ id: "consultation",
6832
+ name: "Consultation",
6833
+ description: "Professional consultation services for treatment planning and assessment",
6834
+ family: "aesthetics" /* AESTHETICS */,
6835
+ isActive: true,
6836
+ createdAt: now,
6837
+ updatedAt: now
6838
+ };
6839
+ batch.set(categoryRef, categoryData);
6840
+ const subcategoryRef = db.collection(CATEGORIES_COLLECTION).doc("consultation").collection(SUBCATEGORIES_COLLECTION).doc("free-consultation");
6841
+ const subcategoryData = {
6842
+ id: "free-consultation",
6843
+ name: "Free Consultation",
6844
+ description: "Complimentary initial consultation to discuss treatment options and assess patient needs",
6845
+ categoryId: "consultation",
6846
+ isActive: true,
6847
+ createdAt: now,
6848
+ updatedAt: now
6849
+ };
6850
+ batch.set(subcategoryRef, subcategoryData);
6851
+ const technologyRef = db.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
6852
+ const technologyData = {
6853
+ id: "free-consultation-tech",
6854
+ name: "Free Consultation Technology",
6855
+ description: "Technology framework for providing free initial consultations to patients",
6856
+ family: "aesthetics" /* AESTHETICS */,
6857
+ categoryId: "consultation",
6858
+ subcategoryId: "free-consultation",
6859
+ technicalDetails: "Standard consultation protocol for initial patient assessment",
6860
+ requirements: {
6861
+ pre: [],
6862
+ // No pre-requirements for consultation
6863
+ post: []
6864
+ // No post-requirements for consultation
6865
+ },
6866
+ blockingConditions: [],
6867
+ // No blocking conditions for consultation
6868
+ contraindications: [],
6869
+ // No contraindications for consultation
6870
+ benefits: [
6871
+ "IMPROVED_PATIENT_UNDERSTANDING",
6872
+ "BETTER_TREATMENT_PLANNING",
6873
+ "ENHANCED_PATIENT_CONFIDENCE"
6874
+ ],
6875
+ certificationRequirement: {
6876
+ minimumLevel: "aesthetician" /* AESTHETICIAN */,
6877
+ requiredSpecialties: []
6878
+ // No required specialties for consultation
6879
+ },
6880
+ documentationTemplates: [],
6881
+ isActive: true,
6882
+ createdAt: now,
6883
+ updatedAt: now
6884
+ };
6885
+ batch.set(technologyRef, technologyData);
6886
+ await batch.commit();
6887
+ console.log("[freeConsultationInfrastructure] Successfully created:");
6888
+ console.log(" \u2713 Category: consultation");
6889
+ console.log(" \u2713 Subcategory: free-consultation");
6890
+ console.log(" \u2713 Technology: free-consultation-tech");
6891
+ }
6892
+
6782
6893
  // src/admin/index.ts
6783
6894
  console.log("[Admin Module] Initialized and services exported.");
6784
6895
  TimestampUtils.enableServerMode();
@@ -6812,5 +6923,6 @@ TimestampUtils.enableServerMode();
6812
6923
  PractitionerTokenStatus,
6813
6924
  ProcedureAggregationService,
6814
6925
  ReviewsAggregationService,
6815
- UserRole
6926
+ UserRole,
6927
+ freeConsultationInfrastructure
6816
6928
  });
@@ -148,9 +148,9 @@ var Logger = class {
148
148
 
149
149
  // src/admin/notifications/notifications.admin.ts
150
150
  var NotificationsAdmin = class {
151
- constructor(firestore15) {
151
+ constructor(firestore16) {
152
152
  this.expo = new Expo();
153
- this.db = firestore15 || admin.firestore();
153
+ this.db = firestore16 || 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(firestore15) {
873
- this.db = firestore15 || admin3.firestore();
872
+ constructor(firestore16) {
873
+ this.db = firestore16 || admin3.firestore();
874
874
  }
875
875
  /**
876
876
  * Adds clinic information to a clinic group when a new clinic is created
@@ -1382,8 +1382,8 @@ var ClinicAggregationService = class {
1382
1382
  import * as admin4 from "firebase-admin";
1383
1383
  var CALENDAR_SUBCOLLECTION_ID2 = "calendar";
1384
1384
  var PractitionerAggregationService = class {
1385
- constructor(firestore15) {
1386
- this.db = firestore15 || admin4.firestore();
1385
+ constructor(firestore16) {
1386
+ this.db = firestore16 || admin4.firestore();
1387
1387
  }
1388
1388
  /**
1389
1389
  * Adds practitioner information to a clinic when a new practitioner is created
@@ -1718,8 +1718,8 @@ var PractitionerAggregationService = class {
1718
1718
  import * as admin5 from "firebase-admin";
1719
1719
  var CALENDAR_SUBCOLLECTION_ID3 = "calendar";
1720
1720
  var ProcedureAggregationService = class {
1721
- constructor(firestore15) {
1722
- this.db = firestore15 || admin5.firestore();
1721
+ constructor(firestore16) {
1722
+ this.db = firestore16 || admin5.firestore();
1723
1723
  }
1724
1724
  /**
1725
1725
  * Adds procedure information to a practitioner when a new procedure is created
@@ -2234,8 +2234,8 @@ var ProcedureAggregationService = class {
2234
2234
  import * as admin6 from "firebase-admin";
2235
2235
  var CALENDAR_SUBCOLLECTION_ID4 = "calendar";
2236
2236
  var PatientAggregationService = class {
2237
- constructor(firestore15) {
2238
- this.db = firestore15 || admin6.firestore();
2237
+ constructor(firestore16) {
2238
+ this.db = firestore16 || admin6.firestore();
2239
2239
  }
2240
2240
  // --- Methods for Patient Creation --- >
2241
2241
  // No specific aggregations defined for patient creation in the plan.
@@ -2367,8 +2367,8 @@ var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
2367
2367
  // src/admin/requirements/patient-requirements.admin.service.ts
2368
2368
  import * as admin7 from "firebase-admin";
2369
2369
  var PatientRequirementsAdminService = class {
2370
- constructor(firestore15) {
2371
- this.db = firestore15 || admin7.firestore();
2370
+ constructor(firestore16) {
2371
+ this.db = firestore16 || admin7.firestore();
2372
2372
  this.notificationsAdmin = new NotificationsAdmin(this.db);
2373
2373
  }
2374
2374
  /**
@@ -2696,8 +2696,8 @@ var PatientRequirementsAdminService = class {
2696
2696
  // src/admin/calendar/calendar.admin.service.ts
2697
2697
  import * as admin8 from "firebase-admin";
2698
2698
  var CalendarAdminService = class {
2699
- constructor(firestore15) {
2700
- this.db = firestore15 || admin8.firestore();
2699
+ constructor(firestore16) {
2700
+ this.db = firestore16 || admin8.firestore();
2701
2701
  Logger.info("[CalendarAdminService] Initialized.");
2702
2702
  }
2703
2703
  /**
@@ -2980,9 +2980,9 @@ var BaseMailingService = class {
2980
2980
  * @param firestore Firestore instance provided by the caller
2981
2981
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
2982
2982
  */
2983
- constructor(firestore15, mailgunClient) {
2983
+ constructor(firestore16, mailgunClient) {
2984
2984
  var _a;
2985
- this.db = firestore15;
2985
+ this.db = firestore16;
2986
2986
  this.mailgunClient = mailgunClient;
2987
2987
  if (!this.db) {
2988
2988
  Logger.error("[BaseMailingService] No Firestore instance provided");
@@ -3126,8 +3126,8 @@ var BaseMailingService = class {
3126
3126
  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>";
3127
3127
  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>";
3128
3128
  var AppointmentMailingService = class extends BaseMailingService {
3129
- constructor(firestore15, mailgunClient) {
3130
- super(firestore15, mailgunClient);
3129
+ constructor(firestore16, mailgunClient) {
3130
+ super(firestore16, mailgunClient);
3131
3131
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
3132
3132
  Logger.info("[AppointmentMailingService] Initialized.");
3133
3133
  }
@@ -3276,8 +3276,8 @@ var AppointmentAggregationService = class {
3276
3276
  * @param mailgunClient - An initialized Mailgun client instance.
3277
3277
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
3278
3278
  */
3279
- constructor(mailgunClient, firestore15) {
3280
- this.db = firestore15 || admin10.firestore();
3279
+ constructor(mailgunClient, firestore16) {
3280
+ this.db = firestore16 || admin10.firestore();
3281
3281
  this.appointmentMailingService = new AppointmentMailingService(
3282
3282
  this.db,
3283
3283
  mailgunClient
@@ -4533,8 +4533,8 @@ var FilledFormsAggregationService = class {
4533
4533
  * Constructor for FilledFormsAggregationService.
4534
4534
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
4535
4535
  */
4536
- constructor(firestore15) {
4537
- this.db = firestore15 || admin11.firestore();
4536
+ constructor(firestore16) {
4537
+ this.db = firestore16 || admin11.firestore();
4538
4538
  Logger.info("[FilledFormsAggregationService] Initialized");
4539
4539
  }
4540
4540
  /**
@@ -4728,8 +4728,8 @@ var ReviewsAggregationService = class {
4728
4728
  * Constructor for ReviewsAggregationService.
4729
4729
  * @param firestore Optional Firestore instance. If not provided, it uses the default admin SDK instance.
4730
4730
  */
4731
- constructor(firestore15) {
4732
- this.db = firestore15 || admin12.firestore();
4731
+ constructor(firestore16) {
4732
+ this.db = firestore16 || admin12.firestore();
4733
4733
  }
4734
4734
  /**
4735
4735
  * Process a newly created review and update all related entities
@@ -5274,8 +5274,8 @@ var PractitionerInviteMailingService = class extends BaseMailingService {
5274
5274
  * @param firestore Firestore instance provided by the caller
5275
5275
  * @param mailgunClient Mailgun client instance (mailgun.js v10+) provided by the caller
5276
5276
  */
5277
- constructor(firestore15, mailgunClient) {
5278
- super(firestore15, mailgunClient);
5277
+ constructor(firestore16, mailgunClient) {
5278
+ super(firestore16, mailgunClient);
5279
5279
  this.DEFAULT_REGISTRATION_URL = "https://metaesthetics.net/register";
5280
5280
  this.DEFAULT_SUBJECT = "You've Been Invited to Join as a Practitioner";
5281
5281
  this.DEFAULT_MAILGUN_DOMAIN = "mg.metaesthetics.net";
@@ -5949,8 +5949,8 @@ BookingAvailabilityCalculator.DEFAULT_INTERVAL_MINUTES = 15;
5949
5949
  // src/admin/documentation-templates/document-manager.admin.ts
5950
5950
  import * as admin13 from "firebase-admin";
5951
5951
  var DocumentManagerAdminService = class {
5952
- constructor(firestore15) {
5953
- this.db = firestore15;
5952
+ constructor(firestore16) {
5953
+ this.db = firestore16;
5954
5954
  }
5955
5955
  /**
5956
5956
  * Adds operations to a Firestore batch to initialize all linked forms for a new appointment
@@ -6119,8 +6119,8 @@ var BookingAdmin = class {
6119
6119
  * Creates a new BookingAdmin instance
6120
6120
  * @param firestore - Firestore instance provided by the caller
6121
6121
  */
6122
- constructor(firestore15) {
6123
- this.db = firestore15 || admin14.firestore();
6122
+ constructor(firestore16) {
6123
+ this.db = firestore16 || admin14.firestore();
6124
6124
  this.documentManagerAdmin = new DocumentManagerAdminService(this.db);
6125
6125
  }
6126
6126
  /**
@@ -6722,6 +6722,116 @@ var BookingAdmin = class {
6722
6722
  }
6723
6723
  };
6724
6724
 
6725
+ // src/admin/free-consultation/free-consultation-utils.admin.ts
6726
+ import * as admin15 from "firebase-admin";
6727
+
6728
+ // src/backoffice/types/category.types.ts
6729
+ var CATEGORIES_COLLECTION = "backoffice_categories";
6730
+
6731
+ // src/backoffice/types/subcategory.types.ts
6732
+ var SUBCATEGORIES_COLLECTION = "subcategories";
6733
+
6734
+ // src/backoffice/types/technology.types.ts
6735
+ var TECHNOLOGIES_COLLECTION = "technologies";
6736
+
6737
+ // src/admin/free-consultation/free-consultation-utils.admin.ts
6738
+ async function freeConsultationInfrastructure(db) {
6739
+ const firestore16 = db || admin15.firestore();
6740
+ try {
6741
+ console.log(
6742
+ "[freeConsultationInfrastructure] Checking free consultation infrastructure..."
6743
+ );
6744
+ const technologyRef = firestore16.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
6745
+ const technologyDoc = await technologyRef.get();
6746
+ if (technologyDoc.exists) {
6747
+ console.log(
6748
+ "[freeConsultationInfrastructure] Free consultation infrastructure already exists"
6749
+ );
6750
+ return true;
6751
+ }
6752
+ console.log(
6753
+ "[freeConsultationInfrastructure] Creating free consultation infrastructure..."
6754
+ );
6755
+ await createFreeConsultationInfrastructure(firestore16);
6756
+ console.log(
6757
+ "[freeConsultationInfrastructure] Successfully created free consultation infrastructure"
6758
+ );
6759
+ return true;
6760
+ } catch (error) {
6761
+ console.error(
6762
+ "[freeConsultationInfrastructure] Error ensuring infrastructure:",
6763
+ error
6764
+ );
6765
+ throw error;
6766
+ }
6767
+ }
6768
+ async function createFreeConsultationInfrastructure(db) {
6769
+ const batch = db.batch();
6770
+ const now = /* @__PURE__ */ new Date();
6771
+ const categoryRef = db.collection(CATEGORIES_COLLECTION).doc("consultation");
6772
+ const categoryData = {
6773
+ id: "consultation",
6774
+ name: "Consultation",
6775
+ description: "Professional consultation services for treatment planning and assessment",
6776
+ family: "aesthetics" /* AESTHETICS */,
6777
+ isActive: true,
6778
+ createdAt: now,
6779
+ updatedAt: now
6780
+ };
6781
+ batch.set(categoryRef, categoryData);
6782
+ const subcategoryRef = db.collection(CATEGORIES_COLLECTION).doc("consultation").collection(SUBCATEGORIES_COLLECTION).doc("free-consultation");
6783
+ const subcategoryData = {
6784
+ id: "free-consultation",
6785
+ name: "Free Consultation",
6786
+ description: "Complimentary initial consultation to discuss treatment options and assess patient needs",
6787
+ categoryId: "consultation",
6788
+ isActive: true,
6789
+ createdAt: now,
6790
+ updatedAt: now
6791
+ };
6792
+ batch.set(subcategoryRef, subcategoryData);
6793
+ const technologyRef = db.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
6794
+ const technologyData = {
6795
+ id: "free-consultation-tech",
6796
+ name: "Free Consultation Technology",
6797
+ description: "Technology framework for providing free initial consultations to patients",
6798
+ family: "aesthetics" /* AESTHETICS */,
6799
+ categoryId: "consultation",
6800
+ subcategoryId: "free-consultation",
6801
+ technicalDetails: "Standard consultation protocol for initial patient assessment",
6802
+ requirements: {
6803
+ pre: [],
6804
+ // No pre-requirements for consultation
6805
+ post: []
6806
+ // No post-requirements for consultation
6807
+ },
6808
+ blockingConditions: [],
6809
+ // No blocking conditions for consultation
6810
+ contraindications: [],
6811
+ // No contraindications for consultation
6812
+ benefits: [
6813
+ "IMPROVED_PATIENT_UNDERSTANDING",
6814
+ "BETTER_TREATMENT_PLANNING",
6815
+ "ENHANCED_PATIENT_CONFIDENCE"
6816
+ ],
6817
+ certificationRequirement: {
6818
+ minimumLevel: "aesthetician" /* AESTHETICIAN */,
6819
+ requiredSpecialties: []
6820
+ // No required specialties for consultation
6821
+ },
6822
+ documentationTemplates: [],
6823
+ isActive: true,
6824
+ createdAt: now,
6825
+ updatedAt: now
6826
+ };
6827
+ batch.set(technologyRef, technologyData);
6828
+ await batch.commit();
6829
+ console.log("[freeConsultationInfrastructure] Successfully created:");
6830
+ console.log(" \u2713 Category: consultation");
6831
+ console.log(" \u2713 Subcategory: free-consultation");
6832
+ console.log(" \u2713 Technology: free-consultation-tech");
6833
+ }
6834
+
6725
6835
  // src/admin/index.ts
6726
6836
  console.log("[Admin Module] Initialized and services exported.");
6727
6837
  TimestampUtils.enableServerMode();
@@ -6754,5 +6864,6 @@ export {
6754
6864
  PractitionerTokenStatus,
6755
6865
  ProcedureAggregationService,
6756
6866
  ReviewsAggregationService,
6757
- UserRole
6867
+ UserRole,
6868
+ freeConsultationInfrastructure
6758
6869
  };
package/dist/index.d.mts CHANGED
@@ -6058,6 +6058,12 @@ declare class ProcedureService extends BaseService {
6058
6058
  * @returns List of procedures
6059
6059
  */
6060
6060
  getProceduresByPractitioner(practitionerId: string): Promise<Procedure[]>;
6061
+ /**
6062
+ * Gets all inactive procedures for a practitioner
6063
+ * @param practitionerId - The ID of the practitioner
6064
+ * @returns List of inactive procedures
6065
+ */
6066
+ getInactiveProceduresByPractitioner(practitionerId: string): Promise<Procedure[]>;
6061
6067
  /**
6062
6068
  * Updates a procedure
6063
6069
  * @param id - The ID of the procedure to update
@@ -6345,6 +6351,11 @@ declare class PractitionerService extends BaseService {
6345
6351
  * @returns The created consultation procedure
6346
6352
  */
6347
6353
  EnableFreeConsultation(practitionerId: string, clinicId: string): Promise<void>;
6354
+ /**
6355
+ * Ensures that the free consultation infrastructure exists by calling the Cloud Function
6356
+ * @returns Promise<boolean> - True if infrastructure exists or was created successfully
6357
+ */
6358
+ ensureFreeConsultationInfrastructure(): Promise<boolean>;
6348
6359
  /**
6349
6360
  * Disables free consultation for a practitioner in a specific clinic
6350
6361
  * Finds and deactivates the existing free consultation procedure
package/dist/index.d.ts CHANGED
@@ -6058,6 +6058,12 @@ declare class ProcedureService extends BaseService {
6058
6058
  * @returns List of procedures
6059
6059
  */
6060
6060
  getProceduresByPractitioner(practitionerId: string): Promise<Procedure[]>;
6061
+ /**
6062
+ * Gets all inactive procedures for a practitioner
6063
+ * @param practitionerId - The ID of the practitioner
6064
+ * @returns List of inactive procedures
6065
+ */
6066
+ getInactiveProceduresByPractitioner(practitionerId: string): Promise<Procedure[]>;
6061
6067
  /**
6062
6068
  * Updates a procedure
6063
6069
  * @param id - The ID of the procedure to update
@@ -6345,6 +6351,11 @@ declare class PractitionerService extends BaseService {
6345
6351
  * @returns The created consultation procedure
6346
6352
  */
6347
6353
  EnableFreeConsultation(practitionerId: string, clinicId: string): Promise<void>;
6354
+ /**
6355
+ * Ensures that the free consultation infrastructure exists by calling the Cloud Function
6356
+ * @returns Promise<boolean> - True if infrastructure exists or was created successfully
6357
+ */
6358
+ ensureFreeConsultationInfrastructure(): Promise<boolean>;
6348
6359
  /**
6349
6360
  * Disables free consultation for a practitioner in a specific clinic
6350
6361
  * Finds and deactivates the existing free consultation procedure
package/dist/index.js CHANGED
@@ -5543,6 +5543,7 @@ var PractitionerService = class extends BaseService {
5543
5543
  */
5544
5544
  async EnableFreeConsultation(practitionerId, clinicId) {
5545
5545
  try {
5546
+ await this.ensureFreeConsultationInfrastructure();
5546
5547
  const practitioner = await this.getPractitioner(practitionerId);
5547
5548
  if (!practitioner) {
5548
5549
  throw new Error(`Practitioner ${practitionerId} not found`);
@@ -5559,17 +5560,32 @@ var PractitionerService = class extends BaseService {
5559
5560
  `Practitioner ${practitionerId} is not associated with clinic ${clinicId}`
5560
5561
  );
5561
5562
  }
5562
- const existingProcedures = await this.getProcedureService().getProceduresByPractitioner(
5563
- practitionerId
5564
- );
5565
- const existingConsultation = existingProcedures.find(
5566
- (procedure) => procedure.name === "Free Consultation" && procedure.clinicBranchId === clinicId && procedure.isActive
5563
+ const [activeProcedures, inactiveProcedures] = await Promise.all([
5564
+ this.getProcedureService().getProceduresByPractitioner(practitionerId),
5565
+ this.getProcedureService().getInactiveProceduresByPractitioner(
5566
+ practitionerId
5567
+ )
5568
+ ]);
5569
+ const allProcedures = [...activeProcedures, ...inactiveProcedures];
5570
+ const existingConsultation = allProcedures.find(
5571
+ (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
5567
5572
  );
5568
5573
  if (existingConsultation) {
5569
- console.log(
5570
- `Free consultation already exists for practitioner ${practitionerId} in clinic ${clinicId}`
5571
- );
5572
- return;
5574
+ if (existingConsultation.isActive) {
5575
+ console.log(
5576
+ `Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
5577
+ );
5578
+ return;
5579
+ } else {
5580
+ await this.getProcedureService().updateProcedure(
5581
+ existingConsultation.id,
5582
+ { isActive: true }
5583
+ );
5584
+ console.log(
5585
+ `Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
5586
+ );
5587
+ return;
5588
+ }
5573
5589
  }
5574
5590
  const consultationData = {
5575
5591
  name: "Free Consultation",
@@ -5602,6 +5618,74 @@ var PractitionerService = class extends BaseService {
5602
5618
  throw error;
5603
5619
  }
5604
5620
  }
5621
+ /**
5622
+ * Ensures that the free consultation infrastructure exists by calling the Cloud Function
5623
+ * @returns Promise<boolean> - True if infrastructure exists or was created successfully
5624
+ */
5625
+ async ensureFreeConsultationInfrastructure() {
5626
+ try {
5627
+ console.log(
5628
+ "[PRACTITIONER_SERVICE] Ensuring free consultation infrastructure via HTTP"
5629
+ );
5630
+ const currentUser = this.auth.currentUser;
5631
+ if (!currentUser) {
5632
+ throw new Error(
5633
+ "User must be authenticated to ensure free consultation infrastructure"
5634
+ );
5635
+ }
5636
+ const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/ensureFreeConsultationInfrastructure`;
5637
+ const idToken = await currentUser.getIdToken();
5638
+ console.log(
5639
+ `[PRACTITIONER_SERVICE] Making fetch request to ${functionUrl}`
5640
+ );
5641
+ const response = await fetch(functionUrl, {
5642
+ method: "POST",
5643
+ mode: "cors",
5644
+ cache: "no-cache",
5645
+ credentials: "omit",
5646
+ headers: {
5647
+ "Content-Type": "application/json",
5648
+ Authorization: `Bearer ${idToken}`
5649
+ },
5650
+ redirect: "follow",
5651
+ referrerPolicy: "no-referrer",
5652
+ body: JSON.stringify({})
5653
+ // Empty body as no parameters needed
5654
+ });
5655
+ console.log(
5656
+ `[PRACTITIONER_SERVICE] Received response ${response.status}: ${response.statusText}`
5657
+ );
5658
+ if (!response.ok) {
5659
+ const errorText = await response.text();
5660
+ console.error(
5661
+ `[PRACTITIONER_SERVICE] Error response details: ${errorText}`
5662
+ );
5663
+ throw new Error(
5664
+ `Failed to ensure free consultation infrastructure: ${response.status} ${response.statusText} - ${errorText}`
5665
+ );
5666
+ }
5667
+ const result = await response.json();
5668
+ console.log(
5669
+ `[PRACTITIONER_SERVICE] Infrastructure check response:`,
5670
+ result
5671
+ );
5672
+ if (!result.success) {
5673
+ throw new Error(
5674
+ result.error || "Failed to ensure free consultation infrastructure"
5675
+ );
5676
+ }
5677
+ console.log(
5678
+ `[PRACTITIONER_SERVICE] Free consultation infrastructure ensured successfully`
5679
+ );
5680
+ return result.infrastructureExists;
5681
+ } catch (error) {
5682
+ console.error(
5683
+ "[PRACTITIONER_SERVICE] Error ensuring free consultation infrastructure:",
5684
+ error
5685
+ );
5686
+ throw error;
5687
+ }
5688
+ }
5605
5689
  /**
5606
5690
  * Disables free consultation for a practitioner in a specific clinic
5607
5691
  * Finds and deactivates the existing free consultation procedure
@@ -5627,7 +5711,7 @@ var PractitionerService = class extends BaseService {
5627
5711
  practitionerId
5628
5712
  );
5629
5713
  const freeConsultation = existingProcedures.find(
5630
- (procedure) => procedure.name === "Free Consultation" && procedure.clinicBranchId === clinicId && procedure.isActive
5714
+ (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId && procedure.isActive
5631
5715
  );
5632
5716
  if (!freeConsultation) {
5633
5717
  console.log(
@@ -9001,6 +9085,20 @@ var ProcedureService = class extends BaseService {
9001
9085
  const snapshot = await (0, import_firestore27.getDocs)(q);
9002
9086
  return snapshot.docs.map((doc36) => doc36.data());
9003
9087
  }
9088
+ /**
9089
+ * Gets all inactive procedures for a practitioner
9090
+ * @param practitionerId - The ID of the practitioner
9091
+ * @returns List of inactive procedures
9092
+ */
9093
+ async getInactiveProceduresByPractitioner(practitionerId) {
9094
+ const q = (0, import_firestore27.query)(
9095
+ (0, import_firestore27.collection)(this.db, PROCEDURES_COLLECTION),
9096
+ (0, import_firestore27.where)("practitionerId", "==", practitionerId),
9097
+ (0, import_firestore27.where)("isActive", "==", false)
9098
+ );
9099
+ const snapshot = await (0, import_firestore27.getDocs)(q);
9100
+ return snapshot.docs.map((doc36) => doc36.data());
9101
+ }
9004
9102
  /**
9005
9103
  * Updates a procedure
9006
9104
  * @param id - The ID of the procedure to update
package/dist/index.mjs CHANGED
@@ -5447,6 +5447,7 @@ var PractitionerService = class extends BaseService {
5447
5447
  */
5448
5448
  async EnableFreeConsultation(practitionerId, clinicId) {
5449
5449
  try {
5450
+ await this.ensureFreeConsultationInfrastructure();
5450
5451
  const practitioner = await this.getPractitioner(practitionerId);
5451
5452
  if (!practitioner) {
5452
5453
  throw new Error(`Practitioner ${practitionerId} not found`);
@@ -5463,17 +5464,32 @@ var PractitionerService = class extends BaseService {
5463
5464
  `Practitioner ${practitionerId} is not associated with clinic ${clinicId}`
5464
5465
  );
5465
5466
  }
5466
- const existingProcedures = await this.getProcedureService().getProceduresByPractitioner(
5467
- practitionerId
5468
- );
5469
- const existingConsultation = existingProcedures.find(
5470
- (procedure) => procedure.name === "Free Consultation" && procedure.clinicBranchId === clinicId && procedure.isActive
5467
+ const [activeProcedures, inactiveProcedures] = await Promise.all([
5468
+ this.getProcedureService().getProceduresByPractitioner(practitionerId),
5469
+ this.getProcedureService().getInactiveProceduresByPractitioner(
5470
+ practitionerId
5471
+ )
5472
+ ]);
5473
+ const allProcedures = [...activeProcedures, ...inactiveProcedures];
5474
+ const existingConsultation = allProcedures.find(
5475
+ (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
5471
5476
  );
5472
5477
  if (existingConsultation) {
5473
- console.log(
5474
- `Free consultation already exists for practitioner ${practitionerId} in clinic ${clinicId}`
5475
- );
5476
- return;
5478
+ if (existingConsultation.isActive) {
5479
+ console.log(
5480
+ `Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
5481
+ );
5482
+ return;
5483
+ } else {
5484
+ await this.getProcedureService().updateProcedure(
5485
+ existingConsultation.id,
5486
+ { isActive: true }
5487
+ );
5488
+ console.log(
5489
+ `Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
5490
+ );
5491
+ return;
5492
+ }
5477
5493
  }
5478
5494
  const consultationData = {
5479
5495
  name: "Free Consultation",
@@ -5506,6 +5522,74 @@ var PractitionerService = class extends BaseService {
5506
5522
  throw error;
5507
5523
  }
5508
5524
  }
5525
+ /**
5526
+ * Ensures that the free consultation infrastructure exists by calling the Cloud Function
5527
+ * @returns Promise<boolean> - True if infrastructure exists or was created successfully
5528
+ */
5529
+ async ensureFreeConsultationInfrastructure() {
5530
+ try {
5531
+ console.log(
5532
+ "[PRACTITIONER_SERVICE] Ensuring free consultation infrastructure via HTTP"
5533
+ );
5534
+ const currentUser = this.auth.currentUser;
5535
+ if (!currentUser) {
5536
+ throw new Error(
5537
+ "User must be authenticated to ensure free consultation infrastructure"
5538
+ );
5539
+ }
5540
+ const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/ensureFreeConsultationInfrastructure`;
5541
+ const idToken = await currentUser.getIdToken();
5542
+ console.log(
5543
+ `[PRACTITIONER_SERVICE] Making fetch request to ${functionUrl}`
5544
+ );
5545
+ const response = await fetch(functionUrl, {
5546
+ method: "POST",
5547
+ mode: "cors",
5548
+ cache: "no-cache",
5549
+ credentials: "omit",
5550
+ headers: {
5551
+ "Content-Type": "application/json",
5552
+ Authorization: `Bearer ${idToken}`
5553
+ },
5554
+ redirect: "follow",
5555
+ referrerPolicy: "no-referrer",
5556
+ body: JSON.stringify({})
5557
+ // Empty body as no parameters needed
5558
+ });
5559
+ console.log(
5560
+ `[PRACTITIONER_SERVICE] Received response ${response.status}: ${response.statusText}`
5561
+ );
5562
+ if (!response.ok) {
5563
+ const errorText = await response.text();
5564
+ console.error(
5565
+ `[PRACTITIONER_SERVICE] Error response details: ${errorText}`
5566
+ );
5567
+ throw new Error(
5568
+ `Failed to ensure free consultation infrastructure: ${response.status} ${response.statusText} - ${errorText}`
5569
+ );
5570
+ }
5571
+ const result = await response.json();
5572
+ console.log(
5573
+ `[PRACTITIONER_SERVICE] Infrastructure check response:`,
5574
+ result
5575
+ );
5576
+ if (!result.success) {
5577
+ throw new Error(
5578
+ result.error || "Failed to ensure free consultation infrastructure"
5579
+ );
5580
+ }
5581
+ console.log(
5582
+ `[PRACTITIONER_SERVICE] Free consultation infrastructure ensured successfully`
5583
+ );
5584
+ return result.infrastructureExists;
5585
+ } catch (error) {
5586
+ console.error(
5587
+ "[PRACTITIONER_SERVICE] Error ensuring free consultation infrastructure:",
5588
+ error
5589
+ );
5590
+ throw error;
5591
+ }
5592
+ }
5509
5593
  /**
5510
5594
  * Disables free consultation for a practitioner in a specific clinic
5511
5595
  * Finds and deactivates the existing free consultation procedure
@@ -5531,7 +5615,7 @@ var PractitionerService = class extends BaseService {
5531
5615
  practitionerId
5532
5616
  );
5533
5617
  const freeConsultation = existingProcedures.find(
5534
- (procedure) => procedure.name === "Free Consultation" && procedure.clinicBranchId === clinicId && procedure.isActive
5618
+ (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId && procedure.isActive
5535
5619
  );
5536
5620
  if (!freeConsultation) {
5537
5621
  console.log(
@@ -8986,6 +9070,20 @@ var ProcedureService = class extends BaseService {
8986
9070
  const snapshot = await getDocs16(q);
8987
9071
  return snapshot.docs.map((doc36) => doc36.data());
8988
9072
  }
9073
+ /**
9074
+ * Gets all inactive procedures for a practitioner
9075
+ * @param practitionerId - The ID of the practitioner
9076
+ * @returns List of inactive procedures
9077
+ */
9078
+ async getInactiveProceduresByPractitioner(practitionerId) {
9079
+ const q = query16(
9080
+ collection16(this.db, PROCEDURES_COLLECTION),
9081
+ where16("practitionerId", "==", practitionerId),
9082
+ where16("isActive", "==", false)
9083
+ );
9084
+ const snapshot = await getDocs16(q);
9085
+ return snapshot.docs.map((doc36) => doc36.data());
9086
+ }
8989
9087
  /**
8990
9088
  * Updates a procedure
8991
9089
  * @param id - The ID of the procedure to update
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.7.29",
4
+ "version": "1.7.31",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -139,3 +139,6 @@ export * from "./aggregation/reviews/reviews.aggregation.service";
139
139
  // Re-export types that Cloud Functions might need direct access to
140
140
  export * from "../types/appointment";
141
141
  // Add other types as needed
142
+
143
+ // Export free consultation utilities
144
+ export * from "./free-consultation/free-consultation-utils.admin";
@@ -1217,6 +1217,9 @@ export class PractitionerService extends BaseService {
1217
1217
  clinicId: string
1218
1218
  ): Promise<void> {
1219
1219
  try {
1220
+ // First, ensure the free consultation infrastructure exists
1221
+ await this.ensureFreeConsultationInfrastructure();
1222
+
1220
1223
  // Validate that practitioner exists and is active
1221
1224
  const practitioner = await this.getPractitioner(practitionerId);
1222
1225
  if (!practitioner) {
@@ -1243,23 +1246,41 @@ export class PractitionerService extends BaseService {
1243
1246
  );
1244
1247
  }
1245
1248
 
1246
- // Check if free consultation already exists for this practitioner in this clinic
1247
- const existingProcedures =
1248
- await this.getProcedureService().getProceduresByPractitioner(
1249
+ // Get all procedures for this practitioner (including inactive ones)
1250
+ const [activeProcedures, inactiveProcedures] = await Promise.all([
1251
+ this.getProcedureService().getProceduresByPractitioner(practitionerId),
1252
+ this.getProcedureService().getInactiveProceduresByPractitioner(
1249
1253
  practitionerId
1250
- );
1251
- const existingConsultation = existingProcedures.find(
1254
+ ),
1255
+ ]);
1256
+
1257
+ // Combine active and inactive procedures
1258
+ const allProcedures = [...activeProcedures, ...inactiveProcedures];
1259
+
1260
+ // Check if free consultation already exists (active or inactive)
1261
+ const existingConsultation = allProcedures.find(
1252
1262
  (procedure) =>
1253
- procedure.name === "Free Consultation" &&
1254
- procedure.clinicBranchId === clinicId &&
1255
- procedure.isActive
1263
+ procedure.technology.id === "free-consultation-tech" &&
1264
+ procedure.clinicBranchId === clinicId
1256
1265
  );
1257
1266
 
1258
1267
  if (existingConsultation) {
1259
- console.log(
1260
- `Free consultation already exists for practitioner ${practitionerId} in clinic ${clinicId}`
1261
- );
1262
- return;
1268
+ if (existingConsultation.isActive) {
1269
+ console.log(
1270
+ `Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
1271
+ );
1272
+ return;
1273
+ } else {
1274
+ // Reactivate the existing disabled consultation
1275
+ await this.getProcedureService().updateProcedure(
1276
+ existingConsultation.id,
1277
+ { isActive: true }
1278
+ );
1279
+ console.log(
1280
+ `Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
1281
+ );
1282
+ return;
1283
+ }
1263
1284
  }
1264
1285
 
1265
1286
  // Create procedure data for free consultation (without productId)
@@ -1297,6 +1318,91 @@ export class PractitionerService extends BaseService {
1297
1318
  }
1298
1319
  }
1299
1320
 
1321
+ /**
1322
+ * Ensures that the free consultation infrastructure exists by calling the Cloud Function
1323
+ * @returns Promise<boolean> - True if infrastructure exists or was created successfully
1324
+ */
1325
+ async ensureFreeConsultationInfrastructure(): Promise<boolean> {
1326
+ try {
1327
+ console.log(
1328
+ "[PRACTITIONER_SERVICE] Ensuring free consultation infrastructure via HTTP"
1329
+ );
1330
+
1331
+ // Check if user is authenticated
1332
+ const currentUser = this.auth.currentUser;
1333
+ if (!currentUser) {
1334
+ throw new Error(
1335
+ "User must be authenticated to ensure free consultation infrastructure"
1336
+ );
1337
+ }
1338
+
1339
+ // Construct the function URL for the Express app endpoint
1340
+ const functionUrl = `https://europe-west6-metaestetics.cloudfunctions.net/bookingApi/ensureFreeConsultationInfrastructure`;
1341
+
1342
+ // Get the authenticated user's ID token
1343
+ const idToken = await currentUser.getIdToken();
1344
+
1345
+ console.log(
1346
+ `[PRACTITIONER_SERVICE] Making fetch request to ${functionUrl}`
1347
+ );
1348
+
1349
+ // Make the HTTP request
1350
+ const response = await fetch(functionUrl, {
1351
+ method: "POST",
1352
+ mode: "cors",
1353
+ cache: "no-cache",
1354
+ credentials: "omit",
1355
+ headers: {
1356
+ "Content-Type": "application/json",
1357
+ Authorization: `Bearer ${idToken}`,
1358
+ },
1359
+ redirect: "follow",
1360
+ referrerPolicy: "no-referrer",
1361
+ body: JSON.stringify({}), // Empty body as no parameters needed
1362
+ });
1363
+
1364
+ console.log(
1365
+ `[PRACTITIONER_SERVICE] Received response ${response.status}: ${response.statusText}`
1366
+ );
1367
+
1368
+ // Check if the request was successful
1369
+ if (!response.ok) {
1370
+ const errorText = await response.text();
1371
+ console.error(
1372
+ `[PRACTITIONER_SERVICE] Error response details: ${errorText}`
1373
+ );
1374
+ throw new Error(
1375
+ `Failed to ensure free consultation infrastructure: ${response.status} ${response.statusText} - ${errorText}`
1376
+ );
1377
+ }
1378
+
1379
+ // Parse the response
1380
+ const result = await response.json();
1381
+ console.log(
1382
+ `[PRACTITIONER_SERVICE] Infrastructure check response:`,
1383
+ result
1384
+ );
1385
+
1386
+ if (!result.success) {
1387
+ throw new Error(
1388
+ result.error || "Failed to ensure free consultation infrastructure"
1389
+ );
1390
+ }
1391
+
1392
+ console.log(
1393
+ `[PRACTITIONER_SERVICE] Free consultation infrastructure ensured successfully`
1394
+ );
1395
+
1396
+ return result.infrastructureExists;
1397
+ } catch (error) {
1398
+ console.error(
1399
+ "[PRACTITIONER_SERVICE] Error ensuring free consultation infrastructure:",
1400
+ error
1401
+ );
1402
+ throw error;
1403
+ }
1404
+ }
1405
+
1300
1406
  /**
1301
1407
  * Disables free consultation for a practitioner in a specific clinic
1302
1408
  * Finds and deactivates the existing free consultation procedure
@@ -1328,13 +1434,14 @@ export class PractitionerService extends BaseService {
1328
1434
  }
1329
1435
 
1330
1436
  // Find the free consultation procedure for this practitioner in this clinic
1437
+ // Use the more specific search by technology ID instead of name
1331
1438
  const existingProcedures =
1332
1439
  await this.getProcedureService().getProceduresByPractitioner(
1333
1440
  practitionerId
1334
1441
  );
1335
1442
  const freeConsultation = existingProcedures.find(
1336
1443
  (procedure) =>
1337
- procedure.name === "Free Consultation" &&
1444
+ procedure.technology.id === "free-consultation-tech" &&
1338
1445
  procedure.clinicBranchId === clinicId &&
1339
1446
  procedure.isActive
1340
1447
  );
@@ -354,6 +354,23 @@ export class ProcedureService extends BaseService {
354
354
  return snapshot.docs.map((doc) => doc.data() as Procedure);
355
355
  }
356
356
 
357
+ /**
358
+ * Gets all inactive procedures for a practitioner
359
+ * @param practitionerId - The ID of the practitioner
360
+ * @returns List of inactive procedures
361
+ */
362
+ async getInactiveProceduresByPractitioner(
363
+ practitionerId: string
364
+ ): Promise<Procedure[]> {
365
+ const q = query(
366
+ collection(this.db, PROCEDURES_COLLECTION),
367
+ where("practitionerId", "==", practitionerId),
368
+ where("isActive", "==", false)
369
+ );
370
+ const snapshot = await getDocs(q);
371
+ return snapshot.docs.map((doc) => doc.data() as Procedure);
372
+ }
373
+
357
374
  /**
358
375
  * Updates a procedure
359
376
  * @param id - The ID of the procedure to update