@blackcode_sa/metaestetics-api 1.6.17 → 1.6.18

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.
@@ -3383,10 +3383,8 @@ var AppointmentAggregationService = class {
3383
3383
  status: "pendingNotification" /* PENDING_NOTIFICATION */,
3384
3384
  originalNotifyAtValue: notifyAtValue,
3385
3385
  originalTimeframeUnit: template.timeframe.unit,
3386
- updatedAt: admin10.firestore.Timestamp.now(),
3386
+ updatedAt: admin10.firestore.Timestamp.now()
3387
3387
  // Use current server timestamp
3388
- notificationId: void 0,
3389
- actionTakenAt: void 0
3390
3388
  };
3391
3389
  return instructionObject;
3392
3390
  });
@@ -3328,10 +3328,8 @@ var AppointmentAggregationService = class {
3328
3328
  status: "pendingNotification" /* PENDING_NOTIFICATION */,
3329
3329
  originalNotifyAtValue: notifyAtValue,
3330
3330
  originalTimeframeUnit: template.timeframe.unit,
3331
- updatedAt: admin10.firestore.Timestamp.now(),
3331
+ updatedAt: admin10.firestore.Timestamp.now()
3332
3332
  // Use current server timestamp
3333
- notificationId: void 0,
3334
- actionTakenAt: void 0
3335
3333
  };
3336
3334
  return instructionObject;
3337
3335
  });
package/dist/index.d.mts CHANGED
@@ -5,6 +5,7 @@ import { FirebaseApp } from 'firebase/app';
5
5
  import { Auth, User as User$1 } from 'firebase/auth';
6
6
  import { Analytics } from 'firebase/analytics';
7
7
  import { FirebaseStorage } from 'firebase/storage';
8
+ import { Functions } from 'firebase/functions';
8
9
 
9
10
  /**
10
11
  * Review system type definitions
@@ -5042,6 +5043,8 @@ interface FirebaseInstance {
5042
5043
  db: Firestore;
5043
5044
  auth: Auth;
5044
5045
  analytics: Analytics | null;
5046
+ storage: FirebaseStorage;
5047
+ functions: Functions;
5045
5048
  }
5046
5049
  declare const initializeFirebase: (config: {
5047
5050
  apiKey: string;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import { FirebaseApp } from 'firebase/app';
5
5
  import { Auth, User as User$1 } from 'firebase/auth';
6
6
  import { Analytics } from 'firebase/analytics';
7
7
  import { FirebaseStorage } from 'firebase/storage';
8
+ import { Functions } from 'firebase/functions';
8
9
 
9
10
  /**
10
11
  * Review system type definitions
@@ -5042,6 +5043,8 @@ interface FirebaseInstance {
5042
5043
  db: Firestore;
5043
5044
  auth: Auth;
5044
5045
  analytics: Analytics | null;
5046
+ storage: FirebaseStorage;
5047
+ functions: Functions;
5045
5048
  }
5046
5049
  declare const initializeFirebase: (config: {
5047
5050
  apiKey: string;
package/dist/index.js CHANGED
@@ -705,17 +705,21 @@ var import_firestore = require("firebase/firestore");
705
705
  var import_auth = require("firebase/auth");
706
706
  var import_analytics = require("firebase/analytics");
707
707
  var import_react_native = require("react-native");
708
+ var import_storage = require("firebase/storage");
709
+ var import_functions = require("firebase/functions");
708
710
  var firebaseInstance = null;
709
711
  var initializeFirebase = (config) => {
710
712
  if (!firebaseInstance) {
711
713
  const app = (0, import_app.initializeApp)(config);
712
714
  const db = (0, import_firestore.getFirestore)(app);
713
715
  const auth = (0, import_auth.getAuth)(app);
716
+ const storage = (0, import_storage.getStorage)(app);
717
+ const functions = (0, import_functions.getFunctions)(app);
714
718
  let analytics = null;
715
719
  if (typeof window !== "undefined" && import_react_native.Platform.OS === "web") {
716
720
  analytics = (0, import_analytics.getAnalytics)(app);
717
721
  }
718
- firebaseInstance = { app, db, auth, analytics };
722
+ firebaseInstance = { app, db, auth, analytics, storage, functions };
719
723
  }
720
724
  return firebaseInstance;
721
725
  };
@@ -1061,13 +1065,13 @@ var FirebaseErrorCode = /* @__PURE__ */ ((FirebaseErrorCode2) => {
1061
1065
  })(FirebaseErrorCode || {});
