@blackcode_sa/metaestetics-api 1.12.62 → 1.12.64
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.d.mts +4 -2
- package/dist/admin/index.d.ts +4 -2
- package/dist/admin/index.js +4 -45
- package/dist/admin/index.mjs +4 -45
- package/dist/backoffice/index.d.mts +86 -1
- package/dist/backoffice/index.d.ts +86 -1
- package/dist/backoffice/index.js +308 -0
- package/dist/backoffice/index.mjs +306 -0
- package/dist/index.d.mts +99 -3
- package/dist/index.d.ts +99 -3
- package/dist/index.js +545 -281
- package/dist/index.mjs +867 -603
- package/package.json +119 -119
- package/src/__mocks__/firstore.ts +10 -10
- package/src/admin/aggregation/README.md +79 -79
- package/src/admin/aggregation/appointment/README.md +128 -128
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +1844 -1844
- package/src/admin/aggregation/appointment/index.ts +1 -1
- package/src/admin/aggregation/clinic/README.md +52 -52
- package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +703 -703
- package/src/admin/aggregation/clinic/index.ts +1 -1
- package/src/admin/aggregation/forms/README.md +13 -13
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +322 -322
- package/src/admin/aggregation/forms/index.ts +1 -1
- package/src/admin/aggregation/index.ts +8 -8
- package/src/admin/aggregation/patient/README.md +27 -27
- package/src/admin/aggregation/patient/index.ts +1 -1
- package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -141
- package/src/admin/aggregation/practitioner/README.md +42 -42
- package/src/admin/aggregation/practitioner/index.ts +1 -1
- package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -433
- package/src/admin/aggregation/practitioner-invite/index.ts +1 -1
- package/src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts +961 -961
- package/src/admin/aggregation/procedure/README.md +43 -43
- package/src/admin/aggregation/procedure/index.ts +1 -1
- package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +702 -702
- package/src/admin/aggregation/reviews/index.ts +1 -1
- package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +641 -689
- package/src/admin/booking/README.md +125 -125
- package/src/admin/booking/booking.admin.ts +1037 -1037
- package/src/admin/booking/booking.calculator.ts +712 -712
- package/src/admin/booking/booking.types.ts +59 -59
- package/src/admin/booking/index.ts +3 -3
- package/src/admin/booking/timezones-problem.md +185 -185
- package/src/admin/calendar/README.md +7 -7
- package/src/admin/calendar/calendar.admin.service.ts +345 -345
- package/src/admin/calendar/index.ts +1 -1
- package/src/admin/documentation-templates/document-manager.admin.ts +260 -260
- package/src/admin/documentation-templates/index.ts +1 -1
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +148 -148
- package/src/admin/free-consultation/index.ts +1 -1
- package/src/admin/index.ts +75 -75
- package/src/admin/logger/index.ts +78 -78
- package/src/admin/mailing/README.md +95 -95
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +732 -732
- package/src/admin/mailing/appointment/index.ts +1 -1
- package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -40
- package/src/admin/mailing/base.mailing.service.ts +208 -208
- package/src/admin/mailing/index.ts +3 -3
- package/src/admin/mailing/practitionerInvite/existing-practitioner-invite.mailing.ts +611 -611
- package/src/admin/mailing/practitionerInvite/index.ts +2 -2
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +395 -395
- package/src/admin/mailing/practitionerInvite/templates/existing-practitioner-invitation.template.ts +155 -155
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -101
- package/src/admin/mailing/practitionerInvite/templates/invite-accepted-notification.template.ts +228 -228
- package/src/admin/mailing/practitionerInvite/templates/invite-rejected-notification.template.ts +242 -242
- package/src/admin/notifications/index.ts +1 -1
- package/src/admin/notifications/notifications.admin.ts +710 -710
- package/src/admin/requirements/README.md +128 -128
- package/src/admin/requirements/index.ts +1 -1
- package/src/admin/requirements/patient-requirements.admin.service.ts +475 -475
- package/src/admin/users/index.ts +1 -1
- package/src/admin/users/user-profile.admin.ts +405 -405
- package/src/backoffice/constants/certification.constants.ts +13 -13
- package/src/backoffice/constants/index.ts +1 -1
- package/src/backoffice/errors/backoffice.errors.ts +181 -181
- package/src/backoffice/errors/index.ts +1 -1
- package/src/backoffice/expo-safe/README.md +26 -26
- package/src/backoffice/expo-safe/index.ts +41 -41
- package/src/backoffice/index.ts +5 -5
- package/src/backoffice/services/FIXES_README.md +102 -102
- package/src/backoffice/services/README.md +40 -40
- package/src/backoffice/services/brand.service.ts +256 -256
- package/src/backoffice/services/category.service.ts +318 -318
- package/src/backoffice/services/constants.service.ts +385 -385
- package/src/backoffice/services/documentation-template.service.ts +202 -202
- package/src/backoffice/services/index.ts +11 -8
- package/src/backoffice/services/migrate-products.ts +116 -116
- package/src/backoffice/services/product.service.ts +553 -553
- package/src/backoffice/services/requirement.service.ts +235 -235
- package/src/backoffice/services/subcategory.service.ts +395 -395
- package/src/backoffice/services/technology.service.ts +1083 -1070
- package/src/backoffice/types/README.md +12 -12
- package/src/backoffice/types/admin-constants.types.ts +69 -69
- package/src/backoffice/types/brand.types.ts +29 -29
- package/src/backoffice/types/category.types.ts +62 -62
- package/src/backoffice/types/documentation-templates.types.ts +28 -28
- package/src/backoffice/types/index.ts +10 -10
- package/src/backoffice/types/procedure-product.types.ts +38 -38
- package/src/backoffice/types/product.types.ts +240 -240
- package/src/backoffice/types/requirement.types.ts +63 -63
- package/src/backoffice/types/static/README.md +18 -18
- package/src/backoffice/types/static/blocking-condition.types.ts +21 -21
- package/src/backoffice/types/static/certification.types.ts +37 -37
- package/src/backoffice/types/static/contraindication.types.ts +19 -19
- package/src/backoffice/types/static/index.ts +6 -6
- package/src/backoffice/types/static/pricing.types.ts +16 -16
- package/src/backoffice/types/static/procedure-family.types.ts +14 -14
- package/src/backoffice/types/static/treatment-benefit.types.ts +22 -22
- package/src/backoffice/types/subcategory.types.ts +34 -34
- package/src/backoffice/types/technology.types.ts +163 -161
- package/src/backoffice/validations/index.ts +1 -1
- package/src/backoffice/validations/schemas.ts +164 -163
- package/src/config/__mocks__/firebase.ts +99 -99
- package/src/config/firebase.ts +78 -78
- package/src/config/index.ts +9 -9
- package/src/errors/auth.error.ts +6 -6
- package/src/errors/auth.errors.ts +200 -200
- package/src/errors/clinic.errors.ts +32 -32
- package/src/errors/firebase.errors.ts +47 -47
- package/src/errors/user.errors.ts +99 -99
- package/src/index.backup.ts +407 -407
- package/src/index.ts +6 -6
- package/src/locales/en.ts +31 -31
- package/src/recommender/admin/index.ts +1 -1
- package/src/recommender/admin/services/recommender.service.admin.ts +5 -5
- package/src/recommender/front/index.ts +1 -1
- package/src/recommender/front/services/onboarding.service.ts +5 -5
- package/src/recommender/front/services/recommender.service.ts +3 -3
- package/src/recommender/index.ts +1 -1
- package/src/services/PATIENTAUTH.MD +197 -197
- package/src/services/README.md +106 -106
- package/src/services/__tests__/auth/auth.mock.test.ts +17 -17
- package/src/services/__tests__/auth/auth.setup.ts +293 -293
- package/src/services/__tests__/auth.service.test.ts +346 -346
- package/src/services/__tests__/base.service.test.ts +77 -77
- package/src/services/__tests__/user.service.test.ts +528 -528
- package/src/services/appointment/README.md +17 -17
- package/src/services/appointment/appointment.service.ts +2505 -2082
- package/src/services/appointment/index.ts +1 -1
- package/src/services/appointment/utils/appointment.utils.ts +552 -552
- package/src/services/appointment/utils/extended-procedure.utils.ts +314 -314
- package/src/services/appointment/utils/form-initialization.utils.ts +225 -225
- package/src/services/appointment/utils/recommended-procedure.utils.ts +195 -195
- package/src/services/appointment/utils/zone-management.utils.ts +353 -353
- package/src/services/appointment/utils/zone-photo.utils.ts +152 -152
- package/src/services/auth/auth.service.ts +989 -989
- package/src/services/auth/auth.v2.service.ts +961 -961
- package/src/services/auth/index.ts +7 -7
- package/src/services/auth/utils/error.utils.ts +90 -90
- package/src/services/auth/utils/firebase.utils.ts +49 -49
- package/src/services/auth/utils/index.ts +21 -21
- package/src/services/auth/utils/practitioner.utils.ts +125 -125
- package/src/services/base.service.ts +41 -41
- package/src/services/calendar/calendar.service.ts +1077 -1077
- package/src/services/calendar/calendar.v2.service.ts +1683 -1683
- package/src/services/calendar/calendar.v3.service.ts +313 -313
- package/src/services/calendar/externalCalendar.service.ts +178 -178
- package/src/services/calendar/index.ts +5 -5
- package/src/services/calendar/synced-calendars.service.ts +743 -743
- package/src/services/calendar/utils/appointment.utils.ts +265 -265
- package/src/services/calendar/utils/calendar-event.utils.ts +646 -646
- package/src/services/calendar/utils/clinic.utils.ts +237 -237
- package/src/services/calendar/utils/docs.utils.ts +157 -157
- package/src/services/calendar/utils/google-calendar.utils.ts +697 -697
- package/src/services/calendar/utils/index.ts +8 -8
- package/src/services/calendar/utils/patient.utils.ts +198 -198
- package/src/services/calendar/utils/practitioner.utils.ts +221 -221
- package/src/services/calendar/utils/synced-calendar.utils.ts +472 -472
- package/src/services/clinic/README.md +204 -204
- package/src/services/clinic/__tests__/clinic-admin.service.test.ts +287 -287
- package/src/services/clinic/__tests__/clinic-group.service.test.ts +352 -352
- package/src/services/clinic/__tests__/clinic.service.test.ts +354 -354
- package/src/services/clinic/billing-transactions.service.ts +217 -217
- package/src/services/clinic/clinic-admin.service.ts +202 -202
- package/src/services/clinic/clinic-group.service.ts +310 -310
- package/src/services/clinic/clinic.service.ts +708 -708
- package/src/services/clinic/index.ts +5 -5
- package/src/services/clinic/practitioner-invite.service.ts +519 -519
- package/src/services/clinic/utils/admin.utils.ts +551 -551
- package/src/services/clinic/utils/clinic-group.utils.ts +646 -646
- package/src/services/clinic/utils/clinic.utils.ts +949 -949
- package/src/services/clinic/utils/filter.utils.d.ts +23 -23
- package/src/services/clinic/utils/filter.utils.ts +446 -446
- package/src/services/clinic/utils/index.ts +11 -11
- package/src/services/clinic/utils/photos.utils.ts +188 -188
- package/src/services/clinic/utils/search.utils.ts +84 -84
- package/src/services/clinic/utils/tag.utils.ts +124 -124
- package/src/services/documentation-templates/documentation-template.service.ts +537 -537
- package/src/services/documentation-templates/filled-document.service.ts +587 -587
- package/src/services/documentation-templates/index.ts +2 -2
- package/src/services/index.ts +13 -13
- package/src/services/media/index.ts +1 -1
- package/src/services/media/media.service.ts +418 -418
- package/src/services/notifications/__tests__/notification.service.test.ts +242 -242
- package/src/services/notifications/index.ts +1 -1
- package/src/services/notifications/notification.service.ts +215 -215
- package/src/services/patient/README.md +48 -48
- package/src/services/patient/To-Do.md +43 -43
- package/src/services/patient/__tests__/patient.service.test.ts +294 -294
- package/src/services/patient/index.ts +2 -2
- package/src/services/patient/patient.service.ts +883 -883
- package/src/services/patient/patientRequirements.service.ts +285 -285
- package/src/services/patient/utils/aesthetic-analysis.utils.ts +176 -176
- package/src/services/patient/utils/clinic.utils.ts +80 -80
- package/src/services/patient/utils/docs.utils.ts +142 -142
- package/src/services/patient/utils/index.ts +9 -9
- package/src/services/patient/utils/location.utils.ts +126 -126
- package/src/services/patient/utils/medical-stuff.utils.ts +143 -143
- package/src/services/patient/utils/medical.utils.ts +458 -458
- package/src/services/patient/utils/practitioner.utils.ts +260 -260
- package/src/services/patient/utils/profile.utils.ts +510 -510
- package/src/services/patient/utils/sensitive.utils.ts +260 -260
- package/src/services/patient/utils/token.utils.ts +211 -211
- package/src/services/practitioner/README.md +145 -145
- package/src/services/practitioner/index.ts +1 -1
- package/src/services/practitioner/practitioner.service.ts +1742 -1742
- package/src/services/procedure/README.md +163 -163
- package/src/services/procedure/index.ts +1 -1
- package/src/services/procedure/procedure.service.ts +1682 -1682
- package/src/services/reviews/index.ts +1 -1
- package/src/services/reviews/reviews.service.ts +636 -683
- package/src/services/user/index.ts +1 -1
- package/src/services/user/user.service.ts +489 -489
- package/src/services/user/user.v2.service.ts +466 -466
- package/src/types/appointment/index.ts +481 -453
- package/src/types/calendar/index.ts +258 -258
- package/src/types/calendar/synced-calendar.types.ts +66 -66
- package/src/types/clinic/index.ts +489 -489
- package/src/types/clinic/practitioner-invite.types.ts +91 -91
- package/src/types/clinic/preferences.types.ts +159 -159
- package/src/types/clinic/to-do +3 -3
- package/src/types/documentation-templates/index.ts +308 -308
- package/src/types/index.ts +44 -44
- package/src/types/notifications/README.md +77 -77
- package/src/types/notifications/index.ts +265 -265
- package/src/types/patient/aesthetic-analysis.types.ts +66 -66
- package/src/types/patient/allergies.ts +58 -58
- package/src/types/patient/index.ts +275 -273
- package/src/types/patient/medical-info.types.ts +152 -152
- package/src/types/patient/patient-requirements.ts +92 -92
- package/src/types/patient/token.types.ts +61 -61
- package/src/types/practitioner/index.ts +206 -206
- package/src/types/procedure/index.ts +181 -181
- package/src/types/profile/index.ts +39 -39
- package/src/types/reviews/index.ts +130 -132
- package/src/types/tz-lookup.d.ts +4 -4
- package/src/types/user/index.ts +38 -38
- package/src/utils/TIMESTAMPS.md +176 -176
- package/src/utils/TimestampUtils.ts +241 -241
- package/src/utils/index.ts +1 -1
- package/src/validations/appointment.schema.ts +574 -574
- package/src/validations/calendar.schema.ts +225 -225
- package/src/validations/clinic.schema.ts +493 -493
- package/src/validations/common.schema.ts +25 -25
- package/src/validations/documentation-templates/index.ts +1 -1
- package/src/validations/documentation-templates/template.schema.ts +220 -220
- package/src/validations/documentation-templates.schema.ts +10 -10
- package/src/validations/index.ts +20 -20
- package/src/validations/media.schema.ts +10 -10
- package/src/validations/notification.schema.ts +90 -90
- package/src/validations/patient/aesthetic-analysis.schema.ts +55 -55
- package/src/validations/patient/medical-info.schema.ts +125 -125
- package/src/validations/patient/patient-requirements.schema.ts +84 -84
- package/src/validations/patient/token.schema.ts +29 -29
- package/src/validations/patient.schema.ts +217 -216
- package/src/validations/practitioner.schema.ts +222 -222
- package/src/validations/procedure-product.schema.ts +41 -41
- package/src/validations/procedure.schema.ts +124 -124
- package/src/validations/profile-info.schema.ts +41 -41
- package/src/validations/reviews.schema.ts +189 -195
- package/src/validations/schemas.ts +104 -104
- package/src/validations/shared.schema.ts +78 -78
|
@@ -1,405 +1,405 @@
|
|
|
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
|
-
isManual: false,
|
|
127
|
-
doctors: [],
|
|
128
|
-
clinics: [],
|
|
129
|
-
doctorIds: [],
|
|
130
|
-
clinicIds: [],
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Merge with any provided profile data
|
|
134
|
-
const mergedProfileData = {
|
|
135
|
-
...defaultProfileData,
|
|
136
|
-
...options?.profileData,
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// Create the patient profile document with explicit properties
|
|
140
|
-
const patientProfileData = {
|
|
141
|
-
id: patientProfileId,
|
|
142
|
-
userRef: mergedProfileData.userRef,
|
|
143
|
-
displayName: mergedProfileData.displayName,
|
|
144
|
-
profilePhoto: null,
|
|
145
|
-
gamification: mergedProfileData.gamification || {
|
|
146
|
-
level: 1,
|
|
147
|
-
points: 0,
|
|
148
|
-
},
|
|
149
|
-
expoTokens: mergedProfileData.expoTokens || [],
|
|
150
|
-
isActive: mergedProfileData.isActive,
|
|
151
|
-
isVerified: mergedProfileData.isVerified,
|
|
152
|
-
doctors: mergedProfileData.doctors || [],
|
|
153
|
-
clinics: mergedProfileData.clinics || [],
|
|
154
|
-
doctorIds: mergedProfileData.doctorIds || [],
|
|
155
|
-
clinicIds: mergedProfileData.clinicIds || [],
|
|
156
|
-
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
157
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
// Store the document
|
|
161
|
-
await patientProfileRef.set(patientProfileData);
|
|
162
|
-
|
|
163
|
-
// For returning to the client, use type assertions
|
|
164
|
-
patientProfile = {
|
|
165
|
-
...patientProfileData,
|
|
166
|
-
createdAt: null as any,
|
|
167
|
-
updatedAt: null as any,
|
|
168
|
-
} as unknown as PatientProfile;
|
|
169
|
-
|
|
170
|
-
console.log(
|
|
171
|
-
`[UserProfileAdminService] Creating patient profile with ID ${patientProfileId}`
|
|
172
|
-
);
|
|
173
|
-
} else {
|
|
174
|
-
// Get existing patient profile
|
|
175
|
-
const patientProfileRef = this.db
|
|
176
|
-
.collection(PATIENTS_COLLECTION)
|
|
177
|
-
.doc(patientProfileId);
|
|
178
|
-
const patientProfileDoc = await patientProfileRef.get();
|
|
179
|
-
|
|
180
|
-
if (!patientProfileDoc.exists) {
|
|
181
|
-
throw new Error(
|
|
182
|
-
`Patient profile ${patientProfileId} exists in user but not in patients collection`
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
patientProfile = patientProfileDoc.data() as PatientProfile;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Create sensitive information document
|
|
190
|
-
const sensitiveInfoRef = this.db
|
|
191
|
-
.collection(PATIENTS_COLLECTION)
|
|
192
|
-
.doc(patientProfileId)
|
|
193
|
-
.collection(PATIENT_SENSITIVE_INFO_COLLECTION)
|
|
194
|
-
.doc("info"); // Use 'info' as standard document ID for subcollection
|
|
195
|
-
|
|
196
|
-
// Check if sensitive info document already exists
|
|
197
|
-
const sensitiveInfoDoc = await sensitiveInfoRef.get();
|
|
198
|
-
let patientSensitiveInfo: PatientSensitiveInfo;
|
|
199
|
-
|
|
200
|
-
if (!sensitiveInfoDoc.exists) {
|
|
201
|
-
// Create default sensitive info data
|
|
202
|
-
const defaultSensitiveData: CreatePatientSensitiveInfoData = {
|
|
203
|
-
patientId: patientProfileId,
|
|
204
|
-
userRef: userId,
|
|
205
|
-
firstName: "",
|
|
206
|
-
lastName: "",
|
|
207
|
-
dateOfBirth: null,
|
|
208
|
-
gender: Gender.PREFER_NOT_TO_SAY,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// Merge with provided data
|
|
212
|
-
const mergedSensitiveData = {
|
|
213
|
-
...defaultSensitiveData,
|
|
214
|
-
...options?.sensitiveData,
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
// Create sensitive info
|
|
218
|
-
const sensitiveInfoData = {
|
|
219
|
-
...mergedSensitiveData,
|
|
220
|
-
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
221
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
222
|
-
// Leave dateOfBirth as is
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
// Store the document
|
|
226
|
-
await sensitiveInfoRef.set(sensitiveInfoData);
|
|
227
|
-
|
|
228
|
-
// Convert for client return with type assertions
|
|
229
|
-
patientSensitiveInfo = {
|
|
230
|
-
...sensitiveInfoData,
|
|
231
|
-
createdAt: null as any,
|
|
232
|
-
updatedAt: null as any,
|
|
233
|
-
} as unknown as PatientSensitiveInfo;
|
|
234
|
-
|
|
235
|
-
console.log(
|
|
236
|
-
`[UserProfileAdminService] Creating sensitive info in subcollection for patient ${patientProfileId}`
|
|
237
|
-
);
|
|
238
|
-
} else {
|
|
239
|
-
patientSensitiveInfo = sensitiveInfoDoc.data() as PatientSensitiveInfo;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Create medical info document as a subcollection
|
|
243
|
-
const medicalInfoRef = this.db
|
|
244
|
-
.collection(PATIENTS_COLLECTION)
|
|
245
|
-
.doc(patientProfileId)
|
|
246
|
-
.collection(PATIENT_MEDICAL_INFO_COLLECTION)
|
|
247
|
-
.doc("info"); // Use 'info' as standard document ID for subcollection
|
|
248
|
-
|
|
249
|
-
// Check if medical info document already exists
|
|
250
|
-
const medicalInfoDoc = await medicalInfoRef.get();
|
|
251
|
-
let patientMedicalInfo: PatientMedicalInfo;
|
|
252
|
-
|
|
253
|
-
if (!medicalInfoDoc.exists) {
|
|
254
|
-
// Create medical info
|
|
255
|
-
const medicalInfoData = {
|
|
256
|
-
patientId: patientProfileId,
|
|
257
|
-
vitalStats: {},
|
|
258
|
-
blockingConditions: [],
|
|
259
|
-
contraindications: [],
|
|
260
|
-
allergies: [],
|
|
261
|
-
currentMedications: [],
|
|
262
|
-
lastUpdated: admin.firestore.FieldValue.serverTimestamp(),
|
|
263
|
-
updatedBy: userId,
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// Store the document
|
|
267
|
-
await medicalInfoRef.set(medicalInfoData);
|
|
268
|
-
|
|
269
|
-
// Convert for client return with type assertions
|
|
270
|
-
patientMedicalInfo = {
|
|
271
|
-
...medicalInfoData,
|
|
272
|
-
lastUpdated: null as any,
|
|
273
|
-
} as unknown as PatientMedicalInfo;
|
|
274
|
-
|
|
275
|
-
console.log(
|
|
276
|
-
`[UserProfileAdminService] Creating medical info in subcollection for patient ${patientProfileId}`
|
|
277
|
-
);
|
|
278
|
-
} else {
|
|
279
|
-
patientMedicalInfo = medicalInfoDoc.data() as PatientMedicalInfo;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Update user document with patient role and profile reference
|
|
283
|
-
const batch = this.db.batch();
|
|
284
|
-
|
|
285
|
-
// Add patient role if not already present
|
|
286
|
-
if (!userData.roles.includes(UserRole.PATIENT)) {
|
|
287
|
-
batch.update(userRef, {
|
|
288
|
-
roles: admin.firestore.FieldValue.arrayUnion(UserRole.PATIENT),
|
|
289
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Add patient profile reference if not already set
|
|
294
|
-
if (!userData.patientProfile) {
|
|
295
|
-
batch.update(userRef, {
|
|
296
|
-
patientProfile: patientProfileId,
|
|
297
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
await batch.commit();
|
|
302
|
-
|
|
303
|
-
// Get updated user document
|
|
304
|
-
const updatedUserDoc = await userRef.get();
|
|
305
|
-
const updatedUser = updatedUserDoc.data() as User;
|
|
306
|
-
|
|
307
|
-
console.log(
|
|
308
|
-
`[UserProfileAdminService] Successfully initialized patient role for user ${userId}`
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
user: updatedUser,
|
|
313
|
-
patientProfile,
|
|
314
|
-
patientSensitiveInfo,
|
|
315
|
-
patientMedicalInfo,
|
|
316
|
-
};
|
|
317
|
-
} catch (error) {
|
|
318
|
-
console.error(
|
|
319
|
-
`[UserProfileAdminService] Error initializing patient role for user ${userId}:`,
|
|
320
|
-
error
|
|
321
|
-
);
|
|
322
|
-
throw error;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Initializes clinic admin role for a user
|
|
328
|
-
* @param userId The user ID to initialize with clinic admin role
|
|
329
|
-
* @returns The updated user document
|
|
330
|
-
*/
|
|
331
|
-
async initializeClinicAdminRole(userId: string): Promise<User> {
|
|
332
|
-
console.log(
|
|
333
|
-
`[UserProfileAdminService] Initializing clinic admin role for user ${userId}`
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
try {
|
|
337
|
-
const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
|
|
338
|
-
const userDoc = await userRef.get();
|
|
339
|
-
|
|
340
|
-
if (!userDoc.exists) {
|
|
341
|
-
throw new Error(`User ${userId} not found`);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const userData = userDoc.data() as User;
|
|
345
|
-
|
|
346
|
-
// Only add the role if it doesn't already exist
|
|
347
|
-
if (!userData.roles.includes(UserRole.CLINIC_ADMIN)) {
|
|
348
|
-
await userRef.update({
|
|
349
|
-
roles: admin.firestore.FieldValue.arrayUnion(UserRole.CLINIC_ADMIN),
|
|
350
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Return the updated user document
|
|
355
|
-
const updatedUserDoc = await userRef.get();
|
|
356
|
-
return updatedUserDoc.data() as User;
|
|
357
|
-
} catch (error) {
|
|
358
|
-
console.error(
|
|
359
|
-
`[UserProfileAdminService] Error initializing clinic admin role for user ${userId}:`,
|
|
360
|
-
error
|
|
361
|
-
);
|
|
362
|
-
throw error;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Initializes practitioner role for a user
|
|
368
|
-
* @param userId The user ID to initialize with practitioner role
|
|
369
|
-
* @returns The updated user document
|
|
370
|
-
*/
|
|
371
|
-
async initializePractitionerRole(userId: string): Promise<User> {
|
|
372
|
-
console.log(
|
|
373
|
-
`[UserProfileAdminService] Initializing practitioner role for user ${userId}`
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
try {
|
|
377
|
-
const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
|
|
378
|
-
const userDoc = await userRef.get();
|
|
379
|
-
|
|
380
|
-
if (!userDoc.exists) {
|
|
381
|
-
throw new Error(`User ${userId} not found`);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const userData = userDoc.data() as User;
|
|
385
|
-
|
|
386
|
-
// Only add the role if it doesn't already exist
|
|
387
|
-
if (!userData.roles.includes(UserRole.PRACTITIONER)) {
|
|
388
|
-
await userRef.update({
|
|
389
|
-
roles: admin.firestore.FieldValue.arrayUnion(UserRole.PRACTITIONER),
|
|
390
|
-
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Return the updated user document
|
|
395
|
-
const updatedUserDoc = await userRef.get();
|
|
396
|
-
return updatedUserDoc.data() as User;
|
|
397
|
-
} catch (error) {
|
|
398
|
-
console.error(
|
|
399
|
-
`[UserProfileAdminService] Error initializing practitioner role for user ${userId}:`,
|
|
400
|
-
error
|
|
401
|
-
);
|
|
402
|
-
throw error;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
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
|
+
isManual: false,
|
|
127
|
+
doctors: [],
|
|
128
|
+
clinics: [],
|
|
129
|
+
doctorIds: [],
|
|
130
|
+
clinicIds: [],
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Merge with any provided profile data
|
|
134
|
+
const mergedProfileData = {
|
|
135
|
+
...defaultProfileData,
|
|
136
|
+
...options?.profileData,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Create the patient profile document with explicit properties
|
|
140
|
+
const patientProfileData = {
|
|
141
|
+
id: patientProfileId,
|
|
142
|
+
userRef: mergedProfileData.userRef,
|
|
143
|
+
displayName: mergedProfileData.displayName,
|
|
144
|
+
profilePhoto: null,
|
|
145
|
+
gamification: mergedProfileData.gamification || {
|
|
146
|
+
level: 1,
|
|
147
|
+
points: 0,
|
|
148
|
+
},
|
|
149
|
+
expoTokens: mergedProfileData.expoTokens || [],
|
|
150
|
+
isActive: mergedProfileData.isActive,
|
|
151
|
+
isVerified: mergedProfileData.isVerified,
|
|
152
|
+
doctors: mergedProfileData.doctors || [],
|
|
153
|
+
clinics: mergedProfileData.clinics || [],
|
|
154
|
+
doctorIds: mergedProfileData.doctorIds || [],
|
|
155
|
+
clinicIds: mergedProfileData.clinicIds || [],
|
|
156
|
+
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
157
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Store the document
|
|
161
|
+
await patientProfileRef.set(patientProfileData);
|
|
162
|
+
|
|
163
|
+
// For returning to the client, use type assertions
|
|
164
|
+
patientProfile = {
|
|
165
|
+
...patientProfileData,
|
|
166
|
+
createdAt: null as any,
|
|
167
|
+
updatedAt: null as any,
|
|
168
|
+
} as unknown as PatientProfile;
|
|
169
|
+
|
|
170
|
+
console.log(
|
|
171
|
+
`[UserProfileAdminService] Creating patient profile with ID ${patientProfileId}`
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
// Get existing patient profile
|
|
175
|
+
const patientProfileRef = this.db
|
|
176
|
+
.collection(PATIENTS_COLLECTION)
|
|
177
|
+
.doc(patientProfileId);
|
|
178
|
+
const patientProfileDoc = await patientProfileRef.get();
|
|
179
|
+
|
|
180
|
+
if (!patientProfileDoc.exists) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`Patient profile ${patientProfileId} exists in user but not in patients collection`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
patientProfile = patientProfileDoc.data() as PatientProfile;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Create sensitive information document
|
|
190
|
+
const sensitiveInfoRef = this.db
|
|
191
|
+
.collection(PATIENTS_COLLECTION)
|
|
192
|
+
.doc(patientProfileId)
|
|
193
|
+
.collection(PATIENT_SENSITIVE_INFO_COLLECTION)
|
|
194
|
+
.doc("info"); // Use 'info' as standard document ID for subcollection
|
|
195
|
+
|
|
196
|
+
// Check if sensitive info document already exists
|
|
197
|
+
const sensitiveInfoDoc = await sensitiveInfoRef.get();
|
|
198
|
+
let patientSensitiveInfo: PatientSensitiveInfo;
|
|
199
|
+
|
|
200
|
+
if (!sensitiveInfoDoc.exists) {
|
|
201
|
+
// Create default sensitive info data
|
|
202
|
+
const defaultSensitiveData: CreatePatientSensitiveInfoData = {
|
|
203
|
+
patientId: patientProfileId,
|
|
204
|
+
userRef: userId,
|
|
205
|
+
firstName: "",
|
|
206
|
+
lastName: "",
|
|
207
|
+
dateOfBirth: null,
|
|
208
|
+
gender: Gender.PREFER_NOT_TO_SAY,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Merge with provided data
|
|
212
|
+
const mergedSensitiveData = {
|
|
213
|
+
...defaultSensitiveData,
|
|
214
|
+
...options?.sensitiveData,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Create sensitive info
|
|
218
|
+
const sensitiveInfoData = {
|
|
219
|
+
...mergedSensitiveData,
|
|
220
|
+
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
221
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
222
|
+
// Leave dateOfBirth as is
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Store the document
|
|
226
|
+
await sensitiveInfoRef.set(sensitiveInfoData);
|
|
227
|
+
|
|
228
|
+
// Convert for client return with type assertions
|
|
229
|
+
patientSensitiveInfo = {
|
|
230
|
+
...sensitiveInfoData,
|
|
231
|
+
createdAt: null as any,
|
|
232
|
+
updatedAt: null as any,
|
|
233
|
+
} as unknown as PatientSensitiveInfo;
|
|
234
|
+
|
|
235
|
+
console.log(
|
|
236
|
+
`[UserProfileAdminService] Creating sensitive info in subcollection for patient ${patientProfileId}`
|
|
237
|
+
);
|
|
238
|
+
} else {
|
|
239
|
+
patientSensitiveInfo = sensitiveInfoDoc.data() as PatientSensitiveInfo;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Create medical info document as a subcollection
|
|
243
|
+
const medicalInfoRef = this.db
|
|
244
|
+
.collection(PATIENTS_COLLECTION)
|
|
245
|
+
.doc(patientProfileId)
|
|
246
|
+
.collection(PATIENT_MEDICAL_INFO_COLLECTION)
|
|
247
|
+
.doc("info"); // Use 'info' as standard document ID for subcollection
|
|
248
|
+
|
|
249
|
+
// Check if medical info document already exists
|
|
250
|
+
const medicalInfoDoc = await medicalInfoRef.get();
|
|
251
|
+
let patientMedicalInfo: PatientMedicalInfo;
|
|
252
|
+
|
|
253
|
+
if (!medicalInfoDoc.exists) {
|
|
254
|
+
// Create medical info
|
|
255
|
+
const medicalInfoData = {
|
|
256
|
+
patientId: patientProfileId,
|
|
257
|
+
vitalStats: {},
|
|
258
|
+
blockingConditions: [],
|
|
259
|
+
contraindications: [],
|
|
260
|
+
allergies: [],
|
|
261
|
+
currentMedications: [],
|
|
262
|
+
lastUpdated: admin.firestore.FieldValue.serverTimestamp(),
|
|
263
|
+
updatedBy: userId,
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Store the document
|
|
267
|
+
await medicalInfoRef.set(medicalInfoData);
|
|
268
|
+
|
|
269
|
+
// Convert for client return with type assertions
|
|
270
|
+
patientMedicalInfo = {
|
|
271
|
+
...medicalInfoData,
|
|
272
|
+
lastUpdated: null as any,
|
|
273
|
+
} as unknown as PatientMedicalInfo;
|
|
274
|
+
|
|
275
|
+
console.log(
|
|
276
|
+
`[UserProfileAdminService] Creating medical info in subcollection for patient ${patientProfileId}`
|
|
277
|
+
);
|
|
278
|
+
} else {
|
|
279
|
+
patientMedicalInfo = medicalInfoDoc.data() as PatientMedicalInfo;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Update user document with patient role and profile reference
|
|
283
|
+
const batch = this.db.batch();
|
|
284
|
+
|
|
285
|
+
// Add patient role if not already present
|
|
286
|
+
if (!userData.roles.includes(UserRole.PATIENT)) {
|
|
287
|
+
batch.update(userRef, {
|
|
288
|
+
roles: admin.firestore.FieldValue.arrayUnion(UserRole.PATIENT),
|
|
289
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Add patient profile reference if not already set
|
|
294
|
+
if (!userData.patientProfile) {
|
|
295
|
+
batch.update(userRef, {
|
|
296
|
+
patientProfile: patientProfileId,
|
|
297
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
await batch.commit();
|
|
302
|
+
|
|
303
|
+
// Get updated user document
|
|
304
|
+
const updatedUserDoc = await userRef.get();
|
|
305
|
+
const updatedUser = updatedUserDoc.data() as User;
|
|
306
|
+
|
|
307
|
+
console.log(
|
|
308
|
+
`[UserProfileAdminService] Successfully initialized patient role for user ${userId}`
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
user: updatedUser,
|
|
313
|
+
patientProfile,
|
|
314
|
+
patientSensitiveInfo,
|
|
315
|
+
patientMedicalInfo,
|
|
316
|
+
};
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error(
|
|
319
|
+
`[UserProfileAdminService] Error initializing patient role for user ${userId}:`,
|
|
320
|
+
error
|
|
321
|
+
);
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Initializes clinic admin role for a user
|
|
328
|
+
* @param userId The user ID to initialize with clinic admin role
|
|
329
|
+
* @returns The updated user document
|
|
330
|
+
*/
|
|
331
|
+
async initializeClinicAdminRole(userId: string): Promise<User> {
|
|
332
|
+
console.log(
|
|
333
|
+
`[UserProfileAdminService] Initializing clinic admin role for user ${userId}`
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
|
|
338
|
+
const userDoc = await userRef.get();
|
|
339
|
+
|
|
340
|
+
if (!userDoc.exists) {
|
|
341
|
+
throw new Error(`User ${userId} not found`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const userData = userDoc.data() as User;
|
|
345
|
+
|
|
346
|
+
// Only add the role if it doesn't already exist
|
|
347
|
+
if (!userData.roles.includes(UserRole.CLINIC_ADMIN)) {
|
|
348
|
+
await userRef.update({
|
|
349
|
+
roles: admin.firestore.FieldValue.arrayUnion(UserRole.CLINIC_ADMIN),
|
|
350
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Return the updated user document
|
|
355
|
+
const updatedUserDoc = await userRef.get();
|
|
356
|
+
return updatedUserDoc.data() as User;
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error(
|
|
359
|
+
`[UserProfileAdminService] Error initializing clinic admin role for user ${userId}:`,
|
|
360
|
+
error
|
|
361
|
+
);
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Initializes practitioner role for a user
|
|
368
|
+
* @param userId The user ID to initialize with practitioner role
|
|
369
|
+
* @returns The updated user document
|
|
370
|
+
*/
|
|
371
|
+
async initializePractitionerRole(userId: string): Promise<User> {
|
|
372
|
+
console.log(
|
|
373
|
+
`[UserProfileAdminService] Initializing practitioner role for user ${userId}`
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const userRef = this.db.collection(USERS_COLLECTION).doc(userId);
|
|
378
|
+
const userDoc = await userRef.get();
|
|
379
|
+
|
|
380
|
+
if (!userDoc.exists) {
|
|
381
|
+
throw new Error(`User ${userId} not found`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const userData = userDoc.data() as User;
|
|
385
|
+
|
|
386
|
+
// Only add the role if it doesn't already exist
|
|
387
|
+
if (!userData.roles.includes(UserRole.PRACTITIONER)) {
|
|
388
|
+
await userRef.update({
|
|
389
|
+
roles: admin.firestore.FieldValue.arrayUnion(UserRole.PRACTITIONER),
|
|
390
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Return the updated user document
|
|
395
|
+
const updatedUserDoc = await userRef.get();
|
|
396
|
+
return updatedUserDoc.data() as User;
|
|
397
|
+
} catch (error) {
|
|
398
|
+
console.error(
|
|
399
|
+
`[UserProfileAdminService] Error initializing practitioner role for user ${userId}:`,
|
|
400
|
+
error
|
|
401
|
+
);
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|