@blackcode_sa/metaestetics-api 1.5.25 → 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.mjs CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/config/firebase.ts
2
9
  import { initializeApp } from "firebase/app";
3
10
  import { getFirestore } from "firebase/firestore";
@@ -5270,6 +5277,143 @@ async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGr
5270
5277
  clinicGroupService
5271
5278
  );
5272
5279
  }
5280
+ async function getClinicById(db, clinicId) {
5281
+ try {
5282
+ const clinicRef = doc9(db, CLINICS_COLLECTION, clinicId);
5283
+ const clinicSnapshot = await getDoc12(clinicRef);
5284
+ if (!clinicSnapshot.exists()) {
5285
+ return null;
5286
+ }
5287
+ const clinicData = clinicSnapshot.data();
5288
+ return {
5289
+ ...clinicData,
5290
+ id: clinicSnapshot.id
5291
+ };
5292
+ } catch (error) {
5293
+ console.error("[CLINIC_UTILS] Error getting clinic by ID:", error);
5294
+ throw error;
5295
+ }
5296
+ }
5297
+ async function getAllClinics(db, pagination, lastDoc) {
5298
+ try {
5299
+ const clinicsCollection = collection7(db, CLINICS_COLLECTION);
5300
+ let clinicsQuery = query7(clinicsCollection);
5301
+ if (pagination && pagination > 0) {
5302
+ const { limit: limit4, startAfter: startAfter4 } = __require("firebase/firestore");
5303
+ if (lastDoc) {
5304
+ clinicsQuery = query7(
5305
+ clinicsCollection,
5306
+ startAfter4(lastDoc),
5307
+ limit4(pagination)
5308
+ );
5309
+ } else {
5310
+ clinicsQuery = query7(clinicsCollection, limit4(pagination));
5311
+ }
5312
+ }
5313
+ const clinicsSnapshot = await getDocs7(clinicsQuery);
5314
+ const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
5315
+ const clinics = clinicsSnapshot.docs.map((doc28) => {
5316
+ const data = doc28.data();
5317
+ return {
5318
+ ...data,
5319
+ id: doc28.id
5320
+ };
5321
+ });
5322
+ return {
5323
+ clinics,
5324
+ lastDoc: lastVisible
5325
+ };
5326
+ } catch (error) {
5327
+ console.error("[CLINIC_UTILS] Error getting all clinics:", error);
5328
+ throw error;
5329
+ }
5330
+ }
5331
+ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc, filters) {
5332
+ try {
5333
+ const { distanceBetween: distanceBetween2 } = __require("geofire-common");
5334
+ const centerLat = center.latitude;
5335
+ const centerLng = center.longitude;
5336
+ const clinicsCollection = collection7(db, CLINICS_COLLECTION);
5337
+ let clinicsQuery = query7(clinicsCollection);
5338
+ if ((filters == null ? void 0 : filters.isActive) !== void 0) {
5339
+ clinicsQuery = query7(
5340
+ clinicsCollection,
5341
+ where7("isActive", "==", filters.isActive)
5342
+ );
5343
+ }
5344
+ const querySnapshot = await getDocs7(clinicsQuery);
5345
+ console.log(
5346
+ `[CLINIC_UTILS] Found ${querySnapshot.docs.length} total clinics to filter by distance`
5347
+ );
5348
+ const filteredDocs = [];
5349
+ for (const doc28 of querySnapshot.docs) {
5350
+ const clinic = doc28.data();
5351
+ if (!clinic.location || !clinic.location.latitude || !clinic.location.longitude) {
5352
+ continue;
5353
+ }
5354
+ const distanceInM = distanceBetween2(
5355
+ [centerLat, centerLng],
5356
+ [clinic.location.latitude, clinic.location.longitude]
5357
+ );
5358
+ const distanceInKm = distanceInM / 1e3;
5359
+ if (distanceInKm <= rangeInKm) {
5360
+ if ((filters == null ? void 0 : filters.tags) && filters.tags.length > 0) {
5361
+ const hasAllTags = filters.tags.every(
5362
+ (filterTag) => clinic.tags.some(
5363
+ (clinicTag) => filterTag.id === clinicTag.id || filterTag.name === clinicTag.name
5364
+ )
5365
+ );
5366
+ if (hasAllTags) {
5367
+ filteredDocs.push({
5368
+ doc: doc28,
5369
+ distance: distanceInKm
5370
+ });
5371
+ }
5372
+ } else {
5373
+ filteredDocs.push({
5374
+ doc: doc28,
5375
+ distance: distanceInKm
5376
+ });
5377
+ }
5378
+ }
5379
+ }
5380
+ console.log(
5381
+ `[CLINIC_UTILS] Filtered to ${filteredDocs.length} clinics within ${rangeInKm}km`
5382
+ );
5383
+ filteredDocs.sort((a, b) => a.distance - b.distance);
5384
+ let paginatedDocs = filteredDocs;
5385
+ let lastVisible = null;
5386
+ if (pagination && pagination > 0) {
5387
+ let startIndex = 0;
5388
+ if (lastDoc) {
5389
+ const lastDocIndex = filteredDocs.findIndex(
5390
+ (item) => item.doc.id === lastDoc.id
5391
+ );
5392
+ if (lastDocIndex !== -1) {
5393
+ startIndex = lastDocIndex + 1;
5394
+ }
5395
+ }
5396
+ paginatedDocs = filteredDocs.slice(startIndex, startIndex + pagination);
5397
+ lastVisible = paginatedDocs.length > 0 ? paginatedDocs[paginatedDocs.length - 1].doc : null;
5398
+ }
5399
+ const clinics = paginatedDocs.map((item) => {
5400
+ const data = item.doc.data();
5401
+ return {
5402
+ ...data,
5403
+ id: item.doc.id,
5404
+ distance: item.distance
5405
+ // Include distance in response
5406
+ };
5407
+ });
5408
+ return {
5409
+ clinics,
5410
+ lastDoc: lastVisible
5411
+ };
5412
+ } catch (error) {
5413
+ console.error("[CLINIC_UTILS] Error getting clinics in range:", error);
5414
+ throw error;
5415
+ }
5416
+ }
5273
5417
 
