@blackcode_sa/metaestetics-api 1.8.18 → 1.11.0

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
@@ -583,7 +583,8 @@ var createAppointmentSchema = import_zod3.z.object({
583
583
  currency: import_zod3.z.string().min(1, "Currency is required"),
584
584
  patientNotes: import_zod3.z.string().max(MAX_STRING_LENGTH, "Patient notes too long").nullable().optional(),
585
585
  initialStatus: appointmentStatusSchema,
586
- initialPaymentStatus: paymentStatusSchema.optional().default("unpaid" /* UNPAID */)
586
+ initialPaymentStatus: paymentStatusSchema.optional().default("unpaid" /* UNPAID */),
587
+ clinic_tz: import_zod3.z.string().min(1, "Timezone is required")
587
588
  }).refine((data) => data.appointmentEndTime > data.appointmentStartTime, {
588
589
  message: "Appointment end time must be after start time",
589
590
  path: ["appointmentEndTime"]
@@ -617,6 +618,7 @@ var updateAppointmentSchema = import_zod3.z.object({
617
618
  cost: import_zod3.z.number().min(0).optional(),
618
619
  clinicBranchId: import_zod3.z.string().min(MIN_STRING_LENGTH).optional(),
619
620
  practitionerId: import_zod3.z.string().min(MIN_STRING_LENGTH).optional(),
621
+ clinic_tz: import_zod3.z.string().min(MIN_STRING_LENGTH).optional(),
620
622
  linkedForms: import_zod3.z.union([import_zod3.z.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), import_zod3.z.any()]).optional(),
621
623
  media: import_zod3.z.union([
622
624
  import_zod3.z.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
@@ -3453,7 +3455,8 @@ var clinicLocationSchema = import_zod10.z.object({
3453
3455
  postalCode: import_zod10.z.string(),
3454
3456
  latitude: import_zod10.z.number().min(-90).max(90),
3455
3457
  longitude: import_zod10.z.number().min(-180).max(180),
3456
- geohash: import_zod10.z.string().nullable().optional()
3458
+ geohash: import_zod10.z.string().nullable().optional(),
3459
+ tz: import_zod10.z.string().nullable().optional()
3457
3460
  });
3458
3461
  var workingHoursTimeSchema = import_zod10.z.object({
3459
3462
  open: import_zod10.z.string(),
@@ -3607,6 +3610,7 @@ var createClinicGroupSchema = import_zod10.z.object({
3607
3610
  calendarSyncEnabled: import_zod10.z.boolean().optional(),
3608
3611
  autoConfirmAppointments: import_zod10.z.boolean().optional(),
3609
3612
  businessIdentificationNumber: import_zod10.z.string().optional().nullable(),
3613
+ tz: import_zod10.z.string().nullable().optional(),
3610
3614
  onboarding: import_zod10.z.object({
3611
3615
  completed: import_zod10.z.boolean().optional().default(false),
3612
3616
  step: import_zod10.z.number().optional().default(1)
@@ -8271,6 +8275,7 @@ var ClinicGroupService = class extends BaseService {
8271
8275
 
8272
8276
  // src/services/clinic/clinic.service.ts
8273
8277
  var import_firestore27 = require("firebase/firestore");
8278
+ var import_functions2 = require("firebase/functions");
8274
8279
  var import_geofire_common7 = require("geofire-common");
8275
8280
  var import_zod20 = require("zod");
8276
8281
 
@@ -8710,6 +8715,7 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
8710
8715
  var import_firestore26 = require("firebase/firestore");
8711
8716
  var import_geofire_common6 = require("geofire-common");
8712
8717
  async function getClinicsByFilters(db, filters) {
8718
+ var _a;
8713
8719
  try {
8714
8720
  console.log("[CLINIC_SERVICE] Starting clinic filtering with multiple strategies");
8715
8721
  if (filters.center && filters.radiusInKm) {
@@ -8724,10 +8730,53 @@ async function getClinicsByFilters(db, filters) {
8724
8730
  filters.radiusInKm = void 0;
8725
8731
  }
8726
8732
  }
8733
+ if (filters.center && filters.radiusInKm) {
8734
+ try {
8735
+ console.log("[CLINIC_SERVICE] Strategy 0: Geohash bounds prefilter");
8736
+ const bounds = (0, import_geofire_common6.geohashQueryBounds)(
8737
+ [filters.center.latitude, filters.center.longitude],
8738
+ (filters.radiusInKm || 0) * 1e3
8739
+ );
8740
+ const collected = [];
8741
+ for (const b of bounds) {
8742
+ const constraints = [
8743
+ (0, import_firestore26.where)("location.geohash", ">=", b[0]),
8744
+ (0, import_firestore26.where)("location.geohash", "<=", b[1]),
8745
+ (0, import_firestore26.where)("isActive", "==", (_a = filters.isActive) != null ? _a : true)
8746
+ ];
8747
+ if (filters.tags && filters.tags.length > 0) {
8748
+ constraints.push((0, import_firestore26.where)("tags", "array-contains", filters.tags[0]));
8749
+ }
8750
+ const q0 = (0, import_firestore26.query)((0, import_firestore26.collection)(db, CLINICS_COLLECTION), ...constraints);
8751
+ const snap = await (0, import_firestore26.getDocs)(q0);
8752
+ snap.docs.forEach((d) => collected.push({ ...d.data(), id: d.id }));
8753
+ }
8754
+ const uniqueMap = /* @__PURE__ */ new Map();
8755
+ for (const c of collected) {
8756
+ uniqueMap.set(c.id, c);
8757
+ }
8758
+ let clinics = Array.from(uniqueMap.values());
8759
+ clinics = applyInMemoryFilters(clinics, filters);
8760
+ const pageSize = filters.pagination || 5;
8761
+ let startIndex = 0;
8762
+ if (filters.lastDoc && typeof filters.lastDoc === "object" && filters.lastDoc.id) {
8763
+ const idx = clinics.findIndex((c) => c.id === filters.lastDoc.id);
8764
+ if (idx >= 0) startIndex = idx + 1;
8765
+ }
8766
+ const page = clinics.slice(startIndex, startIndex + pageSize);
8767
+ const newLastDoc = page.length === pageSize ? page[page.length - 1] : null;
8768
+ console.log(
8769
+ `[CLINIC_SERVICE] Strategy 0 success: ${page.length} clinics (of ${clinics.length})`
8770
+ );
8771
+ return { clinics: page, lastDoc: newLastDoc };
8772
+ } catch (geoErr) {
8773
+ console.log("[CLINIC_SERVICE] Strategy 0 failed:", geoErr);
8774
+ }
8775
+ }
8727
8776
  const getBaseConstraints = () => {
8728
- var _a;
8777
+ var _a2;
8729
8778
  const constraints = [];
8730
- constraints.push((0, import_firestore26.where)("isActive", "==", (_a = filters.isActive) != null ? _a : true));
8779
+ constraints.push((0, import_firestore26.where)("isActive", "==", (_a2 = filters.isActive) != null ? _a2 : true));
8731
8780
  if (filters.tags && filters.tags.length > 0) {
8732
8781
  constraints.push((0, import_firestore26.where)("tags", "array-contains", filters.tags[0]));
8733
8782
  }
@@ -8804,7 +8853,9 @@ async function getClinicsByFilters(db, filters) {
8804
8853
  }
8805
8854
  }
8806
8855
  try {
8807
- console.log("[CLINIC_SERVICE] Strategy 3: Using createdAt ordering with client-side filtering");
8856
+ console.log(
8857
+ "[CLINIC_SERVICE] Strategy 3: Using createdAt ordering with client-side filtering"
8858
+ );
8808
8859
  const constraints = getBaseConstraints();
8809
8860
  constraints.push((0, import_firestore26.orderBy)("createdAt", "desc"));
8810
8861
  if (filters.lastDoc) {
@@ -8859,20 +8910,24 @@ async function getClinicsByFilters(db, filters) {
8859
8910
  }
8860
8911
  function applyInMemoryFilters(clinics, filters) {
8861
8912
  let filteredClinics = [...clinics];
8862
- console.log(`[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`);
8913
+ console.log(
8914
+ `[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`
8915
+ );
8863
8916
  if (filters.tags && filters.tags.length > 1) {
8864
8917
  const initialCount = filteredClinics.length;
8865
8918
  filteredClinics = filteredClinics.filter(
8866
8919
  (clinic) => filters.tags.every((tag) => clinic.tags.includes(tag))
8867
8920
  );
8868
- console.log(`[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8921
+ console.log(
8922
+ `[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`
8923
+ );
8869
8924
  }
8870
8925
  if (filters.tags && filters.tags.length === 1) {
8871
8926
  const initialCount = filteredClinics.length;
8872
- filteredClinics = filteredClinics.filter(
8873
- (clinic) => clinic.tags.includes(filters.tags[0])
8927
+ filteredClinics = filteredClinics.filter((clinic) => clinic.tags.includes(filters.tags[0]));
8928
+ console.log(
8929
+ `[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`
8874
8930
  );
8875
- console.log(`[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8876
8931
  }
8877
8932
  if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
8878
8933
  const initialCount = filteredClinics.length;
@@ -8883,7 +8938,9 @@ function applyInMemoryFilters(clinics, filters) {
8883
8938
  if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
8884
8939
  return true;
8885
8940
  });
8886
- console.log(`[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`);
8941
+ console.log(
8942
+ `[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`
8943
+ );
8887
8944
  }
8888
8945
  if (filters.nameSearch && filters.nameSearch.trim()) {
8889
8946
  const initialCount = filteredClinics.length;
@@ -8893,7 +8950,9 @@ function applyInMemoryFilters(clinics, filters) {
8893
8950
  const nameLower = clinic.nameLower || "";
8894
8951
  return name.includes(searchTerm) || nameLower.includes(searchTerm);
8895
8952
  });
8896
- console.log(`[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8953
+ console.log(
8954
+ `[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`
8955
+ );
8897
8956
  }
8898
8957
  if (filters.procedureFamily) {
8899
8958
  const initialCount = filteredClinics.length;
@@ -8901,7 +8960,9 @@ function applyInMemoryFilters(clinics, filters) {
8901
8960
  const proceduresInfo = clinic.proceduresInfo || [];
8902
8961
  return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
8903
8962
  });
8904
- console.log(`[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8963
+ console.log(
8964
+ `[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`
8965
+ );
8905
8966
  }
8906
8967
  if (filters.procedureCategory) {
8907
8968
  const initialCount = filteredClinics.length;
@@ -8909,7 +8970,9 @@ function applyInMemoryFilters(clinics, filters) {
8909
8970
  const proceduresInfo = clinic.proceduresInfo || [];
8910
8971
  return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
8911
8972
  });
8912
- console.log(`[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8973
+ console.log(
8974
+ `[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`
8975
+ );
8913
8976
  }
8914
8977
  if (filters.procedureSubcategory) {
8915
8978
  const initialCount = filteredClinics.length;
@@ -8917,7 +8980,9 @@ function applyInMemoryFilters(clinics, filters) {
8917
8980
  const proceduresInfo = clinic.proceduresInfo || [];
8918
8981
  return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
8919
8982
  });
8920
- console.log(`[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8983
+ console.log(
8984
+ `[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`
8985
+ );
8921
8986
  }
8922
8987
  if (filters.procedureTechnology) {
8923
8988
  const initialCount = filteredClinics.length;
@@ -8925,7 +8990,9 @@ function applyInMemoryFilters(clinics, filters) {
8925
8990
  const proceduresInfo = clinic.proceduresInfo || [];
8926
8991
  return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
8927
8992
  });
8928
- console.log(`[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8993
+ console.log(
8994
+ `[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`
8995
+ );
8929
8996
  }
8930
8997
  if (filters.center && filters.radiusInKm) {
8931
8998
  const initialCount = filteredClinics.length;
@@ -8939,11 +9006,15 @@ function applyInMemoryFilters(clinics, filters) {
8939
9006
  [filters.center.latitude, filters.center.longitude],
8940
9007
  [clinic.location.latitude, clinic.location.longitude]
8941
9008
  ) / 1e3;
8942
- console.log(`[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`);
9009
+ console.log(
9010
+ `[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`
9011
+ );
8943
9012
  clinic.distance = distance;
8944
9013
  return distance <= filters.radiusInKm;
8945
9014
  });
8946
- console.log(`[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`);
9015
+ console.log(
9016
+ `[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`
9017
+ );
8947
9018
  filteredClinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
8948
9019
  }
8949
9020
  console.log(`[CLINIC_SERVICE] Final filtered result: ${filteredClinics.length} clinics`);
@@ -8957,6 +9028,27 @@ var ClinicService = class extends BaseService {
8957
9028
  this.clinicAdminService = clinicAdminService;
8958
9029
  this.clinicGroupService = clinicGroupService;
8959
9030
  this.mediaService = mediaService;
9031
+ this.functions = (0, import_functions2.getFunctions)(app);
9032
+ }
9033
+ /**
9034
+ * Get timezone from coordinates using the callable function
9035
+ * @param lat Latitude
9036
+ * @param lng Longitude
9037
+ * @returns IANA timezone string
9038
+ */
9039
+ async getTimezone(lat, lng) {
9040
+ try {
9041
+ const getTimezoneFromCoordinates = (0, import_functions2.httpsCallable)(
9042
+ this.functions,
9043
+ "getTimezoneFromCoordinates"
9044
+ );
9045
+ const result = await getTimezoneFromCoordinates({ lat, lng });
9046
+ const data = result.data;
9047
+ return data.timezone;
9048
+ } catch (error) {
9049
+ console.error("Error getting timezone:", error);
9050
+ return null;
9051
+ }
8960
9052
  }
8961
9053
  /**
8962
9054
  * Process media resource (string URL or File object)
@@ -9069,6 +9161,7 @@ var ClinicService = class extends BaseService {
9069
9161
  );
9070
9162
  const location = validatedData.location;
9071
9163
  const hash = (0, import_geofire_common7.geohashForLocation)([location.latitude, location.longitude]);
9164
+ const tz = await this.getTimezone(location.latitude, location.longitude);
9072
9165
  const defaultReviewInfo = {
9073
9166
  totalReviews: 0,
9074
9167
  averageRating: 0,
@@ -9086,7 +9179,7 @@ var ClinicService = class extends BaseService {
9086
9179
  nameLower: validatedData.name.toLowerCase(),
9087
9180
  // Add this line
9088
9181
  description: validatedData.description,
9089
- location: { ...location, geohash: hash },
9182
+ location: { ...location, geohash: hash, tz },
9090
9183
  contactInfo: validatedData.contactInfo,
9091
9184
  workingHours: validatedData.workingHours,
9092
9185
  tags: validatedData.tags,
@@ -9190,9 +9283,11 @@ var ClinicService = class extends BaseService {
9190
9283
  }
9191
9284
  if (validatedData.location) {
9192
9285
  const loc = validatedData.location;
9286
+ const tz = await this.getTimezone(loc.latitude, loc.longitude);
9193
9287
  updatePayload.location = {
9194
9288
  ...loc,
9195
- geohash: (0, import_geofire_common7.geohashForLocation)([loc.latitude, loc.longitude])
9289
+ geohash: (0, import_geofire_common7.geohashForLocation)([loc.latitude, loc.longitude]),
9290
+ tz
9196
9291
  };
9197
9292
  }
9198
9293
  updatePayload.updatedAt = (0, import_firestore27.serverTimestamp)();
@@ -14565,7 +14660,8 @@ var import_firestore45 = require("firebase/firestore");
14565
14660
  var import_zod24 = require("zod");
14566
14661
  var createProcedureSchema = import_zod24.z.object({
14567
14662
  name: import_zod24.z.string().min(1).max(200),
14568
- nameLower: import_zod24.z.string().min(1).max(200),
14663
+ // Optional: service will derive from name if not provided by client
14664
+ nameLower: import_zod24.z.string().min(1).max(200).optional(),
14569
14665
  description: import_zod24.z.string().min(1).max(2e3),
14570
14666
  family: import_zod24.z.nativeEnum(ProcedureFamily),
14571
14667
  categoryId: import_zod24.z.string().min(1),
@@ -14658,9 +14754,7 @@ var ProcedureService = class extends BaseService {
14658
14754
  return media;
14659
14755
  }
14660
14756
  if (media instanceof File || media instanceof Blob) {
14661
- console.log(
14662
- `[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
14663
- );
14757
+ console.log(`[ProcedureService] Uploading ${collectionName} media for ${ownerId}`);
14664
14758
  const metadata = await this.mediaService.uploadMedia(
14665
14759
  media,
14666
14760
  ownerId,
@@ -14682,11 +14776,7 @@ var ProcedureService = class extends BaseService {
14682
14776
  if (!mediaArray || mediaArray.length === 0) return [];
14683
14777
  const result = [];
14684
14778
  for (const media of mediaArray) {
14685
- const processedUrl = await this.processMedia(
14686
- media,
14687
- ownerId,
14688
- collectionName
14689
- );
14779
+ const processedUrl = await this.processMedia(media, ownerId, collectionName);
14690
14780
  if (processedUrl) {
14691
14781
  result.push(processedUrl);
14692
14782
  }
@@ -14704,41 +14794,23 @@ var ProcedureService = class extends BaseService {
14704
14794
  const procedureId = this.generateId();
14705
14795
  const [category, subcategory, technology, product] = await Promise.all([
14706
14796
  this.categoryService.getById(validatedData.categoryId),
14707
- this.subcategoryService.getById(
14708
- validatedData.categoryId,
14709
- validatedData.subcategoryId
14710
- ),
14797
+ this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
14711
14798
  this.technologyService.getById(validatedData.technologyId),
14712
- this.productService.getById(
14713
- validatedData.technologyId,
14714
- validatedData.productId
14715
- )
14799
+ this.productService.getById(validatedData.technologyId, validatedData.productId)
14716
14800
  ]);
14717
14801
  if (!category || !subcategory || !technology || !product) {
14718
14802
  throw new Error("One or more required base entities not found");
14719
14803
  }
14720
- const clinicRef = (0, import_firestore45.doc)(
14721
- this.db,
14722
- CLINICS_COLLECTION,
14723
- validatedData.clinicBranchId
14724
- );
14804
+ const clinicRef = (0, import_firestore45.doc)(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId);
14725
14805
  const clinicSnapshot = await (0, import_firestore45.getDoc)(clinicRef);
14726
14806
  if (!clinicSnapshot.exists()) {
14727
- throw new Error(
14728
- `Clinic with ID ${validatedData.clinicBranchId} not found`
14729
- );
14807
+ throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
14730
14808
  }
14731
14809
  const clinic = clinicSnapshot.data();
14732
- const practitionerRef = (0, import_firestore45.doc)(
14733
- this.db,
14734
- PRACTITIONERS_COLLECTION,
14735
- validatedData.practitionerId
14736
- );
14810
+ const practitionerRef = (0, import_firestore45.doc)(this.db, PRACTITIONERS_COLLECTION, validatedData.practitionerId);
14737
14811
  const practitionerSnapshot = await (0, import_firestore45.getDoc)(practitionerRef);
14738
14812
  if (!practitionerSnapshot.exists()) {
14739
- throw new Error(
14740
- `Practitioner with ID ${validatedData.practitionerId} not found`
14741
- );
14813
+ throw new Error(`Practitioner with ID ${validatedData.practitionerId} not found`);
14742
14814
  }
14743
14815
  const practitioner = practitionerSnapshot.data();
14744
14816
  let processedPhotos = [];
@@ -14769,6 +14841,7 @@ var ProcedureService = class extends BaseService {
14769
14841
  const newProcedure = {
14770
14842
  id: procedureId,
14771
14843
  ...validatedData,
14844
+ // Ensure nameLower is always set even if omitted by client
14772
14845
  nameLower: validatedData.nameLower || validatedData.name.toLowerCase(),
14773
14846
  photos: processedPhotos,
14774
14847
  category,
@@ -14827,24 +14900,16 @@ var ProcedureService = class extends BaseService {
14827
14900
  const validatedData = createProcedureSchema.parse(validationData);
14828
14901
  const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
14829
14902
  this.categoryService.getById(validatedData.categoryId),
14830
- this.subcategoryService.getById(
14831
- validatedData.categoryId,
14832
- validatedData.subcategoryId
14833
- ),
14903
+ this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
14834
14904
  this.technologyService.getById(validatedData.technologyId),
14835
- this.productService.getById(
14836
- validatedData.technologyId,
14837
- validatedData.productId
14838
- ),
14905
+ this.productService.getById(validatedData.technologyId, validatedData.productId),
14839
14906
  (0, import_firestore45.getDoc)((0, import_firestore45.doc)(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
14840
14907
  ]);
14841
14908
  if (!category || !subcategory || !technology || !product) {
14842
14909
  throw new Error("One or more required base entities not found");
14843
14910
  }
14844
14911
  if (!clinicSnapshot.exists()) {
14845
- throw new Error(
14846
- `Clinic with ID ${validatedData.clinicBranchId} not found`
14847
- );
14912
+ throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
14848
14913
  }
14849
14914
  const clinic = clinicSnapshot.data();
14850
14915
  let processedPhotos = [];
@@ -14870,12 +14935,8 @@ var ProcedureService = class extends BaseService {
14870
14935
  }
14871
14936
  if (practitionersMap.size !== practitionerIds.length) {
14872
14937
  const foundIds = Array.from(practitionersMap.keys());
14873
- const notFoundIds = practitionerIds.filter(
14874
- (id) => !foundIds.includes(id)
14875
- );
14876
- throw new Error(
14877
- `The following practitioners were not found: ${notFoundIds.join(", ")}`
14878
- );
14938
+ const notFoundIds = practitionerIds.filter((id) => !foundIds.includes(id));
14939
+ throw new Error(`The following practitioners were not found: ${notFoundIds.join(", ")}`);
14879
14940
  }
14880
14941
  const batch = (0, import_firestore45.writeBatch)(this.db);
14881
14942
  const createdProcedureIds = [];
@@ -14943,10 +15004,7 @@ var ProcedureService = class extends BaseService {
14943
15004
  const fetchedProcedures = [];
14944
15005
  for (let i = 0; i < createdProcedureIds.length; i += 30) {
14945
15006
  const chunk = createdProcedureIds.slice(i, i + 30);
14946
- const q = (0, import_firestore45.query)(
14947
- (0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION),
14948
- (0, import_firestore45.where)((0, import_firestore45.documentId)(), "in", chunk)
14949
- );
15007
+ const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), (0, import_firestore45.where)((0, import_firestore45.documentId)(), "in", chunk));
14950
15008
  const snapshot = await (0, import_firestore45.getDocs)(q);
14951
15009
  snapshot.forEach((doc37) => {
14952
15010
  fetchedProcedures.push(doc37.data());
@@ -15047,9 +15105,7 @@ var ProcedureService = class extends BaseService {
15047
15105
  );
15048
15106
  const newPractitionerSnap = await (0, import_firestore45.getDoc)(newPractitionerRef);
15049
15107
  if (!newPractitionerSnap.exists())
15050
- throw new Error(
15051
- `New Practitioner ${validatedData.practitionerId} not found`
15052
- );
15108
+ throw new Error(`New Practitioner ${validatedData.practitionerId} not found`);
15053
15109
  newPractitioner = newPractitionerSnap.data();
15054
15110
  updatedProcedureData.doctorInfo = {
15055
15111
  id: newPractitioner.id,
@@ -15063,11 +15119,7 @@ var ProcedureService = class extends BaseService {
15063
15119
  }
15064
15120
  if (validatedData.clinicBranchId && validatedData.clinicBranchId !== oldClinicId) {
15065
15121
  clinicChanged = true;
15066
- const newClinicRef = (0, import_firestore45.doc)(
15067
- this.db,
15068
- CLINICS_COLLECTION,
15069
- validatedData.clinicBranchId
15070
- );
15122
+ const newClinicRef = (0, import_firestore45.doc)(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId);
15071
15123
  const newClinicSnap = await (0, import_firestore45.getDoc)(newClinicRef);
15072
15124
  if (!newClinicSnap.exists())
15073
15125
  throw new Error(`New Clinic ${validatedData.clinicBranchId} not found`);
@@ -15086,11 +15138,8 @@ var ProcedureService = class extends BaseService {
15086
15138
  updatedProcedureData.nameLower = validatedData.name.toLowerCase();
15087
15139
  }
15088
15140
  if (validatedData.categoryId) {
15089
- const category = await this.categoryService.getById(
15090
- validatedData.categoryId
15091
- );
15092
- if (!category)
15093
- throw new Error(`Category ${validatedData.categoryId} not found`);
15141
+ const category = await this.categoryService.getById(validatedData.categoryId);
15142
+ if (!category) throw new Error(`Category ${validatedData.categoryId} not found`);
15094
15143
  updatedProcedureData.category = category;
15095
15144
  finalCategoryId = category.id;
15096
15145
  }
@@ -15105,17 +15154,12 @@ var ProcedureService = class extends BaseService {
15105
15154
  );
15106
15155
  updatedProcedureData.subcategory = subcategory;
15107
15156
  } else if (validatedData.subcategoryId) {
15108
- console.warn(
15109
- "Attempted to update subcategory without a valid categoryId"
15110
- );
15157
+ console.warn("Attempted to update subcategory without a valid categoryId");
15111
15158
  }
15112
15159
  let finalTechnologyId = existingProcedure.technology.id;
15113
15160
  if (validatedData.technologyId) {
15114
- const technology = await this.technologyService.getById(
15115
- validatedData.technologyId
15116
- );
15117
- if (!technology)
15118
- throw new Error(`Technology ${validatedData.technologyId} not found`);
15161
+ const technology = await this.technologyService.getById(validatedData.technologyId);
15162
+ if (!technology) throw new Error(`Technology ${validatedData.technologyId} not found`);
15119
15163
  updatedProcedureData.technology = technology;
15120
15164
  finalTechnologyId = technology.id;
15121
15165
  updatedProcedureData.blockingConditions = technology.blockingConditions;
@@ -15126,10 +15170,7 @@ var ProcedureService = class extends BaseService {
15126
15170
  updatedProcedureData.documentationTemplates = technology.documentationTemplates || [];
15127
15171
  }
15128
15172
  if (validatedData.productId && finalTechnologyId) {
15129
- const product = await this.productService.getById(
15130
- finalTechnologyId,
15131
- validatedData.productId
15132
- );
15173
+ const product = await this.productService.getById(finalTechnologyId, validatedData.productId);
15133
15174
  if (!product)
15134
15175
  throw new Error(
15135
15176
  `Product ${validatedData.productId} not found for technology ${finalTechnologyId}`
@@ -15211,11 +15252,7 @@ var ProcedureService = class extends BaseService {
15211
15252
  limit16(pagination)
15212
15253
  );
15213
15254
  } else {
15214
- proceduresQuery = (0, import_firestore45.query)(
15215
- proceduresCollection,
15216
- (0, import_firestore45.orderBy)("name"),
15217
- limit16(pagination)
15218
- );
15255
+ proceduresQuery = (0, import_firestore45.query)(proceduresCollection, (0, import_firestore45.orderBy)("name"), limit16(pagination));
15219
15256
  }
15220
15257
  } else {
15221
15258
  proceduresQuery = (0, import_firestore45.query)(proceduresCollection, (0, import_firestore45.orderBy)("name"));
@@ -15338,7 +15375,9 @@ var ProcedureService = class extends BaseService {
15338
15375
  constraints.push((0, import_firestore45.limit)(filters.pagination || 10));
15339
15376
  const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15340
15377
  const querySnapshot = await (0, import_firestore45.getDocs)(q);
15341
- const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15378
+ const procedures = querySnapshot.docs.map(
15379
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15380
+ );
15342
15381
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15343
15382
  console.log(`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`);
15344
15383
  if (procedures.length < (filters.pagination || 10)) {
@@ -15369,7 +15408,9 @@ var ProcedureService = class extends BaseService {
15369
15408
  constraints.push((0, import_firestore45.limit)(filters.pagination || 10));
15370
15409
  const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15371
15410
  const querySnapshot = await (0, import_firestore45.getDocs)(q);
15372
- const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15411
+ const procedures = querySnapshot.docs.map(
15412
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15413
+ );
15373
15414
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15374
15415
  console.log(`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`);
15375
15416
  if (procedures.length < (filters.pagination || 10)) {
@@ -15381,7 +15422,9 @@ var ProcedureService = class extends BaseService {
15381
15422
  }
15382
15423
  }
15383
15424
  try {
15384
- console.log("[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering");
15425
+ console.log(
15426
+ "[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering"
15427
+ );
15385
15428
  const constraints = getBaseConstraints();
15386
15429
  constraints.push((0, import_firestore45.orderBy)("createdAt", "desc"));
15387
15430
  if (filters.lastDoc) {
@@ -15396,7 +15439,9 @@ var ProcedureService = class extends BaseService {
15396
15439
  constraints.push((0, import_firestore45.limit)(filters.pagination || 10));
15397
15440
  const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15398
15441
  const querySnapshot = await (0, import_firestore45.getDocs)(q);
15399
- let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15442
+ let procedures = querySnapshot.docs.map(
15443
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15444
+ );
15400
15445
  procedures = this.applyInMemoryFilters(procedures, filters);
15401
15446
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15402
15447
  console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
@@ -15416,7 +15461,9 @@ var ProcedureService = class extends BaseService {
15416
15461
  ];
15417
15462
  const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15418
15463
  const querySnapshot = await (0, import_firestore45.getDocs)(q);
15419
- let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15464
+ let procedures = querySnapshot.docs.map(
15465
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15466
+ );
15420
15467
  procedures = this.applyInMemoryFilters(procedures, filters);
15421
15468
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15422
15469
  console.log(`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`);
@@ -15456,7 +15503,9 @@ var ProcedureService = class extends BaseService {
15456
15503
  if (filters.maxPrice !== void 0 && price > filters.maxPrice) return false;
15457
15504
  return true;
15458
15505
  });
15459
- console.log(`[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`);
15506
+ console.log(
15507
+ `[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`
15508
+ );
15460
15509
  }
15461
15510
  if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
15462
15511
  filteredProcedures = filteredProcedures.filter((procedure) => {
@@ -15466,7 +15515,9 @@ var ProcedureService = class extends BaseService {
15466
15515
  if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
15467
15516
  return true;
15468
15517
  });
15469
- console.log(`[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`);
15518
+ console.log(
15519
+ `[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`
15520
+ );
15470
15521
  }
15471
15522
  if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15472
15523
  const benefitsToMatch = filters.treatmentBenefits;
@@ -15474,32 +15525,50 @@ var ProcedureService = class extends BaseService {
15474
15525
  const procedureBenefits = procedure.treatmentBenefits || [];
15475
15526
  return benefitsToMatch.some((benefit) => procedureBenefits.includes(benefit));
15476
15527
  });
15477
- console.log(`[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`);
15528
+ console.log(
15529
+ `[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`
15530
+ );
15478
15531
  }
15479
15532
  if (filters.procedureFamily) {
15480
- filteredProcedures = filteredProcedures.filter((procedure) => procedure.family === filters.procedureFamily);
15481
- console.log(`[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`);
15533
+ filteredProcedures = filteredProcedures.filter(
15534
+ (procedure) => procedure.family === filters.procedureFamily
15535
+ );
15536
+ console.log(
15537
+ `[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`
15538
+ );
15482
15539
  }
15483
15540
  if (filters.procedureCategory) {
15484
- filteredProcedures = filteredProcedures.filter((procedure) => {
15485
- var _a;
15486
- return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
15487
- });
15488
- console.log(`[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`);
15541
+ filteredProcedures = filteredProcedures.filter(
15542
+ (procedure) => {
15543
+ var _a;
15544
+ return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
15545
+ }
15546
+ );
15547
+ console.log(
15548
+ `[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`
15549
+ );
15489
15550
  }
15490
15551
  if (filters.procedureSubcategory) {
15491
- filteredProcedures = filteredProcedures.filter((procedure) => {
15492
- var _a;
15493
- return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
15494
- });
15495
- console.log(`[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`);
15552
+ filteredProcedures = filteredProcedures.filter(
15553
+ (procedure) => {
15554
+ var _a;
15555
+ return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
15556
+ }
15557
+ );
15558
+ console.log(
15559
+ `[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`
15560
+ );
15496
15561
  }
15497
15562
  if (filters.procedureTechnology) {
15498
- filteredProcedures = filteredProcedures.filter((procedure) => {
15499
- var _a;
15500
- return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
15501
- });
15502
- console.log(`[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`);
15563
+ filteredProcedures = filteredProcedures.filter(
15564
+ (procedure) => {
15565
+ var _a;
15566
+ return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
15567
+ }
15568
+ );
15569
+ console.log(
15570
+ `[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`
15571
+ );
15503
15572
  }
15504
15573
  if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
15505
15574
  const location = filters.location;
@@ -15523,29 +15592,48 @@ var ProcedureService = class extends BaseService {
15523
15592
  return filteredProcedures;
15524
15593
  }
15525
15594
  handleGeoQuery(filters) {
15526
- console.log("[PROCEDURE_SERVICE] Executing geo query with enhanced debugging");
15595
+ console.log("[PROCEDURE_SERVICE] Executing geo query with geohash bounds");
15527
15596
  try {
15528
15597
  const location = filters.location;
15529
15598
  const radiusInKm = filters.radiusInKm;
15530
- console.log("[PROCEDURE_SERVICE] Geo query parameters:", {
15531
- latitude: location.latitude,
15532
- longitude: location.longitude,
15533
- radiusInKm,
15534
- pagination: filters.pagination || 10
15599
+ if (!location || !radiusInKm) {
15600
+ return Promise.resolve({ procedures: [], lastDoc: null });
15601
+ }
15602
+ const bounds = (0, import_geofire_common8.geohashQueryBounds)([location.latitude, location.longitude], radiusInKm * 1e3);
15603
+ const fetches = bounds.map((b) => {
15604
+ const constraints = [
15605
+ (0, import_firestore45.where)("clinicInfo.location.geohash", ">=", b[0]),
15606
+ (0, import_firestore45.where)("clinicInfo.location.geohash", "<=", b[1]),
15607
+ (0, import_firestore45.where)("isActive", "==", filters.isActive !== void 0 ? filters.isActive : true)
15608
+ ];
15609
+ return (0, import_firestore45.getDocs)((0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints));
15535
15610
  });
15536
- const constraints = [
15537
- (0, import_firestore45.where)("isActive", "==", true),
15538
- (0, import_firestore45.orderBy)("createdAt", "desc"),
15539
- (0, import_firestore45.limit)((filters.pagination || 10) * 3)
15540
- // Get more results for geo filtering
15541
- ];
15542
- const q = (0, import_firestore45.query)((0, import_firestore45.collection)(this.db, PROCEDURES_COLLECTION), ...constraints);
15543
- return (0, import_firestore45.getDocs)(q).then((querySnapshot) => {
15544
- let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15611
+ return Promise.all(fetches).then((snaps) => {
15612
+ const collected = [];
15613
+ snaps.forEach((snap) => {
15614
+ snap.docs.forEach((d) => collected.push({ ...d.data(), id: d.id }));
15615
+ });
15616
+ const uniqueMap = /* @__PURE__ */ new Map();
15617
+ for (const p of collected) {
15618
+ uniqueMap.set(p.id, p);
15619
+ }
15620
+ let procedures = Array.from(uniqueMap.values());
15545
15621
  procedures = this.applyInMemoryFilters(procedures, filters);
15546
- console.log(`[PROCEDURE_SERVICE] Geo query success: ${procedures.length} procedures within ${radiusInKm}km`);
15547
- const lastDoc = procedures.length < (filters.pagination || 10) ? null : querySnapshot.docs[querySnapshot.docs.length - 1];
15548
- return { procedures, lastDoc };
15622
+ const pageSize = filters.pagination || 10;
15623
+ let startIndex = 0;
15624
+ if (filters.lastDoc && typeof filters.lastDoc === "object" && filters.lastDoc.id) {
15625
+ const idx = procedures.findIndex((p) => p.id === filters.lastDoc.id);
15626
+ if (idx >= 0) startIndex = idx + 1;
15627
+ }
15628
+ const page = procedures.slice(startIndex, startIndex + pageSize);
15629
+ const newLastDoc = page.length === pageSize ? page[page.length - 1] : null;
15630
+ console.log(
15631
+ `[PROCEDURE_SERVICE] Geo query success: ${page.length} (of ${procedures.length}) within ${radiusInKm}km`
15632
+ );
15633
+ return { procedures: page, lastDoc: newLastDoc };
15634
+ }).catch((err) => {
15635
+ console.error("[PROCEDURE_SERVICE] Geo bounds fetch failed:", err);
15636
+ return { procedures: [], lastDoc: null };
15549
15637
  });
15550
15638
  } catch (error) {
15551
15639
  console.error("[PROCEDURE_SERVICE] Geo query failed:", error);
@@ -15575,11 +15663,7 @@ var ProcedureService = class extends BaseService {
15575
15663
  throw new Error(`Clinic with ID ${data.clinicBranchId} not found`);
15576
15664
  }
15577
15665
  const clinic = clinicSnapshot.data();
15578
- const practitionerRef = (0, import_firestore45.doc)(
15579
- this.db,
15580
- PRACTITIONERS_COLLECTION,
15581
- data.practitionerId
15582
- );
15666
+ const practitionerRef = (0, import_firestore45.doc)(this.db, PRACTITIONERS_COLLECTION, data.practitionerId);
15583
15667
  const practitionerSnapshot = await (0, import_firestore45.getDoc)(practitionerRef);
15584
15668
  if (!practitionerSnapshot.exists()) {
15585
15669
  throw new Error(`Practitioner with ID ${data.practitionerId} not found`);
@@ -15587,11 +15671,7 @@ var ProcedureService = class extends BaseService {
15587
15671
  const practitioner = practitionerSnapshot.data();
15588
15672
  let processedPhotos = [];
15589
15673
  if (data.photos && data.photos.length > 0) {
15590
- processedPhotos = await this.processMediaArray(
15591
- data.photos,
15592
- procedureId,
15593
- "procedure-photos"
15594
- );
15674
+ processedPhotos = await this.processMediaArray(data.photos, procedureId, "procedure-photos");
15595
15675
  }
15596
15676
  const clinicInfo = {
15597
15677
  id: clinicSnapshot.id,
@@ -15895,7 +15975,7 @@ var import_auth9 = require("firebase/auth");
15895
15975
  var import_analytics = require("firebase/analytics");
15896
15976
  var import_react_native = require("react-native");
15897
15977
  var import_storage4 = require("firebase/storage");
15898
- var import_functions2 = require("firebase/functions");
15978
+ var import_functions3 = require("firebase/functions");
15899
15979
  var firebaseInstance = null;
15900
15980
  var initializeFirebase = (config) => {
15901
15981
  if (!firebaseInstance) {
@@ -15903,7 +15983,7 @@ var initializeFirebase = (config) => {
15903
15983
  const db = (0, import_firestore47.getFirestore)(app);
15904
15984
  const auth = (0, import_auth9.getAuth)(app);
15905
15985
  const storage = (0, import_storage4.getStorage)(app);
15906
- const functions = (0, import_functions2.getFunctions)(app);
15986
+ const functions = (0, import_functions3.getFunctions)(app);
15907
15987
  let analytics = null;
15908
15988
  if (typeof window !== "undefined" && import_react_native.Platform.OS === "web") {
15909
15989
  analytics = (0, import_analytics.getAnalytics)(app);