1062
1066
 
1063
1067
  // src/services/base.service.ts
1064
- var import_storage = require("firebase/storage");
1068
+ var import_storage2 = require("firebase/storage");
1065
1069
  var BaseService = class {
1066
1070
  constructor(db, auth, app) {
1067
1071
  this.db = db;
1068
1072
  this.auth = auth;
1069
1073
  this.app = app;
1070
- this.storage = (0, import_storage.getStorage)(app);
1074
+ this.storage = (0, import_storage2.getStorage)(app);
1071
1075
  }
1072
1076
  /**
1073
1077
  * Generiše jedinstveni ID za dokumente
@@ -1188,7 +1192,7 @@ var import_firestore11 = require("firebase/firestore");
1188
1192
 
1189
1193
  // src/services/patient/utils/profile.utils.ts
1190
1194
  var import_firestore6 = require("firebase/firestore");
1191
- var import_storage2 = require("firebase/storage");
1195
+ var import_storage3 = require("firebase/storage");
1192
1196
  var import_zod8 = require("zod");
1193
1197
 
1194
1198
  // src/types/patient/medical-info.types.ts
@@ -2116,9 +2120,9 @@ var updatePatientProfileByUserRefUtil = async (db, userRef, data) => {
2116
2120
  }
2117
2121
  };
2118
2122
  var uploadProfilePhotoUtil = async (storage, patientId, file) => {
2119
- const photoRef = (0, import_storage2.ref)(storage, `patient-photos/${patientId}/profile-photo`);
2120
- await (0, import_storage2.uploadBytes)(photoRef, file);
2121
- return (0, import_storage2.getDownloadURL)(photoRef);
2123
+ const photoRef = (0, import_storage3.ref)(storage, `patient-photos/${patientId}/profile-photo`);
2124
+ await (0, import_storage3.uploadBytes)(photoRef, file);
2125
+ return (0, import_storage3.getDownloadURL)(photoRef);
2122
2126
  };
2123
2127
  var updateProfilePhotoUtil = async (storage, db, patientId, file) => {
2124
2128
  const patientDoc = await (0, import_firestore6.getDoc)(getPatientDocRef(db, patientId));
@@ -2126,8 +2130,8 @@ var updateProfilePhotoUtil = async (storage, db, patientId, file) => {
2126
2130
  const patientData = patientDoc.data();
2127
2131
  if (patientData.profilePhoto) {
2128
2132
  try {
2129
- const oldPhotoRef = (0, import_storage2.ref)(storage, patientData.profilePhoto);
2130
- await (0, import_storage2.deleteObject)(oldPhotoRef);
2133
+ const oldPhotoRef = (0, import_storage3.ref)(storage, patientData.profilePhoto);
2134
+ await (0, import_storage3.deleteObject)(oldPhotoRef);
2131
2135
  } catch (error) {
2132
2136
  console.warn("Failed to delete old profile photo:", error);
2133
2137
  }
@@ -2145,8 +2149,8 @@ var deleteProfilePhotoUtil = async (storage, db, patientId) => {
2145
2149
  const patientData = patientDoc.data();
2146
2150
  if (patientData.profilePhoto) {
2147
2151
  try {
2148
- const photoRef = (0, import_storage2.ref)(storage, patientData.profilePhoto);
2149
- await (0, import_storage2.deleteObject)(photoRef);
2152
+ const photoRef = (0, import_storage3.ref)(storage, patientData.profilePhoto);
2153
+ await (0, import_storage3.deleteObject)(photoRef);
2150
2154
  } catch (error) {
2151
2155
  console.warn("Failed to delete profile photo:", error);
2152
2156
  }
@@ -5130,7 +5134,7 @@ var import_geofire_common3 = require("geofire-common");
5130
5134
  var import_zod16 = require("zod");
5131
5135
 
5132
5136
  // src/services/clinic/utils/photos.utils.ts
5133
- var import_storage3 = require("firebase/storage");
5137
+ var import_storage4 = require("firebase/storage");
5134
5138
  async function uploadPhoto(photo, entityType, entityId, photoType, app, fileName) {
5135
5139
  if (!photo || typeof photo !== "string" || !photo.startsWith("data:")) {
5136
5140
  return photo;
@@ -5139,9 +5143,9 @@ async function uploadPhoto(photo, entityType, entityId, photoType, app, fileName
5139
5143
  console.log(
5140
5144
  `[PHOTO_UTILS] Uploading ${photoType} for ${entityType}/${entityId}`
5141
5145
  );
5142
- const storage = (0, import_storage3.getStorage)(app);
5146
+ const storage = (0, import_storage4.getStorage)(app);
5143
5147
  const storageFileName = fileName || `${photoType}-${Date.now()}`;
5144
- const storageRef = (0, import_storage3.ref)(
5148
+ const storageRef = (0, import_storage4.ref)(
5145
5149
  storage,
5146
5150
  `${entityType}/${entityId}/${storageFileName}`
5147
5151
  );
@@ -5153,8 +5157,8 @@ async function uploadPhoto(photo, entityType, entityId, photoType, app, fileName
5153
5157
  byteArrays.push(byteCharacters.charCodeAt(i));
5154
5158
  }
5155
5159
  const blob = new Blob([new Uint8Array(byteArrays)], { type: contentType });
5156
- await (0, import_storage3.uploadBytes)(storageRef, blob, { contentType });
5157
- const downloadUrl = await (0, import_storage3.getDownloadURL)(storageRef);
5160
+ await (0, import_storage4.uploadBytes)(storageRef, blob, { contentType });
5161
+ const downloadUrl = await (0, import_storage4.getDownloadURL)(storageRef);
5158
5162
  console.log(`[PHOTO_UTILS] ${photoType} uploaded successfully`, {
5159
5163
  downloadUrl
5160
5164
  });
@@ -12163,7 +12167,7 @@ var ReviewService = class extends BaseService {
12163
12167
 
12164
12168
  // src/services/appointment/appointment.service.ts
12165
12169
  var import_firestore40 = require("firebase/firestore");
12166
- var import_functions = require("firebase/functions");
12170
+ var import_functions2 = require("firebase/functions");
12167
12171
 
12168
12172
  // src/services/appointment/utils/appointment.utils.ts
12169
12173
  var import_firestore39 = require("firebase/firestore");
@@ -12377,7 +12381,7 @@ var AppointmentService = class extends BaseService {
12377
12381
  this.practitionerService = practitionerService;
12378
12382
  this.clinicService = clinicService;
12379
12383
  this.filledDocumentService = filledDocumentService;
12380
- this.functions = (0, import_functions.getFunctions)(app, "europe-west6");
12384
+ this.functions = (0, import_functions2.getFunctions)(app, "europe-west6");
12381
12385
  }
12382
12386
  /**
12383
12387
  * Gets available booking slots for a specific clinic, practitioner, and procedure using HTTP request.
package/dist/index.mjs CHANGED
@@ -471,17 +471,21 @@ import { getFirestore } from "firebase/firestore";
471
471
  import { getAuth } from "firebase/auth";
472
472
  import { getAnalytics } from "firebase/analytics";
473
473
  import { Platform } from "react-native";
474
+ import { getStorage } from "firebase/storage";
475
+ import { getFunctions } from "firebase/functions";
474
476
  var firebaseInstance = null;
475
477
  var initializeFirebase = (config) => {
476
478
  if (!firebaseInstance) {
477
479
  const app = initializeApp(config);
478
480
  const db = getFirestore(app);
479
481
  const auth = getAuth(app);
482
+ const storage = getStorage(app);
483
+ const functions = getFunctions(app);
480
484
  let analytics = null;
481
485
  if (typeof window !== "undefined" && Platform.OS === "web") {
482
486
  analytics = getAnalytics(app);
483
487
  }
484
- firebaseInstance = { app, db, auth, analytics };
488
+ firebaseInstance = { app, db, auth, analytics, storage, functions };
485
489
  }
486
490
  return firebaseInstance;
487
491
  };
@@ -846,13 +850,13 @@ var FirebaseErrorCode = /* @__PURE__ */ ((FirebaseErrorCode2) => {
846
850
  })(FirebaseErrorCode || {});
847
851
 
848
852
  // src/services/base.service.ts
849
- import { getStorage } from "firebase/storage";
853
+ import { getStorage as getStorage2 } from "firebase/storage";
850
854
  var BaseService = class {
851
855
  constructor(db, auth, app) {
852
856
  this.db = db;
853
857
  this.auth = auth;
854
858
  this.app = app;
855
- this.storage = getStorage(app);
859
+ this.storage = getStorage2(app);
856
860
  }
857
861
  /**
858
862
  * Generiše jedinstveni ID za dokumente
@@ -5036,7 +5040,7 @@ import { z as z16 } from "zod";
5036
5040
 
5037
5041
  // src/services/clinic/utils/photos.utils.ts
5038
5042
  import {
5039
- getStorage as getStorage2,
5043
+ getStorage as getStorage3,
5040
5044
  ref as ref2,
5041
5045
  uploadBytes as uploadBytes2,
5042
5046
  getDownloadURL as getDownloadURL2,
@@ -5050,7 +5054,7 @@ async function uploadPhoto(photo, entityType, entityId, photoType, app, fileName
5050
5054
  console.log(
5051
5055
  `[PHOTO_UTILS] Uploading ${photoType} for ${entityType}/${entityId}`
5052
5056
  );
5053
- const storage = getStorage2(app);
5057
+ const storage = getStorage3(app);
5054
5058
  const storageFileName = fileName || `${photoType}-${Date.now()}`;
5055
5059
  const storageRef = ref2(
5056
5060
  storage,
@@ -12258,7 +12262,7 @@ import {
12258
12262
  startAfter as startAfter11,
12259
12263
  getDocs as getDocs26
12260
12264
  } from "firebase/firestore";
12261
- import { getFunctions } from "firebase/functions";
12265
+ import { getFunctions as getFunctions2 } from "firebase/functions";
12262
12266
 
12263
12267
  // src/services/appointment/utils/appointment.utils.ts
12264
12268
  import {
@@ -12486,7 +12490,7 @@ var AppointmentService = class extends BaseService {
12486
12490
  this.practitionerService = practitionerService;
12487
12491
  this.clinicService = clinicService;
12488
12492
  this.filledDocumentService = filledDocumentService;
12489
- this.functions = getFunctions(app, "europe-west6");
12493
+ this.functions = getFunctions2(app, "europe-west6");
12490
12494
  }
12491
12495
  /**
12492
12496
  * Gets available booking slots for a specific clinic, practitioner, and procedure using HTTP request.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.6.17",
4
+ "version": "1.6.18",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -566,8 +566,6 @@ export class AppointmentAggregationService {
566
566
  originalNotifyAtValue: notifyAtValue,
567
567
  originalTimeframeUnit: template.timeframe.unit,
568
568
  updatedAt: admin.firestore.Timestamp.now() as any, // Use current server timestamp
569
- notificationId: undefined,
570
- actionTakenAt: undefined,
571
569
  };
572
570
  return instructionObject;
573
571
  });
@@ -0,0 +1,404 @@
1
+ import * as admin from "firebase-admin";
2
+ import { User, UserRole, USERS_COLLECTION } from "../../types";
3
+ import {
4
+ PatientProfile,
5
+ PATIENTS_COLLECTION,
6
+ CreatePatientProfileData,
7
+ PatientSensitiveInfo,
8
+ PATIENT_SENSITIVE_INFO_COLLECTION,
9
+ CreatePatientSensitiveInfoData,
10
+ Gender,
11
+ PatientMedicalInfo,
12
+ PATIENT_MEDICAL_INFO_COLLECTION,
13
+ } from "../../types/patient";
14
+
15
+ /**
16
+ * @class UserProfileAdminService
17
+ * @description Handles user profile management operations for admin tasks
18
+ */
19
+ export class UserProfileAdminService {
20
+ private db: admin.firestore.Firestore;
21
+
22
+ /**
23
+ * Constructor for UserProfileAdminService
24
+ * @param firestore Optional Firestore instance. If not provided, uses the default admin SDK instance.
25
+ */
26
+ constructor(firestore?: admin.firestore.Firestore) {
27
+ this.db = firestore || admin.firestore();
28
+ }
29
+
30
+ /**
31
+ * Creates a blank user profile with minimal information
32
+ * @param authUserData Basic user data from Firebase Auth
33
+ * @returns The created user document
34
+ */
35
+ async createBlankUserProfile(authUserData: {
36
+ uid: string;
37
+ email: string | null;
38
+ isAnonymous: boolean;
39
+ }): Promise<User> {
40
+ console.log(
41
+ `[UserProfileAdminService] Creating blank user profile for user ${authUserData.uid}`
42
+ );
43
+
44
+ const userData: User = {
45
+ uid: authUserData.uid,
46
+ email: authUserData.email,
47
+ roles: [], // Empty roles array as requested
48
+ isAnonymous: authUserData.isAnonymous,
49
+ createdAt: admin.firestore.FieldValue.serverTimestamp(),
50
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
51
+ lastLoginAt: admin.firestore.FieldValue.serverTimestamp(),
52
+ };
53
+
54
+ try {
55
+ const userRef = this.db
56
+ .collection(USERS_COLLECTION)
57
+ .doc(authUserData.uid);
58
+ await userRef.set(userData);
59
+
60
+ // Fetch the created document to return with server timestamps
61
+ const userDoc = await userRef.get();
62
+ return userDoc.data() as User;
63
+ } catch (error) {
64
+ console.error(
65
+ `[UserProfileAdminService] Error creating blank user profile for ${authUserData.uid}:`,
66
+ error
67
+ );
68
+ throw error;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Initializes patient role for a user and creates all required patient documents
74
+ * Creates patient profile, sensitive info, and medical info in one operation
75
+ *
76
+ * @param userId The user ID to initialize with patient role
77
+ * @param options Optional data for different aspects of patient initialization
78
+ * @returns Object containing the updated user and all created patient documents
79
+ */
80
+ async initializePatientRole(
81
+ userId: string,
82
+ options?: {
83
+ profileData?: Partial<CreatePatientProfileData>;
84
+ sensitiveData?: Partial<CreatePatientSensitiveInfoData>;
85
+ }
86
+ ): Promise<{
87
+ user: User;
88
+ patientProfile: PatientProfile;
89
+ patientSensitiveInfo: PatientSensitiveInfo;
90
+ patientMedicalInfo: PatientMedicalInfo;
91
+ }> {
92
+ console.log(
93
+ `[UserProfileAdminService] Initializing complete patient role for user ${userId}`
94
+ );
95
+
96
+ try {
97
+ // Get user document
98
+ const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
99
+ const userDoc = await userRef.get();
100
+
101
+ if (!userDoc.exists) {
102
+ throw new Error(`User ${userId} not found`);
103
+ }
104
+
105
+ const userData = userDoc.data() as User;
106
+ let patientProfileId: string | undefined = userData.patientProfile;
107
+
108
+ // Create patient profile if it doesn't exist
109
+ let patientProfile: PatientProfile;
110
+ if (!patientProfileId) {
111
+ // Create patient profile
112
+ const patientProfileRef = this.db.collection(PATIENTS_COLLECTION).doc();
113
+ patientProfileId = patientProfileRef.id;
114
+
115
+ // Set default profile data
116
+ const defaultProfileData: CreatePatientProfileData = {
117
+ userRef: userId,
118
+ displayName: "Patient", // Default display name
119
+ expoTokens: [],
120
+ gamification: {
121
+ level: 1,
122
+ points: 0,
123
+ },
124
+ isActive: true,
125
+ isVerified: false,
126
+ doctors: [],
127
+ clinics: [],
128
+ doctorIds: [],
129
+ clinicIds: [],
130
+ };
131
+
132
+ // Merge with any provided profile data
133
+ const mergedProfileData = {
134
+ ...defaultProfileData,
135
+ ...options?.profileData,
136
+ };
137
+
138
+ // Create the patient profile document with explicit properties
139
+ const patientProfileData = {
140
+ id: patientProfileId,
141
+ userRef: mergedProfileData.userRef,
142
+ displayName: mergedProfileData.displayName,
143
+ profilePhoto: null,
144
+ gamification: mergedProfileData.gamification || {
145
+ level: 1,
146
+ points: 0,
147
+ },
148
+ expoTokens: mergedProfileData.expoTokens || [],
149
+ isActive: mergedProfileData.isActive,
150
+ isVerified: mergedProfileData.isVerified,
151
+ doctors: mergedProfileData.doctors || [],
152
+ clinics: mergedProfileData.clinics || [],
153
+ doctorIds: mergedProfileData.doctorIds || [],
154
+ clinicIds: mergedProfileData.clinicIds || [],
155
+ createdAt: admin.firestore.FieldValue.serverTimestamp(),
156
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
157
+ };
158
+
159
+ // Store the document
160
+ await patientProfileRef.set(patientProfileData);
161
+
162
+ // For returning to the client, use type assertions
163
+ patientProfile = {
164
+ ...patientProfileData,
165
+ createdAt: null as any,
166
+ updatedAt: null as any,
167
+ } as unknown as PatientProfile;
168
+
169
+ console.log(
170
+ `[UserProfileAdminService] Creating patient profile with ID ${patientProfileId}`
171
+ );
172
+ } else {
173
+ // Get existing patient profile
174
+ const patientProfileRef = this.db
175
+ .collection(PATIENTS_COLLECTION)
176
+ .doc(patientProfileId);
177
+ const patientProfileDoc = await patientProfileRef.get();
178
+
179
+ if (!patientProfileDoc.exists) {
180
+ throw new Error(
181
+ `Patient profile ${patientProfileId} exists in user but not in patients collection`
182
+ );
183
+ }
184
+
185
+ patientProfile = patientProfileDoc.data() as PatientProfile;
186
+ }
187
+
188
+ // Create sensitive information document
189
+ const sensitiveInfoRef = this.db
190
+ .collection(PATIENTS_COLLECTION)
191
+ .doc(patientProfileId)
192
+ .collection(PATIENT_SENSITIVE_INFO_COLLECTION)
193
+ .doc("info"); // Use 'info' as standard document ID for subcollection
194
+
195
+ // Check if sensitive info document already exists
196
+ const sensitiveInfoDoc = await sensitiveInfoRef.get();
197
+ let patientSensitiveInfo: PatientSensitiveInfo;
198
+
199
+ if (!sensitiveInfoDoc.exists) {
200
+ // Create default sensitive info data
201
+ const defaultSensitiveData: CreatePatientSensitiveInfoData = {
202
+ patientId: patientProfileId,
203
+ userRef: userId,
204
+ firstName: "",
205
+ lastName: "",
206
+ dateOfBirth: null,
207
+ gender: Gender.PREFER_NOT_TO_SAY,
208
+ };
209
+
210
+ // Merge with provided data
211
+ const mergedSensitiveData = {
212
+ ...defaultSensitiveData,
213
+ ...options?.sensitiveData,
214
+ };
215
+
216
+ // Create sensitive info
217
+ const sensitiveInfoData = {
218
+ ...mergedSensitiveData,
219
+ createdAt: admin.firestore.FieldValue.serverTimestamp(),
220
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
221
+ // Leave dateOfBirth as is
222
+ };
223
+
224
+ // Store the document
225
+ await sensitiveInfoRef.set(sensitiveInfoData);
226
+
227
+ // Convert for client return with type assertions
228
+ patientSensitiveInfo = {
229
+ ...sensitiveInfoData,
230
+ createdAt: null as any,
231
+ updatedAt: null as any,
232
+ } as unknown as PatientSensitiveInfo;
233
+
234
+ console.log(
235
+ `[UserProfileAdminService] Creating sensitive info in subcollection for patient ${patientProfileId}`
236
+ );
237
+ } else {
238
+ patientSensitiveInfo = sensitiveInfoDoc.data() as PatientSensitiveInfo;
239
+ }
240
+
241
+ // Create medical info document as a subcollection
242
+ const medicalInfoRef = this.db
243
+ .collection(PATIENTS_COLLECTION)
244
+ .doc(patientProfileId)
245
+ .collection(PATIENT_MEDICAL_INFO_COLLECTION)
246
+ .doc("info"); // Use 'info' as standard document ID for subcollection
247
+
248
+ // Check if medical info document already exists
249
+ const medicalInfoDoc = await medicalInfoRef.get();
250
+ let patientMedicalInfo: PatientMedicalInfo;
251
+
252
+ if (!medicalInfoDoc.exists) {
253
+ // Create medical info
254
+ const medicalInfoData = {
255
+ patientId: patientProfileId,
256
+ vitalStats: {},
257
+ blockingConditions: [],
258
+ contraindications: [],
259
+ allergies: [],
260
+ currentMedications: [],
261
+ lastUpdated: admin.firestore.FieldValue.serverTimestamp(),
262
+ updatedBy: userId,
263
+ };
264
+
265
+ // Store the document
266
+ await medicalInfoRef.set(medicalInfoData);
267
+
268
+ // Convert for client return with type assertions
269
+ patientMedicalInfo = {
270
+ ...medicalInfoData,
271
+ lastUpdated: null as any,
272
+ } as unknown as PatientMedicalInfo;
273
+
274
+ console.log(
275
+ `[UserProfileAdminService] Creating medical info in subcollection for patient ${patientProfileId}`
276
+ );
277
+ } else {
278
+ patientMedicalInfo = medicalInfoDoc.data() as PatientMedicalInfo;
279
+ }
280
+
281
+ // Update user document with patient role and profile reference
282
+ const batch = this.db.batch();
283
+
284
+ // Add patient role if not already present
285
+ if (!userData.roles.includes(UserRole.PATIENT)) {
286
+ batch.update(userRef, {
287
+ roles: admin.firestore.FieldValue.arrayUnion(UserRole.PATIENT),
288
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
289
+ });
290
+ }
291
+
292
+ // Add patient profile reference if not already set
293
+ if (!userData.patientProfile) {
294
+ batch.update(userRef, {
295
+ patientProfile: patientProfileId,
296
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
297
+ });
298
+ }
299
+
300
+ await batch.commit();
301
+
302
+ // Get updated user document
303
+ const updatedUserDoc = await userRef.get();
304
+ const updatedUser = updatedUserDoc.data() as User;
305
+
306
+ console.log(
307
+ `[UserProfileAdminService] Successfully initialized patient role for user ${userId}`
308
+ );
309
+
310
+ return {
311
+ user: updatedUser,
312
+ patientProfile,
313
+ patientSensitiveInfo,
314
+ patientMedicalInfo,
315
+ };
316
+ } catch (error) {
317
+ console.error(
318
+ `[UserProfileAdminService] Error initializing patient role for user ${userId}:`,
319
+ error
320
+ );
321
+ throw error;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Initializes clinic admin role for a user
327
+ * @param userId The user ID to initialize with clinic admin role
328
+ * @returns The updated user document
329
+ */
330
+ async initializeClinicAdminRole(userId: string): Promise<User> {
331
+ console.log(
332
+ `[UserProfileAdminService] Initializing clinic admin role for user ${userId}`
333
+ );
334
+
335
+ try {
336
+ const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
337
+ const userDoc = await userRef.get();
338
+
339
+ if (!userDoc.exists) {
340
+ throw new Error(`User ${userId} not found`);
341
+ }
342
+
343
+ const userData = userDoc.data() as User;
344
+
345
+ // Only add the role if it doesn't already exist
346
+ if (!userData.roles.includes(UserRole.CLINIC_ADMIN)) {
347
+ await userRef.update({
348
+ roles: admin.firestore.FieldValue.arrayUnion(UserRole.CLINIC_ADMIN),
349
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
350
+ });
351
+ }
352
+
353
+ // Return the updated user document
354
+ const updatedUserDoc = await userRef.get();
355
+ return updatedUserDoc.data() as User;
356
+ } catch (error) {
357
+ console.error(
358
+ `[UserProfileAdminService] Error initializing clinic admin role for user ${userId}:`,
359
+ error
360
+ );
361
+ throw error;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Initializes practitioner role for a user
367
+ * @param userId The user ID to initialize with practitioner role
368
+ * @returns The updated user document
369
+ */
370
+ async initializePractitionerRole(userId: string): Promise<User> {
371
+ console.log(
372
+ `[UserProfileAdminService] Initializing practitioner role for user ${userId}`
373
+ );
374
+
375
+ try {
376
+ const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
377
+ const userDoc = await userRef.get();
378
+
379
+ if (!userDoc.exists) {
380
+ throw new Error(`User ${userId} not found`);
381
+ }
382
+
383
+ const userData = userDoc.data() as User;
384
+
385
+ // Only add the role if it doesn't already exist
386
+ if (!userData.roles.includes(UserRole.PRACTITIONER)) {
387
+ await userRef.update({
388
+ roles: admin.firestore.FieldValue.arrayUnion(UserRole.PRACTITIONER),
389
+ updatedAt: admin.firestore.FieldValue.serverTimestamp(),
390
+ });
391
+ }
392
+
393
+ // Return the updated user document
394
+ const updatedUserDoc = await userRef.get();
395
+ return updatedUserDoc.data() as User;
396
+ } catch (error) {
397
+ console.error(
398
+ `[UserProfileAdminService] Error initializing practitioner role for user ${userId}:`,
399
+ error
400
+ );
401
+ throw error;
402
+ }
403
+ }
404
+ }