@blackcode_sa/metaestetics-api 1.5.27 → 1.5.28

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/index.js CHANGED
@@ -5339,6 +5339,143 @@ async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGr
5339
5339
  clinicGroupService
5340
5340
  );
5341
5341
  }
5342
+ async function getClinicById(db, clinicId) {
5343
+ try {
5344
+ const clinicRef = (0, import_firestore16.doc)(db, CLINICS_COLLECTION, clinicId);
5345
+ const clinicSnapshot = await (0, import_firestore16.getDoc)(clinicRef);
5346
+ if (!clinicSnapshot.exists()) {
5347
+ return null;
5348
+ }
5349
+ const clinicData = clinicSnapshot.data();
5350
+ return {
5351
+ ...clinicData,
5352
+ id: clinicSnapshot.id
5353
+ };
5354
+ } catch (error) {
5355
+ console.error("[CLINIC_UTILS] Error getting clinic by ID:", error);
5356
+ throw error;
5357
+ }
5358
+ }
5359
+ async function getAllClinics(db, pagination, lastDoc) {
5360
+ try {
5361
+ const clinicsCollection = (0, import_firestore16.collection)(db, CLINICS_COLLECTION);
5362
+ let clinicsQuery = (0, import_firestore16.query)(clinicsCollection);
5363
+ if (pagination && pagination > 0) {
5364
+ const { limit: limit4, startAfter: startAfter4 } = require("firebase/firestore");
5365
+ if (lastDoc) {
5366
+ clinicsQuery = (0, import_firestore16.query)(
5367
+ clinicsCollection,
5368
+ startAfter4(lastDoc),
5369
+ limit4(pagination)
5370
+ );
5371
+ } else {
5372
+ clinicsQuery = (0, import_firestore16.query)(clinicsCollection, limit4(pagination));
5373
+ }
5374
+ }
5375
+ const clinicsSnapshot = await (0, import_firestore16.getDocs)(clinicsQuery);
5376
+ const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
5377
+ const clinics = clinicsSnapshot.docs.map((doc28) => {
5378
+ const data = doc28.data();
5379
+ return {
5380
+ ...data,
5381
+ id: doc28.id
5382
+ };
5383
+ });
5384
+ return {
5385
+ clinics,
5386
+ lastDoc: lastVisible
5387
+ };
5388
+ } catch (error) {
5389
+ console.error("[CLINIC_UTILS] Error getting all clinics:", error);
5390
+ throw error;
5391
+ }
5392
+ }
5393
+ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc, filters) {
5394
+ try {
5395
+ const { distanceBetween: distanceBetween2 } = require("geofire-common");
5396
+ const centerLat = center.latitude;
5397
+ const centerLng = center.longitude;
5398
+ const clinicsCollection = (0, import_firestore16.collection)(db, CLINICS_COLLECTION);
5399
+ let clinicsQuery = (0, import_firestore16.query)(clinicsCollection);
5400
+ if ((filters == null ? void 0 : filters.isActive) !== void 0) {
5401
+ clinicsQuery = (0, import_firestore16.query)(
5402
+ clinicsCollection,
5403
+ (0, import_firestore16.where)("isActive", "==", filters.isActive)
5404
+ );
5405
+ }
5406
+ const querySnapshot = await (0, import_firestore16.getDocs)(clinicsQuery);
5407
+ console.log(
5408
+ `[CLINIC_UTILS] Found ${querySnapshot.docs.length} total clinics to filter by distance`
5409
+ );
5410
+ const filteredDocs = [];
5411
+ for (const doc28 of querySnapshot.docs) {
5412
+ const clinic = doc28.data();
5413
+ if (!clinic.location || !clinic.location.latitude || !clinic.location.longitude) {
5414
+ continue;
5415
+ }
5416
+ const distanceInM = distanceBetween2(
5417
+ [centerLat, centerLng],
5418
+ [clinic.location.latitude, clinic.location.longitude]
5419
+ );
5420
+ const distanceInKm = distanceInM / 1e3;
5421
+ if (distanceInKm <= rangeInKm) {
5422
+ if ((filters == null ? void 0 : filters.tags) && filters.tags.length > 0) {
5423
+ const hasAllTags = filters.tags.every(
5424
+ (filterTag) => clinic.tags.some(
5425
+ (clinicTag) => filterTag.id === clinicTag.id || filterTag.name === clinicTag.name
5426
+ )
5427
+ );
5428
+ if (hasAllTags) {
5429
+ filteredDocs.push({
5430
+ doc: doc28,
5431
+ distance: distanceInKm
5432
+ });
5433
+ }
5434
+ } else {
5435
+ filteredDocs.push({
5436
+ doc: doc28,
5437
+ distance: distanceInKm
5438
+ });
5439
+ }
5440
+ }
5441
+ }
5442
+ console.log(
5443
+ `[CLINIC_UTILS] Filtered to ${filteredDocs.length} clinics within ${rangeInKm}km`
5444
+ );
5445
+ filteredDocs.sort((a, b) => a.distance - b.distance);
5446
+ let paginatedDocs = filteredDocs;
5447
+ let lastVisible = null;
5448
+ if (pagination && pagination > 0) {
5449
+ let startIndex = 0;
5450
+ if (lastDoc) {
5451
+ const lastDocIndex = filteredDocs.findIndex(
5452
+ (item) => item.doc.id === lastDoc.id
5453
+ );
5454
+ if (lastDocIndex !== -1) {
5455
+ startIndex = lastDocIndex + 1;
5456
+ }
5457
+ }
5458
+ paginatedDocs = filteredDocs.slice(startIndex, startIndex + pagination);
5459
+ lastVisible = paginatedDocs.length > 0 ? paginatedDocs[paginatedDocs.length - 1].doc : null;
5460
+ }
5461
+ const clinics = paginatedDocs.map((item) => {
5462
+ const data = item.doc.data();
5463
+ return {
5464
+ ...data,
5465
+ id: item.doc.id,
5466
+ distance: item.distance
5467
+ // Include distance in response
5468
+ };
5469
+ });
5470
+ return {
5471
+ clinics,
5472
+ lastDoc: lastVisible
5473
+ };
5474
+ } catch (error) {
5475
+ console.error("[CLINIC_UTILS] Error getting clinics in range:", error);
5476
+ throw error;
5477
+ }
5478
+ }
5342
5479
 
