@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.
- package/dist/admin/index.js +1 -3
- package/dist/admin/index.mjs +1 -3
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +22 -18
- package/dist/index.mjs +11 -7
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +0 -2
- package/src/admin/users/user-profile.admin.ts +404 -0
- package/src/config/firebase.ts +17 -1
- package/src/services/auth.v2.service.ts +959 -0
- package/src/services/user.v2.service.ts +466 -0
package/dist/admin/index.js
CHANGED
|
@@ -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
|
});
|
package/dist/admin/index.mjs
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
|
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,
|
|
2120
|
-
await (0,
|
|
2121
|
-
return (0,
|
|
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,
|
|
2130
|
-
await (0,
|
|
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,
|
|
2149
|
-
await (0,
|
|
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
|
|
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,
|
|
5146
|
+
const storage = (0, import_storage4.getStorage)(app);
|
|
5143
5147
|
const storageFileName = fileName || `${photoType}-${Date.now()}`;
|
|
5144
|
-
const storageRef = (0,
|
|
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,
|
|
5157
|
-
const downloadUrl = await (0,
|
|
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
|
|
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,
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
@@ -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
|
+
}
|