@blackcode_sa/metaestetics-api 1.5.28 → 1.5.30

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.
Files changed (49) hide show
  1. package/dist/admin/index.d.mts +1324 -1
  2. package/dist/admin/index.d.ts +1324 -1
  3. package/dist/admin/index.js +1674 -2
  4. package/dist/admin/index.mjs +1668 -2
  5. package/dist/backoffice/index.d.mts +99 -7
  6. package/dist/backoffice/index.d.ts +99 -7
  7. package/dist/index.d.mts +4036 -2372
  8. package/dist/index.d.ts +4036 -2372
  9. package/dist/index.js +2331 -2009
  10. package/dist/index.mjs +2279 -1954
  11. package/package.json +2 -1
  12. package/src/admin/aggregation/README.md +79 -0
  13. package/src/admin/aggregation/clinic/README.md +52 -0
  14. package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +642 -0
  15. package/src/admin/aggregation/patient/README.md +27 -0
  16. package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -0
  17. package/src/admin/aggregation/practitioner/README.md +42 -0
  18. package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -0
  19. package/src/admin/aggregation/procedure/README.md +43 -0
  20. package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +508 -0
  21. package/src/admin/index.ts +60 -4
  22. package/src/admin/mailing/README.md +95 -0
  23. package/src/admin/mailing/base.mailing.service.ts +131 -0
  24. package/src/admin/mailing/index.ts +2 -0
  25. package/src/admin/mailing/practitionerInvite/index.ts +1 -0
  26. package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +256 -0
  27. package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -0
  28. package/src/index.ts +28 -4
  29. package/src/services/README.md +106 -0
  30. package/src/services/clinic/README.md +87 -0
  31. package/src/services/clinic/clinic.service.ts +197 -107
  32. package/src/services/clinic/utils/clinic.utils.ts +68 -119
  33. package/src/services/clinic/utils/filter.utils.d.ts +23 -0
  34. package/src/services/clinic/utils/filter.utils.ts +264 -0
  35. package/src/services/practitioner/README.md +145 -0
  36. package/src/services/practitioner/practitioner.service.ts +439 -104
  37. package/src/services/procedure/README.md +88 -0
  38. package/src/services/procedure/procedure.service.ts +521 -311
  39. package/src/services/reviews/reviews.service.ts +842 -0
  40. package/src/types/clinic/index.ts +24 -56
  41. package/src/types/practitioner/index.ts +34 -33
  42. package/src/types/procedure/index.ts +32 -0
  43. package/src/types/profile/index.ts +1 -1
  44. package/src/types/reviews/index.ts +126 -0
  45. package/src/validations/clinic.schema.ts +37 -64
  46. package/src/validations/practitioner.schema.ts +42 -32
  47. package/src/validations/procedure.schema.ts +11 -3
  48. package/src/validations/reviews.schema.ts +189 -0
  49. package/src/services/clinic/utils/review.utils.ts +0 -93
