@blackcode_sa/metaestetics-api 1.14.23 → 1.14.27

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.
@@ -46,12 +46,18 @@ export class BrandService extends BaseService {
46
46
  }
47
47
 
48
48
  /**
49
- * Gets a paginated list of active brands, optionally filtered by name.
49
+ * Gets a paginated list of active brands, optionally filtered by name and category.
50
50
  * @param rowsPerPage - The number of brands to fetch.
51
51
  * @param searchTerm - An optional string to filter brand names by (starts-with search).
52
52
  * @param lastVisible - An optional document snapshot to use as a cursor for pagination.
53
+ * @param category - An optional category to filter brands by.
53
54
  */
54
- async getAll(rowsPerPage: number, searchTerm?: string, lastVisible?: any) {
55
+ async getAll(
56
+ rowsPerPage: number,
57
+ searchTerm?: string,
58
+ lastVisible?: any,
59
+ category?: string
60
+ ) {
55
61
  const constraints: QueryConstraint[] = [
56
62
  where("isActive", "==", true),
57
63
  orderBy("name_lowercase"),
@@ -65,6 +71,10 @@ export class BrandService extends BaseService {
65
71
  );
66
72
  }
67
73
 
74
+ if (category) {
75
+ constraints.push(where("category", "==", category));
76
+ }
77
+
68
78
  if (lastVisible) {
69
79
  constraints.push(startAfter(lastVisible));
70
80
  }
@@ -87,10 +97,11 @@ export class BrandService extends BaseService {
87
97
  }
88
98
 
89
99
  /**
90
- * Gets the total count of active brands, optionally filtered by name.
100
+ * Gets the total count of active brands, optionally filtered by name and category.
91
101
  * @param searchTerm - An optional string to filter brand names by (starts-with search).
102
+ * @param category - An optional category to filter brands by.
92
103
  */
93
- async getBrandsCount(searchTerm?: string) {
104
+ async getBrandsCount(searchTerm?: string, category?: string) {
94
105
  const constraints: QueryConstraint[] = [where("isActive", "==", true)];
95
106
 
96
107
  if (searchTerm) {
@@ -101,6 +112,10 @@ export class BrandService extends BaseService {
101
112
  );
102
113
  }
103
114
 
115
+ if (category) {
116
+ constraints.push(where("category", "==", category));
117
+ }
118
+
104
119
  const q = query(this.getBrandsRef(), ...constraints);
105
120
  const snapshot = await getCountFromServer(q);
106
121
  return snapshot.data().count;
@@ -184,6 +199,7 @@ export class BrandService extends BaseService {
184
199
  "id",
185
200
  "name",
186
201
  "manufacturer",
202
+ "category",
187
203
  "website",
188
204
  "description",
189
205
  "isActive",
@@ -230,6 +246,7 @@ export class BrandService extends BaseService {
230
246
  brand.id ?? "",
231
247
  brand.name ?? "",
232
248
  brand.manufacturer ?? "",
249
+ brand.category ?? "",
233
250
  brand.website ?? "",
234
251
  brand.description ?? "",
235
252
  String(brand.isActive ?? ""),
@@ -7,6 +7,7 @@
7
7
  * @property manufacturer - Naziv proizvođača
8
8
  * @property description - Detaljan opis brenda i njegovih proizvoda
9
9
  * @property website - Web stranica brenda
10
+ * @property category - Kategorija brenda (npr. "laser", "peeling", "injectables") - za filtriranje
10
11
  * @property isActive - Da li je brend aktivan u sistemu
11
12
  * @property createdAt - Datum kreiranja
12
13
  * @property updatedAt - Datum poslednjeg ažuriranja
@@ -21,6 +22,7 @@ export interface Brand {
21
22
  isActive: boolean;
22
23
  website?: string;
23
24
  description?: string;
25
+ category?: string;
24
26
  }
25
27
 
26
28
  /**
@@ -45,7 +45,6 @@ import { FirebaseErrorCode } from '../../errors/firebase.errors';
45
45
  import { FirebaseError } from '../../errors/firebase.errors';
46
46
  import { BaseService } from '../base.service';
47
47
  import { UserService } from '../user/user.service';
48
- import { throws } from 'assert';
49
48
  import {
50
49
  ClinicGroup,
51
50
  AdminToken,
@@ -639,11 +638,23 @@ export class AuthService extends BaseService {
639
638
  }
640
639
 
641
640
  const firebaseError = error as FirebaseError;
642
- if (firebaseError.code === FirebaseErrorCode.USER_NOT_FOUND) {
643
- throw AUTH_ERRORS.USER_NOT_FOUND;
641
+
642
+ // Handle specific Firebase errors
643
+ switch (firebaseError.code) {
644
+ case FirebaseErrorCode.USER_NOT_FOUND:
645
+ throw AUTH_ERRORS.USER_NOT_FOUND;
646
+ case FirebaseErrorCode.INVALID_EMAIL:
647
+ throw AUTH_ERRORS.INVALID_EMAIL;
648
+ case FirebaseErrorCode.TOO_MANY_REQUESTS:
649
+ throw AUTH_ERRORS.TOO_MANY_REQUESTS;
650
+ case FirebaseErrorCode.NETWORK_ERROR:
651
+ throw AUTH_ERRORS.NETWORK_ERROR;
652
+ case FirebaseErrorCode.OPERATION_NOT_ALLOWED:
653
+ throw AUTH_ERRORS.OPERATION_NOT_ALLOWED;
654
+ default:
655
+ // Re-throw unknown errors as-is
656
+ throw error;
644
657
  }
645
-
646
- throw error;
647
658
  }
648
659
  }
649
660
 
@@ -86,6 +86,7 @@ import {
86
86
  getPatientsByPractitionerUtil,
87
87
  getPatientsByPractitionerWithDetailsUtil,
88
88
  getPatientsByClinicUtil,
89
+ getPatientsByClinicWithDetailsUtil,
89
90
  createPatientTokenUtil,
90
91
  validatePatientTokenUtil,
91
92
  markPatientTokenAsUsedUtil,
@@ -763,13 +764,14 @@ export class PatientService extends BaseService {
763
764
  }
764
765
 
765
766
  /**
766
- * Gets all patients associated with a specific clinic.
767
+ * Gets all patients associated with a specific clinic, including sensitive info.
768
+ * This merges data from PatientProfile and PatientSensitiveInfo subcollection.
767
769
  *
768
770
  * @param {string} clinicId - ID of the clinic whose patients to retrieve
769
771
  * @param {Object} options - Optional parameters for pagination
770
772
  * @param {number} options.limit - Maximum number of profiles to return
771
773
  * @param {string} options.startAfter - The ID of the document to start after (for pagination)
772
- * @returns {Promise<PatientProfile[]>} A promise resolving to an array of patient profiles
774
+ * @returns {Promise<PatientProfile[]>} A promise resolving to an array of patient profiles with merged sensitive info
773
775
  */
774
776
  async getPatientsByClinic(
775
777
  clinicId: string,
@@ -778,8 +780,8 @@ export class PatientService extends BaseService {
778
780
  startAfter?: string;
779
781
  },
780
782
  ): Promise<PatientProfile[]> {
781
- console.log(`[PatientService.getPatientsByClinic] Fetching patients for clinic: ${clinicId}`);
782
- return getPatientsByClinicUtil(this.db, clinicId, options);
783
+ console.log(`[PatientService.getPatientsByClinic] Fetching patients with details for clinic: ${clinicId}`);
784
+ return getPatientsByClinicWithDetailsUtil(this.db, clinicId, options);
783
785
  }
784
786
 
785
787
  /**
@@ -10,7 +10,12 @@ import {
10
10
  getDoc,
11
11
  QueryConstraint,
12
12
  } from "firebase/firestore";
13
- import { PatientProfile, PATIENTS_COLLECTION } from "../../../types/patient";
13
+ import {
14
+ PatientProfile,
15
+ PATIENTS_COLLECTION,
16
+ PatientSensitiveInfo,
17
+ } from "../../../types/patient";
18
+ import { getSensitiveInfoDocRef } from "./docs.utils";
14
19
 
15
20
  /**
16
21
  * Retrieves all patients associated with a specific clinic with pagination support.
@@ -78,3 +83,75 @@ export const getPatientsByClinicUtil = async (
78
83
  );
79
84
  }
80
85
  };
86
+
87
+ /**
88
+ * Retrieves all patients associated with a specific clinic, including their sensitive info.
89
+ * This merges data from PatientProfile and PatientSensitiveInfo subcollection.
90
+ *
91
+ * @param {Firestore} db - Firestore instance
92
+ * @param {string} clinicId - ID of the clinic whose patients to retrieve
93
+ * @param {Object} options - Optional parameters for pagination
94
+ * @param {number} options.limit - Maximum number of profiles to return
95
+ * @param {string} options.startAfter - The ID of the document to start after (for pagination)
96
+ * @returns {Promise<PatientProfile[]>} A promise resolving to an array of patient profiles with merged sensitive info
97
+ */
98
+ export const getPatientsByClinicWithDetailsUtil = async (
99
+ db: Firestore,
100
+ clinicId: string,
101
+ options?: { limit?: number; startAfter?: string }
102
+ ): Promise<PatientProfile[]> => {
103
+ try {
104
+ console.log(
105
+ `[getPatientsByClinicWithDetailsUtil] Fetching patients with details for clinic ID: ${clinicId} with options:`,
106
+ options
107
+ );
108
+
109
+ // First, get all patient profiles for this clinic
110
+ const patientProfiles = await getPatientsByClinicUtil(db, clinicId, options);
111
+
112
+ // Then, fetch sensitive info for each patient and merge it
113
+ const patientsWithDetails: PatientProfile[] = await Promise.all(
114
+ patientProfiles.map(async (profile) => {
115
+ try {
116
+ const sensitiveInfoDoc = await getDoc(
117
+ getSensitiveInfoDocRef(db, profile.id)
118
+ );
119
+ const sensitiveInfo = sensitiveInfoDoc.exists()
120
+ ? (sensitiveInfoDoc.data() as PatientSensitiveInfo)
121
+ : null;
122
+
123
+ // Merge sensitive info into profile (sensitive info takes precedence)
124
+ return {
125
+ ...profile,
126
+ // Merge phoneNumber from sensitive info if not in profile
127
+ phoneNumber: profile.phoneNumber || sensitiveInfo?.phoneNumber || null,
128
+ // Merge dateOfBirth from sensitive info if not in profile
129
+ dateOfBirth: profile.dateOfBirth || sensitiveInfo?.dateOfBirth || null,
130
+ } as PatientProfile;
131
+ } catch (error) {
132
+ console.error(
133
+ `[getPatientsByClinicWithDetailsUtil] Error fetching sensitive info for patient ${profile.id}:`,
134
+ error
135
+ );
136
+ // Return profile without sensitive info in case of error
137
+ return profile;
138
+ }
139
+ })
140
+ );
141
+
142
+ console.log(
143
+ `[getPatientsByClinicWithDetailsUtil] Found ${patientsWithDetails.length} patients with details for clinic ID: ${clinicId}`
144
+ );
145
+ return patientsWithDetails;
146
+ } catch (error) {
147
+ console.error(
148
+ `[getPatientsByClinicWithDetailsUtil] Error fetching patients with details:`,
149
+ error
150
+ );
151
+ throw new Error(
152
+ `Failed to retrieve patients with details: ${
153
+ error instanceof Error ? error.message : String(error)
154
+ }`
155
+ );
156
+ }
157
+ };