5274
5418
  // src/services/clinic/utils/review.utils.ts
5275
5419
  import {
@@ -5614,6 +5758,45 @@ var ClinicService = class extends BaseService {
5614
5758
  });
5615
5759
  return clinic;
5616
5760
  }
5761
+ /**
5762
+ * Retrieves a clinic by its ID
5763
+ *
5764
+ * @param clinicId - ID of the clinic to retrieve
5765
+ * @returns The clinic if found, null otherwise
5766
+ */
5767
+ async getClinicById(clinicId) {
5768
+ return getClinicById(this.db, clinicId);
5769
+ }
5770
+ /**
5771
+ * Retrieves all clinics with optional pagination
5772
+ *
5773
+ * @param pagination - Optional number of clinics per page (0 or undefined returns all)
5774
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
5775
+ * @returns Array of clinics and the last document for pagination
5776
+ */
5777
+ async getAllClinics(pagination, lastDoc) {
5778
+ return getAllClinics(this.db, pagination, lastDoc);
5779
+ }
5780
+ /**
5781
+ * Retrieves all clinics within a specified range from a location with optional pagination
5782
+ *
5783
+ * @param center - The center location coordinates {latitude, longitude}
5784
+ * @param rangeInKm - The range in kilometers to search within
5785
+ * @param pagination - Optional number of clinics per page (0 or undefined returns all)
5786
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
5787
+ * @param filters - Optional filters to apply to the search (isActive, tags, etc.)
5788
+ * @returns Array of clinics with distance information and the last document for pagination
5789
+ */
5790
+ async getAllClinicsInRange(center, rangeInKm, pagination, lastDoc, filters) {
5791
+ return getAllClinicsInRange(
5792
+ this.db,
5793
+ center,
5794
+ rangeInKm,
5795
+ pagination,
5796
+ lastDoc,
5797
+ filters
5798
+ );
5799
+ }
5617
5800
  };
5618
5801
 
5619
5802
  // src/services/auth.service.ts
@@ -6480,7 +6663,8 @@ import {
6480
6663
  where as where11,
6481
6664
  updateDoc as updateDoc14,
6482
6665
  setDoc as setDoc13,
6483
- serverTimestamp as serverTimestamp13
6666
+ serverTimestamp as serverTimestamp13,
6667
+ writeBatch as writeBatch4
6484
6668
  } from "firebase/firestore";
6485
6669
 
6486
6670
  // src/types/procedure/index.ts
