@blackcode_sa/metaestetics-api 1.5.29 → 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.
@@ -29,9 +29,6 @@ import {
29
29
  ClinicBranchSetupData,
30
30
  CLINIC_ADMINS_COLLECTION,
31
31
  DoctorInfo,
32
- // Remove incorrect imports below
33
- // ProcedureSummaryInfo,
34
- // ClinicInfo
35
32
  } from "../../types/clinic";
36
33
  // Correct imports
37
34
  import { ProcedureSummaryInfo } from "../../types/procedure";
@@ -75,73 +72,8 @@ export class ClinicService extends BaseService {
75
72
  this.clinicGroupService = clinicGroupService;
76
73
  }
77
74
 
78
- // --- Helper Functions ---
79
-
80
- /**
81
- * Creates an aggregated ClinicInfo object from Clinic data.
82
- * @param clinic The clinic object
83
- * @returns ClinicInfo object
84
- */
85
- private _createClinicInfoForAggregation(clinic: Clinic): ClinicInfo {
86
- return {
87
- id: clinic.id,
88
- featuredPhoto:
89
- clinic.featuredPhotos && clinic.featuredPhotos.length > 0
90
- ? clinic.featuredPhotos[0]
91
- : clinic.coverPhoto || "",
92
- name: clinic.name,
93
- description: clinic.description || "",
94
- location: clinic.location,
95
- contactInfo: clinic.contactInfo,
96
- };
97
- }
98
-
99
- /**
100
- * Updates the ClinicInfo within the clinicsInfo array for multiple practitioners.
101
- * @param practitionerIds IDs of practitioners to update
102
- * @param clinicInfo The updated ClinicInfo object
103
- */
104
- private async _updateClinicInfoInPractitioners(
105
- practitionerIds: string[],
106
- clinicInfo: ClinicInfo
107
- ): Promise<void> {
108
- const batch = writeBatch(this.db);
109
- const clinicId = clinicInfo.id;
110
-
111
- for (const practitionerId of practitionerIds) {
112
- const practitionerRef = doc(
113
- this.db,
114
- PRACTITIONERS_COLLECTION,
115
- practitionerId
116
- );
117
- // Remove old clinic info based on ID
118
- batch.update(practitionerRef, {
119
- clinicsInfo: arrayRemove(...[{ id: clinicId }]),
120
- updatedAt: serverTimestamp(),
121
- });
122
- // Add updated clinic info
123
- batch.update(practitionerRef, {
124
- clinicsInfo: arrayUnion(clinicInfo),
125
- updatedAt: serverTimestamp(),
126
- });
127
- }
128
- try {
129
- await batch.commit();
130
- } catch (error) {
131
- console.error(
132
- `Error updating clinic info in practitioners for clinic ${clinicId}:`,
133
- error
134
- );
135
- // Decide on error handling
136
- }
137
- }
138
-
139
- // --- Core Service Methods (Updated) ---
140
-
141
75
  /**
142
76
  * Creates a new clinic.
143
- * Initializes empty doctorsInfo and proceduresInfo.
144
- * Aggregation into Clinic happens via PractitionerService and ProcedureService.
145
77
  */