@@ -0,0 +1,87 @@
1
+ # Clinic Service
2
+
3
+ This service manages clinic data within the Firestore database. It provides methods for creating, reading, updating, and managing clinics, their branches, and associated data like tags and administrators.
4
+
5
+ **Note:** This service relies on helper functions defined in the `./utils` directory for specific operations like photo uploads, geo-queries, and data fetching. Cloud Functions handle data aggregation into related entities (Practitioners, Procedures, ClinicGroups).
6
+
7
+ ## `ClinicService` Class
8
+
9
+ Extends `BaseService`.
10
+
11
+ ### Constructor
12
+
13
+ ```typescript
14
+ constructor(
15
+ db: Firestore,
16
+ auth: Auth,
17
+ app: FirebaseApp,
18
+ clinicGroupService: ClinicGroupService,
19
+ clinicAdminService: ClinicAdminService
20
+ )
21
+ ```
22
+
23
+ Initializes the service with Firestore, Auth, App instances, and required dependency services (`ClinicGroupService`, `ClinicAdminService`).
24
+
25
+ ### Core Methods
26
+
27
+ - **`createClinic(data: CreateClinicData, creatorAdminId: string): Promise<Clinic>`**
28
+ - Creates a new clinic document in Firestore.
29
+ - Validates input data using `createClinicSchema`.
30
+ - Verifies the `creatorAdminId` and their association with the specified `clinicGroupId`.
31
+ - Calculates the geohash for the clinic's location.
32
+ - Initializes default review information.
33
+ - Handles photo uploads for logo, cover photo, featured photos, and photos with tags using utility functions.
34
+ - Updates the creator admin's `clinicsManaged` list.
35
+ - **Aggregation Note:** Adding the clinic to the `ClinicGroup` is handled by Cloud Functions.
36
+ - **`updateClinic(clinicId: string, data: Partial<Omit<Clinic, \"id\" | \"createdAt\" | \"clinicGroupId\">>, adminId: string): Promise<Clinic>`**
37
+ - Updates an existing clinic document.
38
+ - Fetches the current clinic data.
39
+ - Validates the partial update data against the `clinicSchema` after merging with existing data.
40
+ - Updates the geohash if the location is changed.
41
+ - Handles photo updates/uploads if necessary.
42
+ - **Aggregation Note:** Aggregation updates in related entities (Practitioners, Procedures, ClinicGroup) are handled by Cloud Functions.
43
+ - **`deactivateClinic(clinicId: string, adminId: string): Promise<void>`**
44
+ - Sets the `isActive` flag of a clinic to `false`.
45
+ - Requires admin permission (verification logic might be in the calling function or assumed).
46
+ - **Aggregation Note:** Aggregation updates (e.g., removing from active lists, updating related entities) are handled by Cloud Functions.
47
+ - **`getClinic(clinicId: string): Promise<Clinic | null>`**
48
+ - Retrieves a single clinic document by its ID using `ClinicUtils.getClinic`.
49
+ - **`getClinicsByGroup(groupId: string): Promise<Clinic[]>`**
50
+ - Retrieves all clinic documents belonging to a specific `clinicGroupId` using `ClinicUtils.getClinicsByGroup`.
51
+ - **`findClinicsInRadius(center: { latitude: number; longitude: number }, radiusInKm: number, filters?: { procedures?: string[]; tags?: ClinicTag[] }): Promise<Clinic[]>`**
52
+ - Finds active clinics within a specified radius using geohash queries.
53
+ - Leverages `SearchUtils.findClinicsInRadius`.
54
+ - Optionally filters results by procedure IDs (services) and tags.
55
+ - **`addTags(clinicId: string, adminId: string, newTags: { tags?: ClinicTag[] }): Promise<Clinic>`**
56
+ - Adds specified tags to a clinic's `tags` array using `TagUtils.addTags`.
57
+ - Ensures the admin has permission. Prevents duplicates.
58
+ - **`removeTags(clinicId: string, adminId: string, tagsToRemove: { tags?: ClinicTag[] }): Promise<Clinic>`**
59
+ - Removes specified tags from a clinic's `tags` array using `TagUtils.removeTags`.
60
+ - Ensures the admin has permission.
61
+ - **`getClinicsByAdmin(adminId: string, options?: { isActive?: boolean; includeGroupClinics?: boolean }): Promise<Clinic[]>`**
62
+ - Retrieves clinics associated with a specific admin using `ClinicUtils.getClinicsByAdmin`.
63
+ - Handles options for filtering by `isActive` and including all group clinics for owners.
64
+ - **`getActiveClinicsByAdmin(adminId: string): Promise<Clinic[]>`**
65
+ - Retrieves only the active clinics associated with a specific admin using `ClinicUtils.getActiveClinicsByAdmin`.
66
+ - **`createClinicBranch(clinicGroupId: string, setupData: ClinicBranchSetupData, adminId: string): Promise<Clinic>`**
67
+ - Creates a new clinic (branch) within an existing `clinicGroupId`.
68
+ - Validates group existence. Uses `createClinic` internally.
69
+ - **`getClinicById(clinicId: string): Promise<Clinic | null>`**
70
+ - Retrieves a single clinic document by its ID using `ClinicUtils.getClinicById`.
71
+ - **`getAllClinics(pagination?: number, lastDoc?: any): Promise<{ clinics: Clinic[]; lastDoc: any }>`**
72
+ - Retrieves all clinics, ordered by ID, optionally with pagination, using `ClinicUtils.getAllClinics`.
73
+ - **`getAllClinicsInRange(center: { latitude: number; longitude: number }, rangeInKm: number, pagination?: number, lastDoc?: any): Promise<{ clinics: (Clinic & { distance: number })[]; lastDoc: any }>`**
74
+ - Retrieves all clinics within a range, sorted by distance, optionally with pagination, using `ClinicUtils.getAllClinicsInRange`.
75
+ - **`getClinicsByFilters(filters: { ... }): Promise<{ clinics: (Clinic & { distance?: number })[]; lastDoc: any }>`**
76
+ - Retrieves clinics based on complex filters (location, tags, procedures, rating, etc.).
77
+ - Uses `FilterUtils.getClinicsByFilters` for combined query and in-memory filtering.
78
+
79
+ ### Utility Functions (`./utils`)
80
+
81
+ - **`clinic.utils.ts`**: Core fetching (`getClinic`, `getClinicsByGroup`, etc.), create/update/deactivate logic wrappers, admin checks.
82
+ - **`filter.utils.ts`**: Complex filtering logic (`getClinicsByFilters`), including geo-queries.
83
+ - **`clinic-group.utils.ts`**: Helpers for clinic group interactions (used by `ClinicGroupService`).
84
+ - **`tag.utils.ts`**: Logic for adding/removing tags (`addTags`, `removeTags`).
85
+ - **`photos.utils.ts`**: Firebase Storage interactions (`uploadPhoto`, `uploadMultiplePhotos`, `deletePhoto`).
86
+ - **`admin.utils.ts`**: Helpers for clinic admin interactions (used by `ClinicAdminService`).
87
+ - **`search.utils.ts`**: Geo-radius search logic (`findClinicsInRadius`).
@@ -13,19 +13,26 @@ import {
13
13
  GeoPoint,
14
14
  QueryConstraint,
15
15
  FieldValue,
16
+ writeBatch,
17
+ arrayUnion,
18
+ arrayRemove,
16
19
  } from "firebase/firestore";
