@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.mjs CHANGED
@@ -455,7 +455,8 @@ var createAppointmentSchema = z3.object({
455
455
  currency: z3.string().min(1, "Currency is required"),
456
456
  patientNotes: z3.string().max(MAX_STRING_LENGTH, "Patient notes too long").nullable().optional(),
457
457
  initialStatus: appointmentStatusSchema,
458
- initialPaymentStatus: paymentStatusSchema.optional().default("unpaid" /* UNPAID */)
458
+ initialPaymentStatus: paymentStatusSchema.optional().default("unpaid" /* UNPAID */),
459
+ clinic_tz: z3.string().min(1, "Timezone is required")
459
460
  }).refine((data) => data.appointmentEndTime > data.appointmentStartTime, {
460
461
  message: "Appointment end time must be after start time",
461
462
  path: ["appointmentEndTime"]
@@ -489,6 +490,7 @@ var updateAppointmentSchema = z3.object({
489
490
  cost: z3.number().min(0).optional(),
490
491
  clinicBranchId: z3.string().min(MIN_STRING_LENGTH).optional(),
491
492
  practitionerId: z3.string().min(MIN_STRING_LENGTH).optional(),
493
+ clinic_tz: z3.string().min(MIN_STRING_LENGTH).optional(),
492
494
  linkedForms: z3.union([z3.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z3.any()]).optional(),
493
495
  media: z3.union([
494
496
  z3.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
@@ -3437,7 +3439,8 @@ var clinicLocationSchema = z10.object({
3437
3439
  postalCode: z10.string(),
3438
3440
  latitude: z10.number().min(-90).max(90),
3439
3441
  longitude: z10.number().min(-180).max(180),
3440
- geohash: z10.string().nullable().optional()
3442
+ geohash: z10.string().nullable().optional(),
3443
+ tz: z10.string().nullable().optional()
3441
3444
  });
3442
3445
  var workingHoursTimeSchema = z10.object({
3443
3446
  open: z10.string(),
@@ -3591,6 +3594,7 @@ var createClinicGroupSchema = z10.object({
3591
3594
  calendarSyncEnabled: z10.boolean().optional(),
3592
3595
  autoConfirmAppointments: z10.boolean().optional(),
3593
3596
  businessIdentificationNumber: z10.string().optional().nullable(),
3597
+ tz: z10.string().nullable().optional(),
3594
3598
  onboarding: z10.object({
3595
3599
  completed: z10.boolean().optional().default(false),
3596
3600
  step: z10.number().optional().default(1)
@@ -8342,6 +8346,7 @@ import {
8342
8346
  writeBatch as writeBatch4,
8343
8347
  arrayUnion as arrayUnion7
8344
8348
  } from "firebase/firestore";
8349
+ import { getFunctions as getFunctions2, httpsCallable as httpsCallable2 } from "firebase/functions";
8345
8350
  import {
8346
8351
  geohashForLocation as geohashForLocation4
8347
8352
  } from "geofire-common";
@@ -8810,8 +8815,9 @@ import {
8810
8815
  limit as limit9,
8811
8816
  orderBy as orderBy5
8812
8817
  } from "firebase/firestore";
8813
- import { distanceBetween as distanceBetween4 } from "geofire-common";
8818
+ import { geohashQueryBounds as geohashQueryBounds3, distanceBetween as distanceBetween4 } from "geofire-common";
8814
8819
  async function getClinicsByFilters(db, filters) {
8820
+ var _a;
8815
8821
  try {
8816
8822
  console.log("[CLINIC_SERVICE] Starting clinic filtering with multiple strategies");
8817
8823
  if (filters.center && filters.radiusInKm) {
@@ -8826,10 +8832,53 @@ async function getClinicsByFilters(db, filters) {
8826
8832
  filters.radiusInKm = void 0;
8827
8833
  }
8828
8834
  }
8835
+ if (filters.center && filters.radiusInKm) {
8836
+ try {
8837
+ console.log("[CLINIC_SERVICE] Strategy 0: Geohash bounds prefilter");
8838
+ const bounds = geohashQueryBounds3(
8839
+ [filters.center.latitude, filters.center.longitude],
8840
+ (filters.radiusInKm || 0) * 1e3
8841
+ );
8842
+ const collected = [];
8843
+ for (const b of bounds) {
8844
+ const constraints = [
8845
+ where15("location.geohash", ">=", b[0]),
8846
+ where15("location.geohash", "<=", b[1]),
8847
+ where15("isActive", "==", (_a = filters.isActive) != null ? _a : true)
8848
+ ];
8849
+ if (filters.tags && filters.tags.length > 0) {
8850
+ constraints.push(where15("tags", "array-contains", filters.tags[0]));
8851
+ }
8852
+ const q0 = query15(collection15(db, CLINICS_COLLECTION), ...constraints);
8853
+ const snap = await getDocs15(q0);
8854
+ snap.docs.forEach((d) => collected.push({ ...d.data(), id: d.id }));
8855
+ }
8856
+ const uniqueMap = /* @__PURE__ */ new Map();
8857
+ for (const c of collected) {
8858
+ uniqueMap.set(c.id, c);
8859
+ }
8860
+ let clinics = Array.from(uniqueMap.values());
8861
+ clinics = applyInMemoryFilters(clinics, filters);
8862
+ const pageSize = filters.pagination || 5;
8863
+ let startIndex = 0;
8864
+ if (filters.lastDoc && typeof filters.lastDoc === "object" && filters.lastDoc.id) {
8865
+ const idx = clinics.findIndex((c) => c.id === filters.lastDoc.id);
8866
+ if (idx >= 0) startIndex = idx + 1;
8867
+ }
8868
+ const page = clinics.slice(startIndex, startIndex + pageSize);
8869
+ const newLastDoc = page.length === pageSize ? page[page.length - 1] : null;
8870
+ console.log(
8871
+ `[CLINIC_SERVICE] Strategy 0 success: ${page.length} clinics (of ${clinics.length})`
8872
+ );
8873
+ return { clinics: page, lastDoc: newLastDoc };
8874
+ } catch (geoErr) {
8875
+ console.log("[CLINIC_SERVICE] Strategy 0 failed:", geoErr);
8876
+ }
8877
+ }
8829
8878
  const getBaseConstraints = () => {
8830
- var _a;
8879
+ var _a2;
8831
8880
  const constraints = [];
8832
- constraints.push(where15("isActive", "==", (_a = filters.isActive) != null ? _a : true));
8881
+ constraints.push(where15("isActive", "==", (_a2 = filters.isActive) != null ? _a2 : true));
8833
8882
  if (filters.tags && filters.tags.length > 0) {
8834
8883
  constraints.push(where15("tags", "array-contains", filters.tags[0]));
8835
8884
  }
@@ -8906,7 +8955,9 @@ async function getClinicsByFilters(db, filters) {
8906
8955
  }
8907
8956
  }
8908
8957
  try {
8909
- console.log("[CLINIC_SERVICE] Strategy 3: Using createdAt ordering with client-side filtering");
8958
+ console.log(
8959
+ "[CLINIC_SERVICE] Strategy 3: Using createdAt ordering with client-side filtering"
8960
+ );
8910
8961
  const constraints = getBaseConstraints();
8911
8962
  constraints.push(orderBy5("createdAt", "desc"));
8912
8963
  if (filters.lastDoc) {
@@ -8961,20 +9012,24 @@ async function getClinicsByFilters(db, filters) {
8961
9012
  }
8962
9013
  function applyInMemoryFilters(clinics, filters) {
8963
9014
  let filteredClinics = [...clinics];
8964
- console.log(`[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`);
9015
+ console.log(
9016
+ `[CLINIC_SERVICE] Applying in-memory filters - input: ${filteredClinics.length} clinics`
9017
+ );
8965
9018
  if (filters.tags && filters.tags.length > 1) {
8966
9019
  const initialCount = filteredClinics.length;
8967
9020
  filteredClinics = filteredClinics.filter(
8968
9021
  (clinic) => filters.tags.every((tag) => clinic.tags.includes(tag))
8969
9022
  );
8970
- console.log(`[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9023
+ console.log(
9024
+ `[CLINIC_SERVICE] Applied multi-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`
9025
+ );
8971
9026
  }
8972
9027
  if (filters.tags && filters.tags.length === 1) {
8973
9028
  const initialCount = filteredClinics.length;
8974
- filteredClinics = filteredClinics.filter(
8975
- (clinic) => clinic.tags.includes(filters.tags[0])
9029
+ filteredClinics = filteredClinics.filter((clinic) => clinic.tags.includes(filters.tags[0]));
9030
+ console.log(
9031
+ `[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`
8976
9032
  );
8977
- console.log(`[CLINIC_SERVICE] Applied single-tag filter: ${initialCount} \u2192 ${filteredClinics.length}`);
8978
9033
  }
8979
9034
  if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
8980
9035
  const initialCount = filteredClinics.length;
@@ -8985,7 +9040,9 @@ function applyInMemoryFilters(clinics, filters) {
8985
9040
  if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
8986
9041
  return true;
8987
9042
  });
8988
- console.log(`[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`);
9043
+ console.log(
9044
+ `[CLINIC_SERVICE] Applied rating filter (${filters.minRating}-${filters.maxRating}): ${initialCount} \u2192 ${filteredClinics.length}`
9045
+ );
8989
9046
  }
8990
9047
  if (filters.nameSearch && filters.nameSearch.trim()) {
8991
9048
  const initialCount = filteredClinics.length;
@@ -8995,7 +9052,9 @@ function applyInMemoryFilters(clinics, filters) {
8995
9052
  const nameLower = clinic.nameLower || "";
8996
9053
  return name.includes(searchTerm) || nameLower.includes(searchTerm);
8997
9054
  });
8998
- console.log(`[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9055
+ console.log(
9056
+ `[CLINIC_SERVICE] Applied name search filter: ${initialCount} \u2192 ${filteredClinics.length}`
9057
+ );
8999
9058
  }
9000
9059
  if (filters.procedureFamily) {
9001
9060
  const initialCount = filteredClinics.length;
@@ -9003,7 +9062,9 @@ function applyInMemoryFilters(clinics, filters) {
9003
9062
  const proceduresInfo = clinic.proceduresInfo || [];
9004
9063
  return proceduresInfo.some((proc) => proc.family === filters.procedureFamily);
9005
9064
  });
9006
- console.log(`[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9065
+ console.log(
9066
+ `[CLINIC_SERVICE] Applied procedure family filter: ${initialCount} \u2192 ${filteredClinics.length}`
9067
+ );
9007
9068
  }
9008
9069
  if (filters.procedureCategory) {
9009
9070
  const initialCount = filteredClinics.length;
@@ -9011,7 +9072,9 @@ function applyInMemoryFilters(clinics, filters) {
9011
9072
  const proceduresInfo = clinic.proceduresInfo || [];
9012
9073
  return proceduresInfo.some((proc) => proc.categoryName === filters.procedureCategory);
9013
9074
  });
9014
- console.log(`[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9075
+ console.log(
9076
+ `[CLINIC_SERVICE] Applied procedure category filter: ${initialCount} \u2192 ${filteredClinics.length}`
9077
+ );
9015
9078
  }
9016
9079
  if (filters.procedureSubcategory) {
9017
9080
  const initialCount = filteredClinics.length;
@@ -9019,7 +9082,9 @@ function applyInMemoryFilters(clinics, filters) {
9019
9082
  const proceduresInfo = clinic.proceduresInfo || [];
9020
9083
  return proceduresInfo.some((proc) => proc.subcategoryName === filters.procedureSubcategory);
9021
9084
  });
9022
- console.log(`[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9085
+ console.log(
9086
+ `[CLINIC_SERVICE] Applied procedure subcategory filter: ${initialCount} \u2192 ${filteredClinics.length}`
9087
+ );
9023
9088
  }
9024
9089
  if (filters.procedureTechnology) {
9025
9090
  const initialCount = filteredClinics.length;
@@ -9027,7 +9092,9 @@ function applyInMemoryFilters(clinics, filters) {
9027
9092
  const proceduresInfo = clinic.proceduresInfo || [];
9028
9093
  return proceduresInfo.some((proc) => proc.technologyName === filters.procedureTechnology);
9029
9094
  });
9030
- console.log(`[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`);
9095
+ console.log(
9096
+ `[CLINIC_SERVICE] Applied procedure technology filter: ${initialCount} \u2192 ${filteredClinics.length}`
9097
+ );
9031
9098
  }
9032
9099
  if (filters.center && filters.radiusInKm) {
9033
9100
  const initialCount = filteredClinics.length;
@@ -9041,11 +9108,15 @@ function applyInMemoryFilters(clinics, filters) {
9041
9108
  [filters.center.latitude, filters.center.longitude],
9042
9109
  [clinic.location.latitude, clinic.location.longitude]
9043
9110
  ) / 1e3;
9044
- console.log(`[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`);
9111
+ console.log(
9112
+ `[CLINIC_SERVICE] Clinic ${clinic.name}: distance ${distance.toFixed(2)}km (limit: ${filters.radiusInKm}km)`
9113
+ );
9045
9114
  clinic.distance = distance;
9046
9115
  return distance <= filters.radiusInKm;
9047
9116
  });
9048
- console.log(`[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`);
9117
+ console.log(
9118
+ `[CLINIC_SERVICE] Applied geo filter (${filters.radiusInKm}km): ${initialCount} \u2192 ${filteredClinics.length}`
9119
+ );
9049
9120
  filteredClinics.sort((a, b) => (a.distance || 0) - (b.distance || 0));
9050
9121
  }
9051
9122
  console.log(`[CLINIC_SERVICE] Final filtered result: ${filteredClinics.length} clinics`);
@@ -9059,6 +9130,27 @@ var ClinicService = class extends BaseService {
9059
9130
  this.clinicAdminService = clinicAdminService;
9060
9131
  this.clinicGroupService = clinicGroupService;
9061
9132
  this.mediaService = mediaService;
9133
+ this.functions = getFunctions2(app);
9134
+ }
9135
+ /**
9136
+ * Get timezone from coordinates using the callable function
9137
+ * @param lat Latitude
9138
+ * @param lng Longitude
9139
+ * @returns IANA timezone string
9140
+ */
9141
+ async getTimezone(lat, lng) {
9142
+ try {
9143
+ const getTimezoneFromCoordinates = httpsCallable2(
9144
+ this.functions,
9145
+ "getTimezoneFromCoordinates"
9146
+ );
9147
+ const result = await getTimezoneFromCoordinates({ lat, lng });
9148
+ const data = result.data;
9149
+ return data.timezone;
9150
+ } catch (error) {
9151
+ console.error("Error getting timezone:", error);
9152
+ return null;
9153
+ }
9062
9154
  }
9063
9155
  /**
9064
9156
  * Process media resource (string URL or File object)
@@ -9171,6 +9263,7 @@ var ClinicService = class extends BaseService {
9171
9263
  );
9172
9264
  const location = validatedData.location;
9173
9265
  const hash = geohashForLocation4([location.latitude, location.longitude]);
9266
+ const tz = await this.getTimezone(location.latitude, location.longitude);
9174
9267
  const defaultReviewInfo = {
9175
9268
  totalReviews: 0,
9176
9269
  averageRating: 0,
@@ -9188,7 +9281,7 @@ var ClinicService = class extends BaseService {
9188
9281
  nameLower: validatedData.name.toLowerCase(),
9189
9282
  // Add this line
9190
9283
  description: validatedData.description,
9191
- location: { ...location, geohash: hash },
9284
+ location: { ...location, geohash: hash, tz },
9192
9285
  contactInfo: validatedData.contactInfo,
9193
9286
  workingHours: validatedData.workingHours,
9194
9287
  tags: validatedData.tags,
@@ -9292,9 +9385,11 @@ var ClinicService = class extends BaseService {
9292
9385
  }
9293
9386
  if (validatedData.location) {
9294
9387
  const loc = validatedData.location;
9388
+ const tz = await this.getTimezone(loc.latitude, loc.longitude);
9295
9389
  updatePayload.location = {
9296
9390
  ...loc,
9297
- geohash: geohashForLocation4([loc.latitude, loc.longitude])
9391
+ geohash: geohashForLocation4([loc.latitude, loc.longitude]),
9392
+ tz
9298
9393
  };
9299
9394
  }
9300
9395
  updatePayload.updatedAt = serverTimestamp14();
@@ -14816,7 +14911,8 @@ import {
14816
14911
  import { z as z24 } from "zod";
14817
14912
  var createProcedureSchema = z24.object({
14818
14913
  name: z24.string().min(1).max(200),
14819
- nameLower: z24.string().min(1).max(200),
14914
+ // Optional: service will derive from name if not provided by client
14915
+ nameLower: z24.string().min(1).max(200).optional(),
14820
14916
  description: z24.string().min(1).max(2e3),
14821
14917
  family: z24.nativeEnum(ProcedureFamily),
14822
14918
  categoryId: z24.string().min(1),
@@ -14886,7 +14982,7 @@ var procedureSchema = createProcedureSchema.extend({
14886
14982
  });
14887
14983
 
14888
14984
  // src/services/procedure/procedure.service.ts
14889
- import { distanceBetween as distanceBetween6 } from "geofire-common";
14985
+ import { distanceBetween as distanceBetween6, geohashQueryBounds as geohashQueryBounds5 } from "geofire-common";
14890
14986
  var ProcedureService = class extends BaseService {
14891
14987
  constructor(db, auth, app, categoryService, subcategoryService, technologyService, productService, mediaService) {
14892
14988
  super(db, auth, app);
@@ -14909,9 +15005,7 @@ var ProcedureService = class extends BaseService {
14909
15005
  return media;
14910
15006
  }
14911
15007
  if (media instanceof File || media instanceof Blob) {
14912
- console.log(
14913
- `[ProcedureService] Uploading ${collectionName} media for ${ownerId}`
14914
- );
15008
+ console.log(`[ProcedureService] Uploading ${collectionName} media for ${ownerId}`);
14915
15009
  const metadata = await this.mediaService.uploadMedia(
14916
15010
  media,
14917
15011
  ownerId,
@@ -14933,11 +15027,7 @@ var ProcedureService = class extends BaseService {
14933
15027
  if (!mediaArray || mediaArray.length === 0) return [];
14934
15028
  const result = [];
14935
15029
  for (const media of mediaArray) {
14936
- const processedUrl = await this.processMedia(
14937
- media,
14938
- ownerId,
14939
- collectionName
14940
- );
15030
+ const processedUrl = await this.processMedia(media, ownerId, collectionName);
14941
15031
  if (processedUrl) {
14942
15032
  result.push(processedUrl);
14943
15033
  }
@@ -14955,41 +15045,23 @@ var ProcedureService = class extends BaseService {
14955
15045
  const procedureId = this.generateId();
14956
15046
  const [category, subcategory, technology, product] = await Promise.all([
14957
15047
  this.categoryService.getById(validatedData.categoryId),
14958
- this.subcategoryService.getById(
14959
- validatedData.categoryId,
14960
- validatedData.subcategoryId
14961
- ),
15048
+ this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
14962
15049
  this.technologyService.getById(validatedData.technologyId),
14963
- this.productService.getById(
14964
- validatedData.technologyId,
14965
- validatedData.productId
14966
- )
15050
+ this.productService.getById(validatedData.technologyId, validatedData.productId)
14967
15051
  ]);
14968
15052
  if (!category || !subcategory || !technology || !product) {
14969
15053
  throw new Error("One or more required base entities not found");
14970
15054
  }
14971
- const clinicRef = doc30(
14972
- this.db,
14973
- CLINICS_COLLECTION,
14974
- validatedData.clinicBranchId
14975
- );
15055
+ const clinicRef = doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId);
14976
15056
  const clinicSnapshot = await getDoc32(clinicRef);
14977
15057
  if (!clinicSnapshot.exists()) {
14978
- throw new Error(
14979
- `Clinic with ID ${validatedData.clinicBranchId} not found`
14980
- );
15058
+ throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
14981
15059
  }
14982
15060
  const clinic = clinicSnapshot.data();
14983
- const practitionerRef = doc30(
14984
- this.db,
14985
- PRACTITIONERS_COLLECTION,
14986
- validatedData.practitionerId
14987
- );
15061
+ const practitionerRef = doc30(this.db, PRACTITIONERS_COLLECTION, validatedData.practitionerId);
14988
15062
  const practitionerSnapshot = await getDoc32(practitionerRef);
14989
15063
  if (!practitionerSnapshot.exists()) {
14990
- throw new Error(
14991
- `Practitioner with ID ${validatedData.practitionerId} not found`
14992
- );
15064
+ throw new Error(`Practitioner with ID ${validatedData.practitionerId} not found`);
14993
15065
  }
14994
15066
  const practitioner = practitionerSnapshot.data();
14995
15067
  let processedPhotos = [];
@@ -15020,6 +15092,7 @@ var ProcedureService = class extends BaseService {
15020
15092
  const newProcedure = {
15021
15093
  id: procedureId,
15022
15094
  ...validatedData,
15095
+ // Ensure nameLower is always set even if omitted by client
15023
15096
  nameLower: validatedData.nameLower || validatedData.name.toLowerCase(),
15024
15097
  photos: processedPhotos,
15025
15098
  category,
@@ -15078,24 +15151,16 @@ var ProcedureService = class extends BaseService {
15078
15151
  const validatedData = createProcedureSchema.parse(validationData);
15079
15152
  const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
15080
15153
  this.categoryService.getById(validatedData.categoryId),
15081
- this.subcategoryService.getById(
15082
- validatedData.categoryId,
15083
- validatedData.subcategoryId
15084
- ),
15154
+ this.subcategoryService.getById(validatedData.categoryId, validatedData.subcategoryId),
15085
15155
  this.technologyService.getById(validatedData.technologyId),
15086
- this.productService.getById(
15087
- validatedData.technologyId,
15088
- validatedData.productId
15089
- ),
15156
+ this.productService.getById(validatedData.technologyId, validatedData.productId),
15090
15157
  getDoc32(doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
15091
15158
  ]);
15092
15159
  if (!category || !subcategory || !technology || !product) {
15093
15160
  throw new Error("One or more required base entities not found");
15094
15161
  }
15095
15162
  if (!clinicSnapshot.exists()) {
15096
- throw new Error(
15097
- `Clinic with ID ${validatedData.clinicBranchId} not found`
15098
- );
15163
+ throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
15099
15164
  }
15100
15165
  const clinic = clinicSnapshot.data();
15101
15166
  let processedPhotos = [];
@@ -15121,12 +15186,8 @@ var ProcedureService = class extends BaseService {
15121
15186
  }
15122
15187
  if (practitionersMap.size !== practitionerIds.length) {
15123
15188
  const foundIds = Array.from(practitionersMap.keys());
15124
- const notFoundIds = practitionerIds.filter(
15125
- (id) => !foundIds.includes(id)
15126
- );
15127
- throw new Error(
15128
- `The following practitioners were not found: ${notFoundIds.join(", ")}`
15129
- );
15189
+ const notFoundIds = practitionerIds.filter((id) => !foundIds.includes(id));
15190
+ throw new Error(`The following practitioners were not found: ${notFoundIds.join(", ")}`);
15130
15191
  }
15131
15192
  const batch = writeBatch6(this.db);
15132
15193
  const createdProcedureIds = [];
@@ -15194,10 +15255,7 @@ var ProcedureService = class extends BaseService {
15194
15255
  const fetchedProcedures = [];
15195
15256
  for (let i = 0; i < createdProcedureIds.length; i += 30) {
15196
15257
  const chunk = createdProcedureIds.slice(i, i + 30);
15197
- const q = query29(
15198
- collection29(this.db, PROCEDURES_COLLECTION),
15199
- where29(documentId2(), "in", chunk)
15200
- );
15258
+ const q = query29(collection29(this.db, PROCEDURES_COLLECTION), where29(documentId2(), "in", chunk));
15201
15259
  const snapshot = await getDocs29(q);
15202
15260
  snapshot.forEach((doc37) => {
15203
15261
  fetchedProcedures.push(doc37.data());
@@ -15298,9 +15356,7 @@ var ProcedureService = class extends BaseService {
15298
15356
  );
15299
15357
  const newPractitionerSnap = await getDoc32(newPractitionerRef);
15300
15358
  if (!newPractitionerSnap.exists())
15301
- throw new Error(
15302
- `New Practitioner ${validatedData.practitionerId} not found`
15303
- );
15359
+ throw new Error(`New Practitioner ${validatedData.practitionerId} not found`);
15304
15360
  newPractitioner = newPractitionerSnap.data();
15305
15361
  updatedProcedureData.doctorInfo = {
15306
15362
  id: newPractitioner.id,
@@ -15314,11 +15370,7 @@ var ProcedureService = class extends BaseService {
15314
15370
  }
15315
15371
  if (validatedData.clinicBranchId && validatedData.clinicBranchId !== oldClinicId) {
15316
15372
  clinicChanged = true;
15317
- const newClinicRef = doc30(
15318
- this.db,
15319
- CLINICS_COLLECTION,
15320
- validatedData.clinicBranchId
15321
- );
15373
+ const newClinicRef = doc30(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId);
15322
15374
  const newClinicSnap = await getDoc32(newClinicRef);
15323
15375
  if (!newClinicSnap.exists())
15324
15376
  throw new Error(`New Clinic ${validatedData.clinicBranchId} not found`);
@@ -15337,11 +15389,8 @@ var ProcedureService = class extends BaseService {
15337
15389
  updatedProcedureData.nameLower = validatedData.name.toLowerCase();
15338
15390
  }
15339
15391
  if (validatedData.categoryId) {
15340
- const category = await this.categoryService.getById(
15341
- validatedData.categoryId
15342
- );
15343
- if (!category)
15344
- throw new Error(`Category ${validatedData.categoryId} not found`);
15392
+ const category = await this.categoryService.getById(validatedData.categoryId);
15393
+ if (!category) throw new Error(`Category ${validatedData.categoryId} not found`);
15345
15394
  updatedProcedureData.category = category;
15346
15395
  finalCategoryId = category.id;
15347
15396
  }
@@ -15356,17 +15405,12 @@ var ProcedureService = class extends BaseService {
15356
15405
  );
15357
15406
  updatedProcedureData.subcategory = subcategory;
15358
15407
  } else if (validatedData.subcategoryId) {
15359
- console.warn(
15360
- "Attempted to update subcategory without a valid categoryId"
15361
- );
15408
+ console.warn("Attempted to update subcategory without a valid categoryId");
15362
15409
  }
15363
15410
  let finalTechnologyId = existingProcedure.technology.id;
15364
15411
  if (validatedData.technologyId) {
15365
- const technology = await this.technologyService.getById(
15366
- validatedData.technologyId
15367
- );
15368
- if (!technology)
15369
- throw new Error(`Technology ${validatedData.technologyId} not found`);
15412
+ const technology = await this.technologyService.getById(validatedData.technologyId);
15413
+ if (!technology) throw new Error(`Technology ${validatedData.technologyId} not found`);
15370
15414
  updatedProcedureData.technology = technology;
15371
15415
  finalTechnologyId = technology.id;
15372
15416
  updatedProcedureData.blockingConditions = technology.blockingConditions;
@@ -15377,10 +15421,7 @@ var ProcedureService = class extends BaseService {
15377
15421
  updatedProcedureData.documentationTemplates = technology.documentationTemplates || [];
15378
15422
  }
15379
15423
  if (validatedData.productId && finalTechnologyId) {
15380
- const product = await this.productService.getById(
15381
- finalTechnologyId,
15382
- validatedData.productId
15383
- );
15424
+ const product = await this.productService.getById(finalTechnologyId, validatedData.productId);
15384
15425
  if (!product)
15385
15426
  throw new Error(
15386
15427
  `Product ${validatedData.productId} not found for technology ${finalTechnologyId}`
@@ -15462,11 +15503,7 @@ var ProcedureService = class extends BaseService {
15462
15503
  limit16(pagination)
15463
15504
  );
15464
15505
  } else {
15465
- proceduresQuery = query29(
15466
- proceduresCollection,
15467
- orderBy17("name"),
15468
- limit16(pagination)
15469
- );
15506
+ proceduresQuery = query29(proceduresCollection, orderBy17("name"), limit16(pagination));
15470
15507
  }
15471
15508
  } else {
15472
15509
  proceduresQuery = query29(proceduresCollection, orderBy17("name"));
@@ -15589,7 +15626,9 @@ var ProcedureService = class extends BaseService {
15589
15626
  constraints.push(limit15(filters.pagination || 10));
15590
15627
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15591
15628
  const querySnapshot = await getDocs29(q);
15592
- const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15629
+ const procedures = querySnapshot.docs.map(
15630
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15631
+ );
15593
15632
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15594
15633
  console.log(`[PROCEDURE_SERVICE] Strategy 1 success: ${procedures.length} procedures`);
15595
15634
  if (procedures.length < (filters.pagination || 10)) {
@@ -15620,7 +15659,9 @@ var ProcedureService = class extends BaseService {
15620
15659
  constraints.push(limit15(filters.pagination || 10));
15621
15660
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15622
15661
  const querySnapshot = await getDocs29(q);
15623
- const procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15662
+ const procedures = querySnapshot.docs.map(
15663
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15664
+ );
15624
15665
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15625
15666
  console.log(`[PROCEDURE_SERVICE] Strategy 2 success: ${procedures.length} procedures`);
15626
15667
  if (procedures.length < (filters.pagination || 10)) {
@@ -15632,7 +15673,9 @@ var ProcedureService = class extends BaseService {
15632
15673
  }
15633
15674
  }
15634
15675
  try {
15635
- console.log("[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering");
15676
+ console.log(
15677
+ "[PROCEDURE_SERVICE] Strategy 3: Using createdAt orderBy with client-side filtering"
15678
+ );
15636
15679
  const constraints = getBaseConstraints();
15637
15680
  constraints.push(orderBy17("createdAt", "desc"));
15638
15681
  if (filters.lastDoc) {
@@ -15647,7 +15690,9 @@ var ProcedureService = class extends BaseService {
15647
15690
  constraints.push(limit15(filters.pagination || 10));
15648
15691
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15649
15692
  const querySnapshot = await getDocs29(q);
15650
- let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15693
+ let procedures = querySnapshot.docs.map(
15694
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15695
+ );
15651
15696
  procedures = this.applyInMemoryFilters(procedures, filters);
15652
15697
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15653
15698
  console.log(`[PROCEDURE_SERVICE] Strategy 3 success: ${procedures.length} procedures`);
@@ -15667,7 +15712,9 @@ var ProcedureService = class extends BaseService {
15667
15712
  ];
15668
15713
  const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15669
15714
  const querySnapshot = await getDocs29(q);
15670
- let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15715
+ let procedures = querySnapshot.docs.map(
15716
+ (doc37) => ({ ...doc37.data(), id: doc37.id })
15717
+ );
15671
15718
  procedures = this.applyInMemoryFilters(procedures, filters);
15672
15719
  const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
15673
15720
  console.log(`[PROCEDURE_SERVICE] Strategy 4 success: ${procedures.length} procedures`);
@@ -15707,7 +15754,9 @@ var ProcedureService = class extends BaseService {
15707
15754
  if (filters.maxPrice !== void 0 && price > filters.maxPrice) return false;
15708
15755
  return true;
15709
15756
  });
15710
- console.log(`[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`);
15757
+ console.log(
15758
+ `[PROCEDURE_SERVICE] Applied price filter (${filters.minPrice}-${filters.maxPrice}), results: ${filteredProcedures.length}`
15759
+ );
15711
15760
  }
15712
15761
  if (filters.minRating !== void 0 || filters.maxRating !== void 0) {
15713
15762
  filteredProcedures = filteredProcedures.filter((procedure) => {
@@ -15717,7 +15766,9 @@ var ProcedureService = class extends BaseService {
15717
15766
  if (filters.maxRating !== void 0 && rating > filters.maxRating) return false;
15718
15767
  return true;
15719
15768
  });
15720
- console.log(`[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`);
15769
+ console.log(
15770
+ `[PROCEDURE_SERVICE] Applied rating filter, results: ${filteredProcedures.length}`
15771
+ );
15721
15772
  }
15722
15773
  if (filters.treatmentBenefits && filters.treatmentBenefits.length > 0) {
15723
15774
  const benefitsToMatch = filters.treatmentBenefits;
@@ -15725,32 +15776,50 @@ var ProcedureService = class extends BaseService {
15725
15776
  const procedureBenefits = procedure.treatmentBenefits || [];
15726
15777
  return benefitsToMatch.some((benefit) => procedureBenefits.includes(benefit));
15727
15778
  });
15728
- console.log(`[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`);
15779
+ console.log(
15780
+ `[PROCEDURE_SERVICE] Applied benefits filter, results: ${filteredProcedures.length}`
15781
+ );
15729
15782
  }
15730
15783
  if (filters.procedureFamily) {
15731
- filteredProcedures = filteredProcedures.filter((procedure) => procedure.family === filters.procedureFamily);
15732
- console.log(`[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`);
15784
+ filteredProcedures = filteredProcedures.filter(
15785
+ (procedure) => procedure.family === filters.procedureFamily
15786
+ );
15787
+ console.log(
15788
+ `[PROCEDURE_SERVICE] Applied family filter, results: ${filteredProcedures.length}`
15789
+ );
15733
15790
  }
15734
15791
  if (filters.procedureCategory) {
15735
- filteredProcedures = filteredProcedures.filter((procedure) => {
15736
- var _a;
15737
- return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
15738
- });
15739
- console.log(`[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`);
15792
+ filteredProcedures = filteredProcedures.filter(
15793
+ (procedure) => {
15794
+ var _a;
15795
+ return ((_a = procedure.category) == null ? void 0 : _a.id) === filters.procedureCategory;
15796
+ }
15797
+ );
15798
+ console.log(
15799
+ `[PROCEDURE_SERVICE] Applied category filter, results: ${filteredProcedures.length}`
15800
+ );
15740
15801
  }
15741
15802
  if (filters.procedureSubcategory) {
15742
- filteredProcedures = filteredProcedures.filter((procedure) => {
15743
- var _a;
15744
- return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
15745
- });
15746
- console.log(`[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`);
15803
+ filteredProcedures = filteredProcedures.filter(
15804
+ (procedure) => {
15805
+ var _a;
15806
+ return ((_a = procedure.subcategory) == null ? void 0 : _a.id) === filters.procedureSubcategory;
15807
+ }
15808
+ );
15809
+ console.log(
15810
+ `[PROCEDURE_SERVICE] Applied subcategory filter, results: ${filteredProcedures.length}`
15811
+ );
15747
15812
  }
15748
15813
  if (filters.procedureTechnology) {
15749
- filteredProcedures = filteredProcedures.filter((procedure) => {
15750
- var _a;
15751
- return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
15752
- });
15753
- console.log(`[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`);
15814
+ filteredProcedures = filteredProcedures.filter(
15815
+ (procedure) => {
15816
+ var _a;
15817
+ return ((_a = procedure.technology) == null ? void 0 : _a.id) === filters.procedureTechnology;
15818
+ }
15819
+ );
15820
+ console.log(
15821
+ `[PROCEDURE_SERVICE] Applied technology filter, results: ${filteredProcedures.length}`
15822
+ );
15754
15823
  }
15755
15824
  if (filters.location && filters.radiusInKm && filters.radiusInKm > 0) {
15756
15825
  const location = filters.location;
@@ -15774,29 +15843,48 @@ var ProcedureService = class extends BaseService {
15774
15843
  return filteredProcedures;
15775
15844
  }
15776
15845
  handleGeoQuery(filters) {
15777
- console.log("[PROCEDURE_SERVICE] Executing geo query with enhanced debugging");
15846
+ console.log("[PROCEDURE_SERVICE] Executing geo query with geohash bounds");
15778
15847
  try {
15779
15848
  const location = filters.location;
15780
15849
  const radiusInKm = filters.radiusInKm;
15781
- console.log("[PROCEDURE_SERVICE] Geo query parameters:", {
15782
- latitude: location.latitude,
15783
- longitude: location.longitude,
15784
- radiusInKm,
15785
- pagination: filters.pagination || 10
15850
+ if (!location || !radiusInKm) {
15851
+ return Promise.resolve({ procedures: [], lastDoc: null });
15852
+ }
15853
+ const bounds = geohashQueryBounds5([location.latitude, location.longitude], radiusInKm * 1e3);
15854
+ const fetches = bounds.map((b) => {
15855
+ const constraints = [
15856
+ where29("clinicInfo.location.geohash", ">=", b[0]),
15857
+ where29("clinicInfo.location.geohash", "<=", b[1]),
15858
+ where29("isActive", "==", filters.isActive !== void 0 ? filters.isActive : true)
15859
+ ];
15860
+ return getDocs29(query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints));
15786
15861
  });
15787
- const constraints = [
15788
- where29("isActive", "==", true),
15789
- orderBy17("createdAt", "desc"),
15790
- limit15((filters.pagination || 10) * 3)
15791
- // Get more results for geo filtering
15792
- ];
15793
- const q = query29(collection29(this.db, PROCEDURES_COLLECTION), ...constraints);
15794
- return getDocs29(q).then((querySnapshot) => {
15795
- let procedures = querySnapshot.docs.map((doc37) => ({ ...doc37.data(), id: doc37.id }));
15862
+ return Promise.all(fetches).then((snaps) => {
15863
+ const collected = [];
15864
+ snaps.forEach((snap) => {
15865
+ snap.docs.forEach((d) => collected.push({ ...d.data(), id: d.id }));
15866
+ });
15867
+ const uniqueMap = /* @__PURE__ */ new Map();
15868
+ for (const p of collected) {
15869
+ uniqueMap.set(p.id, p);
15870
+ }
15871
+ let procedures = Array.from(uniqueMap.values());
15796
15872
  procedures = this.applyInMemoryFilters(procedures, filters);
15797
- console.log(`[PROCEDURE_SERVICE] Geo query success: ${procedures.length} procedures within ${radiusInKm}km`);
15798
- const lastDoc = procedures.length < (filters.pagination || 10) ? null : querySnapshot.docs[querySnapshot.docs.length - 1];
15799
- return { procedures, lastDoc };
15873
+ const pageSize = filters.pagination || 10;
15874
+ let startIndex = 0;
15875
+ if (filters.lastDoc && typeof filters.lastDoc === "object" && filters.lastDoc.id) {
15876
+ const idx = procedures.findIndex((p) => p.id === filters.lastDoc.id);
15877
+ if (idx >= 0) startIndex = idx + 1;
15878
+ }
15879
+ const page = procedures.slice(startIndex, startIndex + pageSize);
15880
+ const newLastDoc = page.length === pageSize ? page[page.length - 1] : null;
15881
+ console.log(
15882
+ `[PROCEDURE_SERVICE] Geo query success: ${page.length} (of ${procedures.length}) within ${radiusInKm}km`
15883
+ );
15884
+ return { procedures: page, lastDoc: newLastDoc };
15885
+ }).catch((err) => {
15886
+ console.error("[PROCEDURE_SERVICE] Geo bounds fetch failed:", err);
15887
+ return { procedures: [], lastDoc: null };
15800
15888
  });
15801
15889
  } catch (error) {
15802
15890
  console.error("[PROCEDURE_SERVICE] Geo query failed:", error);
@@ -15826,11 +15914,7 @@ var ProcedureService = class extends BaseService {
15826
15914
  throw new Error(`Clinic with ID ${data.clinicBranchId} not found`);
15827
15915
  }
15828
15916
  const clinic = clinicSnapshot.data();
15829
- const practitionerRef = doc30(
15830
- this.db,
15831
- PRACTITIONERS_COLLECTION,
15832
- data.practitionerId
15833
- );
15917
+ const practitionerRef = doc30(this.db, PRACTITIONERS_COLLECTION, data.practitionerId);
15834
15918
  const practitionerSnapshot = await getDoc32(practitionerRef);
15835
15919
  if (!practitionerSnapshot.exists()) {
15836
15920
  throw new Error(`Practitioner with ID ${data.practitionerId} not found`);
@@ -15838,11 +15922,7 @@ var ProcedureService = class extends BaseService {
15838
15922
  const practitioner = practitionerSnapshot.data();
15839
15923
  let processedPhotos = [];
15840
15924
  if (data.photos && data.photos.length > 0) {
15841
- processedPhotos = await this.processMediaArray(
15842
- data.photos,
15843
- procedureId,
15844
- "procedure-photos"
15845
- );
15925
+ processedPhotos = await this.processMediaArray(data.photos, procedureId, "procedure-photos");
15846
15926
  }
15847
15927
  const clinicInfo = {
15848
15928
  id: clinicSnapshot.id,
@@ -16156,7 +16236,7 @@ import { getAuth } from "firebase/auth";
16156
16236
  import { getAnalytics } from "firebase/analytics";
16157
16237
  import { Platform } from "react-native";
16158
16238
  import { getStorage as getStorage3 } from "firebase/storage";
16159
- import { getFunctions as getFunctions2 } from "firebase/functions";
16239
+ import { getFunctions as getFunctions3 } from "firebase/functions";
16160
16240
  var firebaseInstance = null;
16161
16241
  var initializeFirebase = (config) => {
16162
16242
  if (!firebaseInstance) {
@@ -16164,7 +16244,7 @@ var initializeFirebase = (config) => {
16164
16244
  const db = getFirestore2(app);
16165
16245
  const auth = getAuth(app);
16166
16246
  const storage = getStorage3(app);
16167
- const functions = getFunctions2(app);
16247
+ const functions = getFunctions3(app);
16168
16248
  let analytics = null;
16169
16249
  if (typeof window !== "undefined" && Platform.OS === "web") {
16170
16250
  analytics = getAnalytics(app);