@@ -6536,6 +6720,10 @@ var procedureSchema = createProcedureSchema.extend({
6536
6720
  // We'll validate certification requirement separately
6537
6721
  documentationTemplates: z16.array(z16.any()),
6538
6722
  // We'll validate documentation templates separately
6723
+ clinicInfo: clinicInfoSchema,
6724
+ // Clinic info validation
6725
+ doctorInfo: doctorInfoSchema,
6726
+ // Doctor info validation
6539
6727
  isActive: z16.boolean(),
6540
6728
  createdAt: z16.date(),
6541
6729
  updatedAt: z16.date()
@@ -6556,6 +6744,7 @@ var ProcedureService = class extends BaseService {
6556
6744
  * @returns The created procedure
6557
6745
  */
6558
6746
  async createProcedure(data) {
6747
+ var _a;
6559
6748
  const validatedData = createProcedureSchema.parse(data);
6560
6749
  const [category, subcategory, technology, product] = await Promise.all([
6561
6750
  this.categoryService.getById(validatedData.categoryId),
@@ -6572,6 +6761,51 @@ var ProcedureService = class extends BaseService {
6572
6761
  if (!category || !subcategory || !technology || !product) {
6573
6762
  throw new Error("One or more required entities not found");
6574
6763
  }
6764
+ const clinicRef = doc13(
6765
+ this.db,
6766
+ CLINICS_COLLECTION,
6767
+ validatedData.clinicBranchId
6768
+ );
6769
+ const clinicSnapshot = await getDoc16(clinicRef);
6770
+ if (!clinicSnapshot.exists()) {
6771
+ throw new Error(
6772
+ `Clinic with ID ${validatedData.clinicBranchId} not found`
6773
+ );
6774
+ }
6775
+ const clinic = clinicSnapshot.data();
6776
+ const clinicInfo = {
6777
+ id: clinicSnapshot.id,
6778
+ name: clinic.name,
6779
+ description: clinic.description || "",
6780
+ featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? clinic.featuredPhotos[0] : clinic.coverPhoto || "",
6781
+ location: clinic.location,
6782
+ contactInfo: clinic.contactInfo
6783
+ };
6784
+ const practitionerRef = doc13(
6785
+ this.db,
6786
+ PRACTITIONERS_COLLECTION,
6787
+ validatedData.practitionerId
6788
+ );
6789
+ const practitionerSnapshot = await getDoc16(practitionerRef);
6790
+ if (!practitionerSnapshot.exists()) {
6791
+ throw new Error(
6792
+ `Practitioner with ID ${validatedData.practitionerId} not found`
6793
+ );
6794
+ }
6795
+ const practitioner = practitionerSnapshot.data();
6796
+ let doctorInfo = (_a = clinic.doctorsInfo) == null ? void 0 : _a.find(
6797
+ (doctor) => doctor.id === validatedData.practitionerId
6798
+ );
6799
+ if (!doctorInfo) {
6800
+ doctorInfo = {
6801
+ id: practitionerSnapshot.id,
6802
+ name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
6803
+ description: practitioner.basicInfo.bio || "",
6804
+ photo: practitioner.basicInfo.profileImageUrl || "",
6805
+ rating: 0,
6806
+ services: []
6807
+ };
6808
+ }
6575
6809
  const procedure = {
6576
6810
  ...validatedData,
6577
6811
  category,
@@ -6584,6 +6818,8 @@ var ProcedureService = class extends BaseService {
6584
6818
  postRequirements: technology.requirements.post,
6585
6819
  certificationRequirement: technology.certificationRequirement,
6586
6820
  documentationTemplates: technology.documentationTemplates || [],
6821
+ clinicInfo,
6822
+ doctorInfo,
6587
6823
  isActive: true,
6588
6824
  createdAt: /* @__PURE__ */ new Date(),
6589
6825
  updatedAt: /* @__PURE__ */ new Date()
@@ -6703,6 +6939,237 @@ var ProcedureService = class extends BaseService {
6703
6939
  subcategories
6704
6940
  };
6705
6941
  }
6942
+ /**
6943
+ * Gets all procedures with optional pagination
6944
+ *
6945
+ * @param pagination - Optional number of procedures per page (0 or undefined returns all)
6946
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
6947
+ * @returns Object containing procedures array and the last document for pagination
6948
+ */
6949
+ async getAllProcedures(pagination, lastDoc) {
6950
+ try {
6951
+ const proceduresCollection = collection12(this.db, PROCEDURES_COLLECTION);
6952
+ let proceduresQuery = query11(proceduresCollection);
6953
+ if (pagination && pagination > 0) {
6954
+ const { limit: limit4, startAfter: startAfter4 } = __require("firebase/firestore");
6955
+ if (lastDoc) {
6956
+ proceduresQuery = query11(
6957
+ proceduresCollection,
6958
+ startAfter4(lastDoc),
6959
+ limit4(pagination)
6960
+ );
6961
+ } else {
6962
+ proceduresQuery = query11(proceduresCollection, limit4(pagination));
6963
+ }
6964
+ }
6965
+ const proceduresSnapshot = await getDocs11(proceduresQuery);
6966
+ const lastVisible = proceduresSnapshot.docs[proceduresSnapshot.docs.length - 1];
6967
+ const procedures = proceduresSnapshot.docs.map((doc28) => {
6968
+ const data = doc28.data();
6969
+ if (!data.clinicInfo || !data.doctorInfo) {
6970
+ console.warn(
6971
+ `Procedure ${data.id} is missing clinicInfo or doctorInfo fields. These should be updated.`
6972
+ );
6973
+ }
6974
+ return {
6975
+ ...data,
6976
+ id: doc28.id
6977
+ };
6978
+ });
6979
+ return {
6980
+ procedures,
6981
+ lastDoc: lastVisible
6982
+ };
6983
+ } catch (error) {
6984
+ console.error("[PROCEDURE_SERVICE] Error getting all procedures:", error);
6985
+ throw error;
6986
+ }
6987
+ }
6988
+ /**
6989
+ * Updates the clinicInfo and doctorInfo fields in procedures when the source data changes
6990
+ *
6991
+ * @param entityType - The type of entity that changed ("clinic" or "doctor")
6992
+ * @param entityId - The ID of the entity that changed
6993
+ * @param updatedData - The updated data for the entity
6994
+ * @returns Number of procedures updated
6995
+ */
6996
+ async updateProcedureAggregateData(entityType, entityId, updatedData) {
6997
+ let proceduresQuery;
6998
+ const updatedField = entityType === "clinic" ? "clinicInfo" : "doctorInfo";
6999
+ if (entityType === "clinic") {
7000
+ proceduresQuery = query11(
7001
+ collection12(this.db, PROCEDURES_COLLECTION),
7002
+ where11("clinicBranchId", "==", entityId)
7003
+ );
7004
+ } else {
7005
+ proceduresQuery = query11(
7006
+ collection12(this.db, PROCEDURES_COLLECTION),
7007
+ where11("practitionerId", "==", entityId)
7008
+ );
7009
+ }
7010
+ const snapshot = await getDocs11(proceduresQuery);
7011
+ if (snapshot.empty) {
7012
+ return 0;
7013
+ }
7014
+ let updatedFieldData;
7015
+ if (entityType === "clinic") {
7016
+ updatedFieldData = {
7017
+ id: entityId,
7018
+ name: updatedData.name,
7019
+ description: updatedData.description || "",
7020
+ featuredPhoto: updatedData.featuredPhotos && updatedData.featuredPhotos.length > 0 ? updatedData.featuredPhotos[0] : updatedData.coverPhoto || "",
7021
+ location: updatedData.location,
7022
+ contactInfo: updatedData.contactInfo
7023
+ };
7024
+ } else {
7025
+ if (updatedData.basicInfo) {
7026
+ updatedFieldData = {
7027
+ id: entityId,
7028
+ name: `${updatedData.basicInfo.firstName} ${updatedData.basicInfo.lastName}`,
7029
+ description: updatedData.basicInfo.bio || "",
7030
+ photo: updatedData.basicInfo.profileImageUrl || "",
7031
+ rating: 0,
7032
+ // This would need to be calculated or passed in
7033
+ services: []
7034
+ // This would need to be determined or passed in
7035
+ };
7036
+ } else {
7037
+ updatedFieldData = {
7038
+ id: entityId,
7039
+ name: updatedData.name,
7040
+ description: updatedData.description || "",
7041
+ photo: updatedData.photo || "",
7042
+ rating: updatedData.rating || 0,
7043
+ services: updatedData.services || []
7044
+ };
7045
+ }
7046
+ }
7047
+ const batch = writeBatch4(this.db);
7048
+ snapshot.docs.forEach((doc28) => {
7049
+ batch.update(doc28.ref, {
7050
+ [updatedField]: updatedFieldData,
7051
+ updatedAt: serverTimestamp13()
7052
+ });
7053
+ });
7054
+ await batch.commit();
7055
+ return snapshot.size;
7056
+ }
7057
+ /**
7058
+ * Updates the clinicInfo for all procedures associated with a specific clinic
7059
+ *
7060
+ * @param clinicId - The ID of the clinic that was updated
7061
+ * @param clinicData - The updated clinic data
7062
+ * @returns Number of procedures updated
7063
+ */
7064
+ async updateClinicInfoInProcedures(clinicId, clinicData) {
7065
+ return this.updateProcedureAggregateData("clinic", clinicId, clinicData);
7066
+ }
7067
+ /**
7068
+ * Updates the doctorInfo for all procedures associated with a specific practitioner
7069
+ *
7070
+ * @param practitionerId - The ID of the practitioner that was updated
7071
+ * @param practitionerData - The updated practitioner data
7072
+ * @returns Number of procedures updated
7073
+ */
7074
+ async updateDoctorInfoInProcedures(practitionerId, practitionerData) {
7075
+ return this.updateProcedureAggregateData(
7076
+ "doctor",
7077
+ practitionerId,
7078
+ practitionerData
7079
+ );
7080
+ }
7081
+ /**
7082
+ * Updates all existing procedures to include clinicInfo and doctorInfo
7083
+ * This is a migration helper method that can be used to update existing procedures
7084
+ *
7085
+ * @returns Number of procedures updated
7086
+ */
7087
+ async migrateAllProceduresWithAggregateData() {
7088
+ var _a;
7089
+ const proceduresQuery = query11(collection12(this.db, PROCEDURES_COLLECTION));
7090
+ const proceduresSnapshot = await getDocs11(proceduresQuery);
7091
+ if (proceduresSnapshot.empty) {
7092
+ return 0;
7093
+ }
7094
+ let updatedCount = 0;
7095
+ const batch = writeBatch4(this.db);
7096
+ const batchLimit = 500;
7097
+ let batchCount = 0;
7098
+ for (const procedureDoc of proceduresSnapshot.docs) {
7099
+ const procedure = procedureDoc.data();
7100
+ if (procedure.clinicInfo && procedure.doctorInfo) {
7101
+ continue;
7102
+ }
7103
+ try {
7104
+ const clinicRef = doc13(
7105
+ this.db,
7106
+ CLINICS_COLLECTION,
7107
+ procedure.clinicBranchId
7108
+ );
7109
+ const clinicSnapshot = await getDoc16(clinicRef);
7110
+ if (!clinicSnapshot.exists()) {
7111
+ console.warn(
7112
+ `Clinic ${procedure.clinicBranchId} not found for procedure ${procedure.id}`
7113
+ );
7114
+ continue;
7115
+ }
7116
+ const clinic = clinicSnapshot.data();
7117
+ const clinicInfo = {
7118
+ id: clinicSnapshot.id,
7119
+ name: clinic.name,
7120
+ description: clinic.description || "",
7121
+ featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? clinic.featuredPhotos[0] : clinic.coverPhoto || "",
7122
+ location: clinic.location,
7123
+ contactInfo: clinic.contactInfo
7124
+ };
7125
+ const practitionerRef = doc13(
7126
+ this.db,
7127
+ PRACTITIONERS_COLLECTION,
7128
+ procedure.practitionerId
7129
+ );
7130
+ const practitionerSnapshot = await getDoc16(practitionerRef);
7131
+ if (!practitionerSnapshot.exists()) {
7132
+ console.warn(
7133
+ `Practitioner ${procedure.practitionerId} not found for procedure ${procedure.id}`
7134
+ );
7135
+ continue;
7136
+ }
7137
+ const practitioner = practitionerSnapshot.data();
7138
+ let doctorInfo = (_a = clinic.doctorsInfo) == null ? void 0 : _a.find(
7139
+ (doctor) => doctor.id === procedure.practitionerId
7140
+ );
7141
+ if (!doctorInfo) {
7142
+ doctorInfo = {
7143
+ id: practitionerSnapshot.id,
7144
+ name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
7145
+ description: practitioner.basicInfo.bio || "",
7146
+ photo: practitioner.basicInfo.profileImageUrl || "",
7147
+ rating: 0,
7148
+ services: []
7149
+ };
7150
+ }
7151
+ batch.update(procedureDoc.ref, {
7152
+ clinicInfo,
7153
+ doctorInfo,
7154
+ updatedAt: serverTimestamp13()
7155
+ });
7156
+ batchCount++;
7157
+ updatedCount++;
7158
+ if (batchCount >= batchLimit) {
7159
+ await batch.commit();
7160
+ console.log(`Committed batch of ${batchCount} procedure updates`);
7161
+ batchCount = 0;
7162
+ }
7163
+ } catch (error) {
7164
+ console.error(`Error updating procedure ${procedure.id}:`, error);
7165
+ }
7166
+ }
7167
+ if (batchCount > 0) {
7168
+ await batch.commit();
7169
+ console.log(`Committed final batch of ${batchCount} procedure updates`);
7170
+ }
7171
+ return updatedCount;
7172
+ }
6706
7173
  };
6707
7174
 
6708
7175
  // src/services/documentation-templates/documentation-template.service.ts
@@ -9566,6 +10033,8 @@ var CalendarServiceV2 = class extends BaseService {
9566
10033
  const updateParams = {
9567
10034
  appointmentId,
9568
10035
  clinicId,
10036
+ eventTime: appointment.eventTime,
10037
+ description: appointment.description || "",
9569
10038
  doctorId: appointment.practitionerProfileId || "",
9570
10039
  patientId: appointment.patientProfileId || "",
9571
10040
  status
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.5.25",
4
+ "version": "1.5.28",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -1106,6 +1106,8 @@ export class CalendarServiceV2 extends BaseService {
1106
1106
  const updateParams: UpdateAppointmentParams = {
1107
1107
  appointmentId,
1108
1108
  clinicId,
1109
+ eventTime: appointment.eventTime,
1110
+ description: appointment.description || "",
1109
1111
  doctorId: appointment.practitionerProfileId || "",
1110
1112
  patientId: appointment.patientProfileId || "",
1111
1113
  status,
@@ -301,4 +301,58 @@ export class ClinicService extends BaseService {
301
301
  // Note: The createClinic method already adds the clinic to the admin's managed clinics
302
302
  return clinic;
303
303
  }
304
+
305
+ /**
306
+ * Retrieves a clinic by its ID
307
+ *
308
+ * @param clinicId - ID of the clinic to retrieve
309
+ * @returns The clinic if found, null otherwise
310
+ */
311
+ async getClinicById(clinicId: string): Promise<Clinic | null> {
312
+ return ClinicUtils.getClinicById(this.db, clinicId);
313
+ }
314
+
315
+ /**
316
+ * Retrieves all clinics with optional pagination
317
+ *
318
+ * @param pagination - Optional number of clinics per page (0 or undefined returns all)
319
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
320
+ * @returns Array of clinics and the last document for pagination
321
+ */
322
+ async getAllClinics(
323
+ pagination?: number,
324
+ lastDoc?: any
325
+ ): Promise<{ clinics: Clinic[]; lastDoc: any }> {
326
+ return ClinicUtils.getAllClinics(this.db, pagination, lastDoc);
327
+ }
328
+
329
+ /**
330
+ * Retrieves all clinics within a specified range from a location with optional pagination
331
+ *
332
+ * @param center - The center location coordinates {latitude, longitude}
333
+ * @param rangeInKm - The range in kilometers to search within
334
+ * @param pagination - Optional number of clinics per page (0 or undefined returns all)
335
+ * @param lastDoc - Optional last document for pagination (if continuing from a previous page)
336
+ * @param filters - Optional filters to apply to the search (isActive, tags, etc.)
337
+ * @returns Array of clinics with distance information and the last document for pagination
338
+ */
339
+ async getAllClinicsInRange(
340
+ center: { latitude: number; longitude: number },
341
+ rangeInKm: number,
342
+ pagination?: number,
343
+ lastDoc?: any,
344
+ filters?: {
345
+ isActive?: boolean;
346
+ tags?: ClinicTag[];
347
+ }
348
+ ): Promise<{ clinics: (Clinic & { distance: number })[]; lastDoc: any }> {
349
+ return ClinicUtils.getAllClinicsInRange(
350
+ this.db,
351
+ center,
352
+ rangeInKm,
353
+ pagination,
354
+ lastDoc,
355
+ filters
356
+ );
357
+ }
304
358
  }