17
20
  import { BaseService } from "../base.service";
18
21
  import {
19
22
  Clinic,
20
23
  CreateClinicData,
21
24
  CLINICS_COLLECTION,
22
- ClinicReview,
23
25
  ClinicTag,
24
26
  ClinicTags,
25
27
  ClinicGroup,
28
+ CLINIC_GROUPS_COLLECTION,
26
29
  ClinicBranchSetupData,
27
30
  CLINIC_ADMINS_COLLECTION,
31
+ DoctorInfo,
28
32
  } from "../../types/clinic";
33
+ // Correct imports
34
+ import { ProcedureSummaryInfo } from "../../types/procedure";
35
+ import { ClinicInfo } from "../../types/profile";
29
36
  import { ClinicGroupService } from "./clinic-group.service";
30
37
  import { ClinicAdminService } from "./clinic-admin.service";
31
38
  import {
@@ -36,17 +43,18 @@ import {
36
43
  import {
37
44
  clinicSchema,
38
45
  createClinicSchema,
39
- clinicReviewSchema,
40
46
  } from "../../validations/clinic.schema";
41
47
  import { z } from "zod";
42
48
  import { Auth } from "firebase/auth";
43
49
  import { Firestore } from "firebase/firestore";
44
50
  import { FirebaseApp } from "firebase/app";
45
51
  import * as ClinicUtils from "./utils/clinic.utils";
46
- import * as ReviewUtils from "./utils/review.utils";
47
52
  import * as TagUtils from "./utils/tag.utils";
48
53
  import * as SearchUtils from "./utils/search.utils";
49
54
  import * as AdminUtils from "./utils/admin.utils";
55
+ import * as FilterUtils from "./utils/filter.utils";
56
+ import { ClinicReviewInfo } from "../../types/reviews";
57
+ import { PRACTITIONERS_COLLECTION } from "../../types/practitioner";
50
58
 
51
59
  export class ClinicService extends BaseService {
52
60
  private clinicGroupService: ClinicGroupService;
@@ -65,20 +73,172 @@ export class ClinicService extends BaseService {
65
73
  }
66
74
 
67
75
  /**
68
- * Kreira novu kliniku
76
+ * Creates a new clinic.
69
77
  */
70
78
  async createClinic(
71
79
  data: CreateClinicData,
72
80
  creatorAdminId: string
73
81
  ): Promise<Clinic> {
74
- return ClinicUtils.createClinic(
75
- this.db,
76
- data,
77
- creatorAdminId,
78
- this.clinicGroupService,
79
- this.clinicAdminService,
80
- this.app
81
- );
82
+ try {
83
+ const validatedData = createClinicSchema.parse(data);
84
+ const group = await this.clinicGroupService.getClinicGroup(
85
+ validatedData.clinicGroupId
86
+ );
87
+ if (!group)
88
+ throw new Error(
89
+ `Clinic group ${validatedData.clinicGroupId} not found`
90
+ );
91
+ const location = validatedData.location;
92
+ const hash = geohashForLocation([location.latitude, location.longitude]);
93
+ const defaultReviewInfo: ClinicReviewInfo = {
94
+ totalReviews: 0,
95
+ averageRating: 0,
96
+ cleanliness: 0,
97
+ facilities: 0,
98
+ staffFriendliness: 0,
99
+ waitingTime: 0,
100
+ accessibility: 0,
101
+ recommendationPercentage: 0,
102
+ };
103
+ const clinicId = this.generateId();
104
+
105
+ const clinicData: Omit<Clinic, "createdAt" | "updatedAt"> & {
106
+ createdAt: FieldValue;
107
+ updatedAt: FieldValue;
108
+ } = {
109
+ id: clinicId,
110
+ clinicGroupId: validatedData.clinicGroupId,
111
+ name: validatedData.name,
112
+ description: validatedData.description,
113
+ location: { ...location, geohash: hash },
114
+ contactInfo: validatedData.contactInfo,
115
+ workingHours: validatedData.workingHours,
116
+ tags: validatedData.tags,
117
+ featuredPhotos: validatedData.featuredPhotos || [],
118
+ coverPhoto: validatedData.coverPhoto,
119
+ photosWithTags: validatedData.photosWithTags,
120
+ doctors: [],
121
+ procedures: [],
122
+ doctorsInfo: [],
123
+ proceduresInfo: [],
124
+ reviewInfo: defaultReviewInfo,
125
+ admins: [creatorAdminId],
126
+ isActive: validatedData.isActive,
127
+ isVerified: validatedData.isVerified,
128
+ logo: validatedData.logo,
129
+ createdAt: serverTimestamp(),
130
+ updatedAt: serverTimestamp(),
131
+ };
132
+
133
+ // Re-validate before saving (ensure schema matches the final object)
134
+ clinicSchema.parse({
135
+ ...clinicData,
136
+ createdAt: Timestamp.now(),
137
+ updatedAt: Timestamp.now(),
138
+ });
139
+
140
+ const batch = writeBatch(this.db);
141
+ const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
142
+ batch.set(clinicRef, clinicData);
143
+
144
+ // Update admin relationship - this part is still needed
145
+ const adminRef = doc(this.db, CLINIC_ADMINS_COLLECTION, creatorAdminId);
146
+ batch.update(adminRef, {
147
+ clinicsManaged: arrayUnion(clinicId),
148
+ updatedAt: serverTimestamp(),
149
+ });
150
+
151
+ await batch.commit();
152
+ const savedClinic = await this.getClinic(clinicId);
153
+ if (!savedClinic) throw new Error("Failed to retrieve created clinic");
154
+ return savedClinic;
155
+ } catch (error) {
156
+ if (error instanceof z.ZodError) {
157
+ throw new Error("Invalid clinic data: " + error.message);
158
+ }
159
+ console.error("Error creating clinic:", error);
160
+ throw error;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Updates a clinic.
166
+ */
167
+ async updateClinic(
168
+ clinicId: string,
169
+ data: Partial<Omit<Clinic, "id" | "createdAt" | "clinicGroupId">>,
170
+ adminId: string
171
+ ): Promise<Clinic> {
172
+ const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
173
+ const clinicDoc = await getDoc(clinicRef);
174
+ if (!clinicDoc.exists()) {
175
+ throw new Error(`Clinic ${clinicId} not found`);
176
+ }
177
+
178
+ try {
179
+ const currentClinic = clinicDoc.data() as Clinic;
180
+ // Explicitly Omit fields managed by other services or internally
181
+ const { doctorsInfo, proceduresInfo, ...updatePayload } =
182
+ data as Partial<Clinic>;
183
+
184
+ if (updatePayload.location) {
185
+ const loc = updatePayload.location;
186
+ updatePayload.location = {
187
+ ...loc,
188
+ geohash: geohashForLocation([loc.latitude, loc.longitude]),
189
+ };
190
+ }
191
+
192
+ // Merge with current data for validation and aggregation, preserving arrays managed elsewhere
193
+ const finalStateForValidation = {
194
+ ...currentClinic,
195
+ ...updatePayload, // Apply safe updates
196
+ // Explicitly keep arrays managed by other services from current state
197
+ doctorsInfo: currentClinic.doctorsInfo,
198
+ proceduresInfo: currentClinic.proceduresInfo,
199
+ };
200
+
201
+ // Ensure required fields for validation are present
202
+ clinicSchema.parse({
203
+ ...finalStateForValidation,
204
+ updatedAt: Timestamp.now(), // Use current time for validation
205
+ });
206
+
207
+ // Prepare final update data for Firestore, including timestamp
208
+ const updateDataForFirestore = {
209
+ ...updatePayload,
210
+ updatedAt: serverTimestamp(),
211
+ };
212
+
213
+ await updateDoc(clinicRef, updateDataForFirestore);
214
+
215
+ const updatedClinic = await this.getClinic(clinicId);
216
+ if (!updatedClinic) throw new Error("Failed to retrieve updated clinic");
217
+ return updatedClinic;
218
+ } catch (error) {
219
+ if (error instanceof z.ZodError) {
220
+ throw new Error(
221
+ "Invalid clinic update data: " +
222
+ error.errors
223
+ .map((e) => `${e.path.join(".")} - ${e.message}`)
224
+ .join(", ")
225
+ );
226
+ }
227
+ console.error(`Error updating clinic ${clinicId}:`, error);
228
+ throw error;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Deactivates a clinic.
234
+ */
235
+ async deactivateClinic(clinicId: string, adminId: string): Promise<void> {
236
+ // Permission check omitted
237
+ const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
238
+ await updateDoc(clinicRef, {
239
+ isActive: false,
240
+ updatedAt: serverTimestamp(),
241
+ });
82
242
  }
83
243
 
84
244
  /**
@@ -102,8 +262,9 @@ export class ClinicService extends BaseService {
102
262
  center: { latitude: number; longitude: number },
103
263
  radiusInKm: number,
104
264
  filters?: {
105
- services?: string[];
265
+ procedures?: string[];
106
266
  tags?: ClinicTag[];
267
+ // Add other relevant filters based on Clinic/ProcedureSummaryInfo fields
107
268
  }
108
269
  ): Promise<Clinic[]> {
109
270
  return SearchUtils.findClinicsInRadius(
@@ -114,52 +275,6 @@ export class ClinicService extends BaseService {
114
275
  );
115
276
  }
116
277
 
117
- /**
118
- * Ažurira kliniku
119
- */
120
- async updateClinic(
121
- clinicId: string,
122
- data: Partial<Clinic>,
123
- adminId: string
124
- ): Promise<Clinic> {
125
- return ClinicUtils.updateClinic(
126
- this.db,
127
- clinicId,
128
- data,
129
- adminId,
130
- this.clinicAdminService,
131
- this.app
132
- );
133
- }
134
-
135
- /**
136
- * Dodaje recenziju klinici
137
- */
138
- async addReview(
139
- clinicId: string,
140
- review: Omit<
141
- ClinicReview,
142
- "id" | "clinicId" | "createdAt" | "updatedAt" | "isVerified"
143
- >
144
- ): Promise<ClinicReview> {
145
- return ReviewUtils.addReview(this.db, clinicId, review, this.app);
146
- }
147
-
148
- /**
149
- * Deaktivira kliniku
150
- */
151
- async deactivateClinic(clinicId: string, adminId: string): Promise<void> {
152
- return ClinicUtils.deactivateClinic(
153
- this.db,
154
- clinicId,
155
- adminId,
156
- this.clinicAdminService
157
- );
158
- }
159
-
160
- /**
161
- * Dodaje tagove klinici
162
- */
163
278
  async addTags(
164
279
  clinicId: string,
165
280
  adminId: string,
@@ -177,9 +292,6 @@ export class ClinicService extends BaseService {
177
292
  );
178
293
  }
179
294
 
180
- /**
181
- * Uklanja tagove iz klinike
182
- */
183
295
  async removeTags(
184
296
  clinicId: string,
185
297
  adminId: string,
@@ -197,9 +309,6 @@ export class ClinicService extends BaseService {
197
309
  );
198
310
  }
199
311
 
200
- /**
201
- * Dohvata sve klinike gde je korisnik admin
202
- */
203
312
  async getClinicsByAdmin(
204
313
  adminId: string,
205
314
  options?: {
@@ -216,9 +325,6 @@ export class ClinicService extends BaseService {
216
325
  );
217
326
  }
218
327
 
219
- /**
220
- * Dohvata sve aktivne klinike gde je korisnik admin
221
- */
222
328
  async getActiveClinicsByAdmin(adminId: string): Promise<Clinic[]> {
223
329
  return ClinicUtils.getActiveClinicsByAdmin(
224
330
  this.db,
@@ -228,14 +334,6 @@ export class ClinicService extends BaseService {
228
334
  );
229
335
  }
230
336
 
231
- /**
232
- * Creates a new clinic branch for a clinic group
233
- *
234
- * @param clinicGroupId - The ID of the clinic group
235
- * @param setupData - The setup data for the clinic branch
236
- * @param adminId - The ID of the admin creating the branch
237
- * @returns The created clinic
238
- */
239
337
  async createClinicBranch(
240
338
  clinicGroupId: string,
241
339
  setupData: ClinicBranchSetupData,
@@ -276,7 +374,7 @@ export class ClinicService extends BaseService {
276
374
  coverPhoto: setupData.coverPhoto || null,
277
375
  photosWithTags: setupData.photosWithTags || [],
278
376
  doctors: [],
279
- services: [],
377
+ procedures: [],
280
378
  admins: [adminId],
281
379
  isActive: true,
282
380
  isVerified: false,
@@ -302,23 +400,10 @@ export class ClinicService extends BaseService {
302
400
  return clinic;
303
401
  }
304
402
 
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
403
  async getClinicById(clinicId: string): Promise<Clinic | null> {
312
404
  return ClinicUtils.getClinicById(this.db, clinicId);
313
405
  }
314
406
 
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
407
  async getAllClinics(
323
408
  pagination?: number,
324
409
  lastDoc?: any
@@ -326,33 +411,38 @@ export class ClinicService extends BaseService {
326
411
  return ClinicUtils.getAllClinics(this.db, pagination, lastDoc);
327
412
  }
328
413
 
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
414
  async getAllClinicsInRange(
340
415
  center: { latitude: number; longitude: number },
341
416
  rangeInKm: number,
342
417
  pagination?: number,
343
- lastDoc?: any,
344
- filters?: {
345
- isActive?: boolean;
346
- tags?: ClinicTag[];
347
- }
418
+ lastDoc?: any
348
419
  ): Promise<{ clinics: (Clinic & { distance: number })[]; lastDoc: any }> {
349
420
  return ClinicUtils.getAllClinicsInRange(
350
421
  this.db,
351
422
  center,
352
423
  rangeInKm,
353
424
  pagination,
354
- lastDoc,
355
- filters
425
+ lastDoc
356
426
  );
357
427
  }
428
+
429
+ async getClinicsByFilters(filters: {
430
+ center?: { latitude: number; longitude: number };
431
+ radiusInKm?: number;
432
+ tags?: ClinicTag[];
433
+ procedureFamily?: string;
434
+ procedureCategory?: string;
435
+ procedureSubcategory?: string;
436
+ procedureTechnology?: string;
437
+ minRating?: number;
438
+ maxRating?: number;
439
+ pagination?: number;
440
+ lastDoc?: any;
441
+ isActive?: boolean;
442
+ }): Promise<{
443
+ clinics: (Clinic & { distance?: number })[];
444
+ lastDoc: any;
445
+ }> {
446
+ return FilterUtils.getClinicsByFilters(this.db, filters);
447
+ }
358
448
  }