5343
5480
  // src/services/clinic/utils/review.utils.ts
5344
5481
  var import_firestore17 = require("firebase/firestore");
@@ -5672,6 +5809,45 @@ var ClinicService = class extends BaseService {
5672
5809
  });
5673
5810
  return clinic;
5674
5811
  }
5812
+ /**
5813
+ * Retrieves a clinic by its ID
5814
+ *
5815
+ * @param clinicId - ID of the clinic to retrieve
5816
+ * @returns The clinic if found, null otherwise
5817
+ */
5818
+ async getClinicById(clinicId) {
5819
+ return getClinicById(this.db, clinicId);
5820
+ }
5821
+ /**
5822
+ * Retrieves all clinics with optional pagination
5823
+ *
5824
+ * @param pagination - Optional number of clinics per page (0 or undefined returns all)
5825
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
5826
+ * @returns Array of clinics and the last document for pagination
5827
+ */
5828
+ async getAllClinics(pagination, lastDoc) {
5829
+ return getAllClinics(this.db, pagination, lastDoc);
5830
+ }
5831
+ /**
5832
+ * Retrieves all clinics within a specified range from a location with optional pagination
5833
+ *
5834
+ * @param center - The center location coordinates {latitude, longitude}
5835
+ * @param rangeInKm - The range in kilometers to search within
5836
+ * @param pagination - Optional number of clinics per page (0 or undefined returns all)
5837
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
5838
+ * @param filters - Optional filters to apply to the search (isActive, tags, etc.)
5839
+ * @returns Array of clinics with distance information and the last document for pagination
5840
+ */
5841
+ async getAllClinicsInRange(center, rangeInKm, pagination, lastDoc, filters) {
5842
+ return getAllClinicsInRange(
5843
+ this.db,
5844
+ center,
5845
+ rangeInKm,
5846
+ pagination,
5847
+ lastDoc,
5848
+ filters
5849
+ );
5850
+ }
5675
5851
  };