146
78
  async createClinic(
147
79
  data: CreateClinicData,
@@ -209,21 +141,7 @@ export class ClinicService extends BaseService {
209
141
  const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
210
142
  batch.set(clinicRef, clinicData);
211
143
 
212
- const groupRef = doc(
213
- this.db,
214
- CLINIC_GROUPS_COLLECTION,
215
- validatedData.clinicGroupId
216
- );
217
- const newClinicInfoForGroup = this._createClinicInfoForAggregation({
218
- ...clinicData,
219
- id: clinicId,
220
- } as Clinic);
221
- batch.update(groupRef, {
222
- clinics: arrayUnion(clinicId),
223
- clinicsInfo: arrayUnion(newClinicInfoForGroup),
224
- updatedAt: serverTimestamp(),
225
- });
226
-
144
+ // Update admin relationship - this part is still needed
227
145
  const adminRef = doc(this.db, CLINIC_ADMINS_COLLECTION, creatorAdminId);
228
146
  batch.update(adminRef, {
229
147
  clinicsManaged: arrayUnion(clinicId),
@@ -244,7 +162,7 @@ export class ClinicService extends BaseService {
244
162
  }
245
163
 
246
164
  /**
247
- * Updates a clinic and propagates changes (ClinicInfo) to associated practitioners.
165
+ * Updates a clinic.
248
166
  */
249
167
  async updateClinic(
250
168
  clinicId: string,
@@ -292,37 +210,8 @@ export class ClinicService extends BaseService {
292
210
  updatedAt: serverTimestamp(),
293
211
  };
294
212
 
295
- const batch = writeBatch(this.db);
296
- batch.update(clinicRef, updateDataForFirestore);
213
+ await updateDoc(clinicRef, updateDataForFirestore);
297
214
 
298
- const groupRef = doc(
299
- this.db,
300
- CLINIC_GROUPS_COLLECTION,
301
- currentClinic.clinicGroupId
302
- );
303
- // Use the validated final state for aggregation
304
- const updatedClinicInfoForGroup = this._createClinicInfoForAggregation(
305
- finalStateForValidation as Clinic
306
- );
307
- batch.update(groupRef, {
308
- clinicsInfo: arrayRemove(...[{ id: clinicId }]),
309
- updatedAt: serverTimestamp(),
310
- });
311
- batch.update(groupRef, {
312
- clinicsInfo: arrayUnion(updatedClinicInfoForGroup),
313
- updatedAt: serverTimestamp(),
314
- });
315
-
316
- const practitionerIds = currentClinic.doctors || [];
317
- if (practitionerIds.length > 0) {
318
- // Pass the aggregated info based on the final validated state
319
- await this._updateClinicInfoInPractitioners(
320
- practitionerIds,
321
- updatedClinicInfoForGroup
322
- );
323
- }
324
-
325
- await batch.commit();
326
215
  const updatedClinic = await this.getClinic(clinicId);
327
216
  if (!updatedClinic) throw new Error("Failed to retrieve updated clinic");
328
217
  return updatedClinic;
@@ -342,7 +231,6 @@ export class ClinicService extends BaseService {
342
231
 
343
232
  /**
344
233
  * Deactivates a clinic.
345
- * Note: Does not currently remove ClinicInfo from practitioners (might be desired).
346
234
  */
347
235
  async deactivateClinic(clinicId: string, adminId: string): Promise<void> {
348
236
  // Permission check omitted
@@ -351,14 +239,8 @@ export class ClinicService extends BaseService {
351
239
  isActive: false,
352
240
  updatedAt: serverTimestamp(),
353
241
  });
354
- // Consider whether to also update ClinicInfo in practitioners to reflect inactive status
355
242
  }
356
243
 
357
- // --- Other Methods ---
358
- // (getClinic, getClinicsByGroup, findClinicsInRadius, addTags, removeTags, getClinicsByAdmin, etc.)
359
- // Review these methods to ensure they don't rely on outdated aggregation logic (e.g., filtering ServiceInfo)
360
- // and update them to use proceduresInfo if necessary for filtering.
361
-
362
244
  /**
363
245
  * Dohvata kliniku po ID-u
364
246
  */
@@ -375,7 +257,6 @@ export class ClinicService extends BaseService {
375
257
 
376
258
  /**
377
259
  * Pretražuje klinike u određenom radijusu
378
- * REVIEW: SearchUtils.findClinicsInRadius might need updating for filters.
379
260
  */
380
261
  async findClinicsInRadius(
381
262
  center: { latitude: number; longitude: number },
@@ -386,10 +267,6 @@ export class ClinicService extends BaseService {
386
267
  // Add other relevant filters based on Clinic/ProcedureSummaryInfo fields
387
268
  }
388
269
  ): Promise<Clinic[]> {
389
- console.warn(
390
- "SearchUtils.findClinicsInRadius filter logic might need updating for proceduresInfo."
391
- );
392
- // Pass filters directly, assuming SearchUtils handles it.
393
270
  return SearchUtils.findClinicsInRadius(
394
271
  this.db,
395
272
  center,
@@ -13,6 +13,8 @@ import {
13
13
  QueryConstraint,
14
14
  addDoc,
15
15
  writeBatch,
16
+ limit,
17
+ startAfter,
16
18
  } from "firebase/firestore";
17
19
  import {
18
20
  Clinic,
@@ -827,8 +829,6 @@ export async function getAllClinics(
827
829
 
828
830
  // If pagination is specified and greater than 0, limit the query
829
831
  if (pagination && pagination > 0) {
830
- const { limit, startAfter } = require("firebase/firestore");
831
-
832
832
  if (lastDoc) {
833
833
  clinicsQuery = query(
834
834
  clinicsCollection,
@@ -0,0 +1,145 @@
1
+ # Practitioner Service
2
+
3
+ This service manages practitioner (doctor, therapist, etc.) data within the Firestore database. It handles practitioner profiles, draft profiles, registration tokens, and associations with clinics.
4
+
5
+ **Note:** Data aggregation into related entities (Clinics, Procedures) is handled by Cloud Functions triggered by Firestore events.
6
+
7
+ ## `PractitionerService` Class
8
+
9
+ Extends `BaseService`.
10
+
11
+ ### Constructor
12
+
13
+ ```typescript
14
+ constructor(
15
+ db: Firestore,
16
+ auth: Auth,
17
+ app: FirebaseApp,
18
+ clinicService?: ClinicService // Optional dependency injection
19
+ )
20
+ ```
21
+
22
+ Initializes the service with Firestore, Auth, App instances. Optionally accepts a `ClinicService` instance for operations requiring clinic data (like draft creation or token validation).
23
+
24
+ ### Methods
25
+
26
+ - **`createPractitioner(data: CreatePractitionerData): Promise<Practitioner>`**
27
+
28
+ - Creates a new, fully active practitioner profile.
29
+ - Validates data using `createPractitionerSchema`.
30
+ - Generates a unique ID using `this.generateId()`.
31
+ - Initializes default review info.
32
+ - Sets default values for `isActive` (true), `isVerified` (false), and `status` (ACTIVE).
33
+ - Saves the practitioner document.
34
+ - **Aggregation Note:** Adding practitioner info to associated `Clinics` is handled by Cloud Functions.
35
+
36
+ - **`createDraftPractitioner(data: CreateDraftPractitionerData, createdBy: string, clinicId: string): Promise<{ practitioner: Practitioner; token: PractitionerToken }>`**
37
+
38
+ - Creates a draft practitioner profile (status `DRAFT`) not yet linked to a user (`userRef` is empty). Typically used by clinic admins.
39
+ - Validates data using `createDraftPractitionerSchema`.
40
+ - Verifies that the specified `clinicId` exists.
41
+ - Associates the practitioner with the specified `clinicId` (and potentially others in `data.clinics`).
42
+ - Sets `isActive` and `isVerified` to `false` by default.
43
+ - Saves the draft practitioner document.
44
+ - Automatically creates a registration token (`PractitionerToken`) for this draft profile.
45
+ - Returns both the created draft practitioner and the registration token.
46
+
47
+ - **`createPractitionerToken(data: CreatePractitionerTokenData, createdBy: string): Promise<PractitionerToken>`**
48
+
49
+ - Creates a registration token for an existing `DRAFT` practitioner.
50
+ - Validates input data using `createPractitionerTokenSchema`.
51
+ - Ensures the practitioner exists, is in `DRAFT` status, and belongs to the specified `clinicId`.
52
+ - Generates a unique, short, uppercase token string.
53
+ - Sets a default expiration of 7 days if not provided.
54
+ - Saves the token in the `register_tokens` subcollection of the practitioner document.
55
+
56
+ - **`getPractitionerActiveTokens(practitionerId: string): Promise<PractitionerToken[]>`**
57
+
58
+ - Retrieves all `ACTIVE` and non-expired registration tokens for a specific practitioner.
59
+
60
+ - **`validateToken(tokenString: string): Promise<PractitionerToken | null>`**
61
+
62
+ - Finds an `ACTIVE`, non-expired token matching the `tokenString` across _all_ practitioners' `register_tokens` subcollections. Returns the token object or `null`.
63
+
64
+ - **`markTokenAsUsed(tokenId: string, practitionerId: string, userId: string): Promise<void>`**
65
+
66
+ - Updates a specific token's status to `USED` and records who used it (`userId`) and when.
67
+
68
+ - **`getPractitioner(practitionerId: string): Promise<Practitioner | null>`**
69
+
70
+ - Retrieves a single practitioner document by its ID.
71
+
72
+ - **`getPractitionerByUserRef(userRef: string): Promise<Practitioner | null>`**
73
+
74
+ - Finds and retrieves a practitioner document based on the linked user ID (`userRef`).
75
+
76
+ - **`getPractitionersByClinic(clinicId: string): Promise<Practitioner[]>`**
77
+
78
+ - Retrieves all `ACTIVE` practitioners associated with a specific `clinicId`.
79
+
80
+ - **`getAllPractitionersByClinic(clinicId: string): Promise<Practitioner[]>`**
81
+
82
+ - Retrieves all `ACTIVE` practitioners (regardless of status like DRAFT) associated with a `clinicId`.
83
+
84
+ - **`getDraftPractitionersByClinic(clinicId: string): Promise<Practitioner[]>`**
85
+
86
+ - Retrieves all practitioners with `DRAFT` status associated with a specific `clinicId`.
87
+
88
+ - **`updatePractitioner(practitionerId: string, data: UpdatePractitionerData): Promise<Practitioner>`**
89
+
90
+ - Updates an existing practitioner document with partial data.
91
+ - Sets the `updatedAt` timestamp.
92
+ - **Aggregation Note:** Updates to aggregated data in `Clinics` and `Procedures` are handled by Cloud Functions.
93
+
94
+ - **`addClinic(practitionerId: string, clinicId: string): Promise<void>`**
95
+
96
+ - Adds a `clinicId` to the practitioner's `clinics` array.
97
+ - Prevents duplicates.
98
+ - **Aggregation Note:** Updating the clinic's `doctors`/`doctorsInfo` is handled by Cloud Functions.
99
+
100
+ - **`removeClinic(practitionerId: string, clinicId: string): Promise<void>`**
101
+
102
+ - Removes a `clinicId` from the practitioner's `clinics` array.
103
+ - **Aggregation Note:** Updating the clinic's `doctors`/`doctorsInfo` is handled by Cloud Functions.
104
+
105
+ - **`deactivatePractitioner(practitionerId: string): Promise<void>`**
106
+
107
+ - Sets the practitioner's `isActive` flag to `false` using `updatePractitioner`.
108
+ - **Aggregation Note:** Related updates are handled by Cloud Functions.
109
+
110
+ - **`activatePractitioner(practitionerId: string): Promise<void>`**
111
+
112
+ - Sets the practitioner's `isActive` flag to `true` using `updatePractitioner`.
113
+ - **Aggregation Note:** Related updates are handled by Cloud Functions.
114
+
115
+ - **`deletePractitioner(practitionerId: string): Promise<void>`**
116
+
117
+ - Permanently deletes a practitioner document.
118
+ - **Aggregation Note:** Removal of associated data from `Clinics`, `Procedures`, etc., is handled by Cloud Functions.
119
+
120
+ - **`validateTokenAndClaimProfile(tokenString: string, userId: string): Promise<Practitioner | null>`**
121
+
122
+ - Orchestrates the process of a user claiming a draft profile:
123
+ 1. Validates the `tokenString` using `validateToken`.
124
+ 2. Retrieves the associated `DRAFT` practitioner.
125
+ 3. Checks if the user (`userId`) already has a profile.
126
+ 4. Updates the practitioner's `userRef` to the `userId` and status to `ACTIVE` using `updatePractitioner`.
127
+ 5. Marks the token as `USED` using `markTokenAsUsed`.
128
+ 6. Returns the now-claimed practitioner profile.
129
+
130
+ - **`getAllPractitioners(options?: { pagination?: number; lastDoc?: any; includeDraftPractitioners?: boolean }): Promise<{ practitioners: Practitioner[]; lastDoc: any }>`**
131
+
132
+ - Retrieves a list of practitioners, ordered by name.
133
+ - Supports pagination (`pagination`, `lastDoc`).
134
+ - Optionally includes `DRAFT` practitioners.
135
+
136
+ - **`getPractitionersByFilters(filters: { ... }): Promise<{ practitioners: Practitioner[]; lastDoc: any }>`**
137
+ - Retrieves practitioners based on complex filter criteria:
138
+ - Name (first/last)
139
+ - Certifications, Specialties
140
+ - Procedures (Family, Category, Subcategory, Technology)
141
+ - Location/Radius - Filters based on associated clinics
142
+ - Rating (`reviewInfo.averageRating`)
143
+ - Combines Firestore queries with in-memory filtering for non-indexed fields.
144
+ - Supports pagination.
145
+ - Optionally includes `DRAFT` practitioners.