5676
5852
 
5677
5853
  // src/services/auth.service.ts
@@ -6571,6 +6747,10 @@ var procedureSchema = createProcedureSchema.extend({
6571
6747
  // We'll validate certification requirement separately
6572
6748
  documentationTemplates: import_zod16.z.array(import_zod16.z.any()),
6573
6749
  // We'll validate documentation templates separately
6750
+ clinicInfo: clinicInfoSchema,
6751
+ // Clinic info validation
6752
+ doctorInfo: doctorInfoSchema,
6753
+ // Doctor info validation
6574
6754
  isActive: import_zod16.z.boolean(),
6575
6755
  createdAt: import_zod16.z.date(),
6576
6756
  updatedAt: import_zod16.z.date()
@@ -6591,6 +6771,7 @@ var ProcedureService = class extends BaseService {
6591
6771
  * @returns The created procedure
6592
6772
  */
6593
6773
  async createProcedure(data) {
6774
+ var _a;
6594
6775
  const validatedData = createProcedureSchema.parse(data);
6595
6776
  const [category, subcategory, technology, product] = await Promise.all([
6596
6777
  this.categoryService.getById(validatedData.categoryId),
@@ -6607,6 +6788,51 @@ var ProcedureService = class extends BaseService {
6607
6788
  if (!category || !subcategory || !technology || !product) {
6608
6789
  throw new Error("One or more required entities not found");
6609
6790
  }
6791
+ const clinicRef = (0, import_firestore21.doc)(
6792
+ this.db,
6793
+ CLINICS_COLLECTION,
6794
+ validatedData.clinicBranchId
6795
+ );
6796
+ const clinicSnapshot = await (0, import_firestore21.getDoc)(clinicRef);
6797
+ if (!clinicSnapshot.exists()) {
6798
+ throw new Error(
6799
+ `Clinic with ID ${validatedData.clinicBranchId} not found`
6800
+ );
6801
+ }
6802
+ const clinic = clinicSnapshot.data();
6803
+ const clinicInfo = {
6804
+ id: clinicSnapshot.id,
6805
+ name: clinic.name,
6806
+ description: clinic.description || "",
6807
+ featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? clinic.featuredPhotos[0] : clinic.coverPhoto || "",
6808
+ location: clinic.location,
6809
+ contactInfo: clinic.contactInfo
6810
+ };
6811
+ const practitionerRef = (0, import_firestore21.doc)(
6812
+ this.db,
6813
+ PRACTITIONERS_COLLECTION,
6814
+ validatedData.practitionerId
6815
+ );
6816
+ const practitionerSnapshot = await (0, import_firestore21.getDoc)(practitionerRef);
6817
+ if (!practitionerSnapshot.exists()) {
6818
+ throw new Error(
6819
+ `Practitioner with ID ${validatedData.practitionerId} not found`
6820
+ );
6821
+ }
6822
+ const practitioner = practitionerSnapshot.data();
6823
+ let doctorInfo = (_a = clinic.doctorsInfo) == null ? void 0 : _a.find(
6824
+ (doctor) => doctor.id === validatedData.practitionerId
6825
+ );
6826
+ if (!doctorInfo) {
6827
+ doctorInfo = {
6828
+ id: practitionerSnapshot.id,
6829
+ name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
6830
+ description: practitioner.basicInfo.bio || "",
6831
+ photo: practitioner.basicInfo.profileImageUrl || "",
6832
+ rating: 0,
6833
+ services: []
6834
+ };
6835
+ }
6610
6836
  const procedure = {
6611
6837
  ...validatedData,
6612
6838
  category,
@@ -6619,6 +6845,8 @@ var ProcedureService = class extends BaseService {
6619
6845
  postRequirements: technology.requirements.post,
6620
6846
  certificationRequirement: technology.certificationRequirement,
6621
6847
  documentationTemplates: technology.documentationTemplates || [],
6848
+ clinicInfo,
6849
+ doctorInfo,
6622
6850
  isActive: true,
6623
6851
  createdAt: /* @__PURE__ */ new Date(),
6624
6852
  updatedAt: /* @__PURE__ */ new Date()
@@ -6738,6 +6966,237 @@ var ProcedureService = class extends BaseService {
6738
6966
  subcategories
6739
6967
  };
6740
6968
  }
6969
+ /**
6970
+ * Gets all procedures with optional pagination
6971
+ *
6972
+ * @param pagination - Optional number of procedures per page (0 or undefined returns all)
6973
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
6974
+ * @returns Object containing procedures array and the last document for pagination
6975
+ */
6976
+ async getAllProcedures(pagination, lastDoc) {
6977
+ try {
6978
+ const proceduresCollection = (0, import_firestore21.collection)(this.db, PROCEDURES_COLLECTION);
6979
+ let proceduresQuery = (0, import_firestore21.query)(proceduresCollection);
6980
+ if (pagination && pagination > 0) {
6981
+ const { limit: limit4, startAfter: startAfter4 } = require("firebase/firestore");
6982
+ if (lastDoc) {
6983
+ proceduresQuery = (0, import_firestore21.query)(
6984
+ proceduresCollection,
6985
+ startAfter4(lastDoc),
6986
+ limit4(pagination)
6987
+ );
6988
+ } else {
6989
+ proceduresQuery = (0, import_firestore21.query)(proceduresCollection, limit4(pagination));
6990
+ }
6991
+ }
6992
+ const proceduresSnapshot = await (0, import_firestore21.getDocs)(proceduresQuery);
6993
+ const lastVisible = proceduresSnapshot.docs[proceduresSnapshot.docs.length - 1];
6994
+ const procedures = proceduresSnapshot.docs.map((doc28) => {
6995
+ const data = doc28.data();
6996
+ if (!data.clinicInfo || !data.doctorInfo) {
6997
+ console.warn(
6998
+ `Procedure ${data.id} is missing clinicInfo or doctorInfo fields. These should be updated.`
6999
+ );
7000
+ }
7001
+ return {
7002
+ ...data,
7003
+ id: doc28.id
7004
+ };
7005
+ });
7006
+ return {
7007
+ procedures,
7008
+ lastDoc: lastVisible
7009
+ };
7010
+ } catch (error) {
7011
+ console.error("[PROCEDURE_SERVICE] Error getting all procedures:", error);
7012
+ throw error;
7013
+ }
7014
+ }
7015
+ /**
7016
+ * Updates the clinicInfo and doctorInfo fields in procedures when the source data changes
7017
+ *
7018
+ * @param entityType - The type of entity that changed ("clinic" or "doctor")
7019
+ * @param entityId - The ID of the entity that changed
7020
+ * @param updatedData - The updated data for the entity
7021
+ * @returns Number of procedures updated
7022
+ */
7023
+ async updateProcedureAggregateData(entityType, entityId, updatedData) {
7024
+ let proceduresQuery;
7025
+ const updatedField = entityType === "clinic" ? "clinicInfo" : "doctorInfo";
7026
+ if (entityType === "clinic") {
7027
+ proceduresQuery = (0, import_firestore21.query)(
7028
+ (0, import_firestore21.collection)(this.db, PROCEDURES_COLLECTION),
7029
+ (0, import_firestore21.where)("clinicBranchId", "==", entityId)
7030
+ );
7031
+ } else {
7032
+ proceduresQuery = (0, import_firestore21.query)(
7033
+ (0, import_firestore21.collection)(this.db, PROCEDURES_COLLECTION),
7034
+ (0, import_firestore21.where)("practitionerId", "==", entityId)
7035
+ );
7036
+ }
7037
+ const snapshot = await (0, import_firestore21.getDocs)(proceduresQuery);
7038
+ if (snapshot.empty) {
7039
+ return 0;
7040
+ }
7041
+ let updatedFieldData;
7042
+ if (entityType === "clinic") {
7043
+ updatedFieldData = {
7044
+ id: entityId,
7045
+ name: updatedData.name,
7046
+ description: updatedData.description || "",
7047
+ featuredPhoto: updatedData.featuredPhotos && updatedData.featuredPhotos.length > 0 ? updatedData.featuredPhotos[0] : updatedData.coverPhoto || "",
7048
+ location: updatedData.location,
7049
+ contactInfo: updatedData.contactInfo
7050
+ };
7051
+ } else {
7052
+ if (updatedData.basicInfo) {
7053
+ updatedFieldData = {
7054
+ id: entityId,
7055
+ name: `${updatedData.basicInfo.firstName} ${updatedData.basicInfo.lastName}`,
7056
+ description: updatedData.basicInfo.bio || "",
7057
+ photo: updatedData.basicInfo.profileImageUrl || "",
7058
+ rating: 0,
7059
+ // This would need to be calculated or passed in
7060
+ services: []
7061
+ // This would need to be determined or passed in
7062
+ };
7063
+ } else {
7064
+ updatedFieldData = {
7065
+ id: entityId,
7066
+ name: updatedData.name,
7067
+ description: updatedData.description || "",
7068
+ photo: updatedData.photo || "",
7069
+ rating: updatedData.rating || 0,
7070
+ services: updatedData.services || []
7071
+ };
7072
+ }
7073
+ }
7074
+ const batch = (0, import_firestore21.writeBatch)(this.db);
7075
+ snapshot.docs.forEach((doc28) => {
7076
+ batch.update(doc28.ref, {
7077
+ [updatedField]: updatedFieldData,
7078
+ updatedAt: (0, import_firestore21.serverTimestamp)()
7079
+ });
7080
+ });
7081
+ await batch.commit();
7082
+ return snapshot.size;
7083
+ }
7084
+ /**
7085
+ * Updates the clinicInfo for all procedures associated with a specific clinic
7086
+ *
7087
+ * @param clinicId - The ID of the clinic that was updated
7088
+ * @param clinicData - The updated clinic data
7089
+ * @returns Number of procedures updated
7090
+ */
7091
+ async updateClinicInfoInProcedures(clinicId, clinicData) {
7092
+ return this.updateProcedureAggregateData("clinic", clinicId, clinicData);
7093
+ }
7094
+ /**
7095
+ * Updates the doctorInfo for all procedures associated with a specific practitioner
7096
+ *
7097
+ * @param practitionerId - The ID of the practitioner that was updated
7098
+ * @param practitionerData - The updated practitioner data
7099
+ * @returns Number of procedures updated
7100
+ */
7101
+ async updateDoctorInfoInProcedures(practitionerId, practitionerData) {
7102
+ return this.updateProcedureAggregateData(
7103
+ "doctor",
7104
+ practitionerId,
7105
+ practitionerData
7106
+ );
7107
+ }
7108
+ /**
7109
+ * Updates all existing procedures to include clinicInfo and doctorInfo
7110
+ * This is a migration helper method that can be used to update existing procedures
7111
+ *
7112
+ * @returns Number of procedures updated
7113
+ */
7114
+ async migrateAllProceduresWithAggregateData() {
7115
+ var _a;
7116
+ const proceduresQuery = (0, import_firestore21.query)((0, import_firestore21.collection)(this.db, PROCEDURES_COLLECTION));
7117
+ const proceduresSnapshot = await (0, import_firestore21.getDocs)(proceduresQuery);
7118
+ if (proceduresSnapshot.empty) {
7119
+ return 0;
7120
+ }
7121
+ let updatedCount = 0;
7122
+ const batch = (0, import_firestore21.writeBatch)(this.db);
7123
+ const batchLimit = 500;
7124
+ let batchCount = 0;
7125
+ for (const procedureDoc of proceduresSnapshot.docs) {
7126
+ const procedure = procedureDoc.data();
7127
+ if (procedure.clinicInfo && procedure.doctorInfo) {
7128
+ continue;
7129
+ }
7130
+ try {
7131
+ const clinicRef = (0, import_firestore21.doc)(
7132
+ this.db,
7133
+ CLINICS_COLLECTION,
7134
+ procedure.clinicBranchId
7135
+ );
7136
+ const clinicSnapshot = await (0, import_firestore21.getDoc)(clinicRef);
7137
+ if (!clinicSnapshot.exists()) {
7138
+ console.warn(
7139
+ `Clinic ${procedure.clinicBranchId} not found for procedure ${procedure.id}`
7140
+ );
7141
+ continue;
7142
+ }
7143
+ const clinic = clinicSnapshot.data();
7144
+ const clinicInfo = {
7145
+ id: clinicSnapshot.id,
7146
+ name: clinic.name,
7147
+ description: clinic.description || "",
7148
+ featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? clinic.featuredPhotos[0] : clinic.coverPhoto || "",
7149
+ location: clinic.location,
7150
+ contactInfo: clinic.contactInfo
7151
+ };
7152
+ const practitionerRef = (0, import_firestore21.doc)(
7153
+ this.db,
7154
+ PRACTITIONERS_COLLECTION,
7155
+ procedure.practitionerId
7156
+ );
7157
+ const practitionerSnapshot = await (0, import_firestore21.getDoc)(practitionerRef);
7158
+ if (!practitionerSnapshot.exists()) {
7159
+ console.warn(
7160
+ `Practitioner ${procedure.practitionerId} not found for procedure ${procedure.id}`
7161
+ );
7162
+ continue;
7163
+ }
7164
+ const practitioner = practitionerSnapshot.data();
7165
+ let doctorInfo = (_a = clinic.doctorsInfo) == null ? void 0 : _a.find(
7166
+ (doctor) => doctor.id === procedure.practitionerId
7167
+ );
7168
+ if (!doctorInfo) {
7169
+ doctorInfo = {
7170
+ id: practitionerSnapshot.id,
7171
+ name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
7172
+ description: practitioner.basicInfo.bio || "",
7173
+ photo: practitioner.basicInfo.profileImageUrl || "",
7174
+ rating: 0,
7175
+ services: []
7176
+ };
7177
+ }
7178
+ batch.update(procedureDoc.ref, {
7179
+ clinicInfo,
7180
+ doctorInfo,
7181
+ updatedAt: (0, import_firestore21.serverTimestamp)()
7182
+ });
7183
+ batchCount++;
7184
+ updatedCount++;
7185
+ if (batchCount >= batchLimit) {
7186
+ await batch.commit();
7187
+ console.log(`Committed batch of ${batchCount} procedure updates`);
7188
+ batchCount = 0;
7189
+ }
7190
+ } catch (error) {
7191
+ console.error(`Error updating procedure ${procedure.id}:`, error);
7192
+ }
7193
+ }
7194
+ if (batchCount > 0) {
7195
+ await batch.commit();
7196
+ console.log(`Committed final batch of ${batchCount} procedure updates`);
7197
+ }
7198
+ return updatedCount;
7199
+ }
6741
7200
  };
6742
7201
 
6743
7202
  // src/services/documentation-templates/documentation-template.service.ts
@@ -9507,7 +9966,7 @@ var CalendarServiceV2 = class extends BaseService {
9507
9966
  appointmentId,
9508
9967
  clinicId,
9509
9968
  eventTime: appointment.eventTime,
9510
- description: appointment.description,
9969
+ description: appointment.description || "",
9511
9970
  doctorId: appointment.practitionerProfileId || "",
9512
9971
  patientId: appointment.patientProfileId || "",
9513
9972
  status