@blackcode_sa/metaestetics-api 1.7.47 → 1.8.1
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 +1815 -1667
- package/dist/admin/index.d.ts +1815 -1667
- package/dist/admin/index.js +6993 -6535
- package/dist/admin/index.mjs +6991 -6532
- package/dist/backoffice/index.d.mts +1418 -1418
- package/dist/backoffice/index.d.ts +1418 -1418
- package/dist/backoffice/index.js +1464 -1454
- package/dist/backoffice/index.mjs +1509 -1499
- package/dist/index.d.mts +16217 -14582
- package/dist/index.d.ts +16217 -14582
- package/dist/index.js +11419 -13806
- package/dist/index.mjs +12081 -14593
- package/package.json +5 -5
- package/src/admin/aggregation/appointment/index.ts +1 -0
- package/src/admin/aggregation/clinic/index.ts +1 -0
- package/src/admin/aggregation/forms/index.ts +1 -0
- package/src/admin/aggregation/index.ts +8 -0
- package/src/admin/aggregation/patient/index.ts +1 -0
- package/src/admin/aggregation/practitioner/index.ts +1 -0
- package/src/admin/aggregation/practitioner-invite/index.ts +1 -0
- package/src/admin/aggregation/procedure/index.ts +1 -0
- package/src/admin/aggregation/reviews/index.ts +1 -0
- package/src/admin/booking/index.ts +1 -1
- package/src/admin/calendar/index.ts +1 -0
- package/src/admin/documentation-templates/index.ts +1 -0
- package/src/admin/free-consultation/index.ts +1 -0
- package/src/admin/index.ts +23 -118
- package/src/admin/mailing/appointment/index.ts +1 -0
- package/src/admin/mailing/index.ts +1 -2
- package/src/admin/mailing/practitionerInvite/index.ts +1 -0
- package/src/admin/notifications/index.ts +1 -0
- package/src/admin/requirements/index.ts +1 -0
- package/src/admin/users/index.ts +1 -0
- package/src/admin/users/user-profile.admin.ts +1 -0
- package/src/backoffice/constants/index.ts +1 -0
- package/src/backoffice/errors/index.ts +1 -0
- package/src/backoffice/index.ts +5 -14
- package/src/backoffice/services/index.ts +7 -0
- package/src/backoffice/validations/index.ts +1 -0
- package/src/index.backup.ts +407 -0
- package/src/index.ts +5 -406
- package/src/services/PATIENTAUTH.MD +197 -0
- package/src/services/__tests__/auth/auth.setup.ts +2 -2
- package/src/services/__tests__/auth.service.test.ts +1 -1
- package/src/services/__tests__/user.service.test.ts +1 -1
- package/src/services/appointment/index.ts +1 -2
- package/src/services/{auth.service.ts → auth/auth.service.ts} +36 -22
- package/src/services/{auth.v2.service.ts → auth/auth.v2.service.ts} +17 -17
- package/src/services/auth/index.ts +2 -16
- package/src/services/calendar/calendar-refactored.service.ts +1 -1
- package/src/services/calendar/index.ts +5 -0
- package/src/services/clinic/index.ts +4 -0
- package/src/services/index.ts +12 -0
- package/src/services/media/index.ts +1 -0
- package/src/services/notifications/index.ts +1 -0
- package/src/services/patient/README.md +48 -0
- package/src/services/patient/To-Do.md +43 -0
- package/src/services/patient/index.ts +2 -0
- package/src/services/patient/patient.service.ts +289 -34
- package/src/services/patient/utils/index.ts +9 -0
- package/src/services/patient/utils/medical.utils.ts +114 -157
- package/src/services/patient/utils/profile.utils.ts +9 -0
- package/src/services/patient/utils/sensitive.utils.ts +79 -14
- package/src/services/patient/utils/token.utils.ts +211 -0
- package/src/services/practitioner/index.ts +1 -0
- package/src/services/procedure/index.ts +1 -0
- package/src/services/reviews/index.ts +1 -0
- package/src/services/user/index.ts +1 -0
- package/src/services/{user.service.ts → user/user.service.ts} +61 -12
- package/src/services/{user.v2.service.ts → user/user.v2.service.ts} +12 -12
- package/src/types/index.ts +42 -42
- package/src/types/patient/index.ts +33 -6
- package/src/types/patient/token.types.ts +61 -0
- package/src/types/user/index.ts +38 -0
- package/src/utils/index.ts +1 -0
- package/src/validations/calendar.schema.ts +6 -45
- package/src/validations/documentation-templates/index.ts +1 -0
- package/src/validations/documentation-templates.schema.ts +1 -1
- package/src/validations/index.ts +20 -0
- package/src/validations/patient/token.schema.ts +29 -0
- package/src/validations/patient.schema.ts +23 -6
- package/src/validations/profile-info.schema.ts +1 -1
- package/src/validations/schemas.ts +24 -24
|
@@ -45,64 +45,25 @@ import { AuthError } from "../../../errors/auth.errors";
|
|
|
45
45
|
import { UserRole } from "../../../types";
|
|
46
46
|
import { getMedicalInfoDocRef, getPatientDocRef } from "./docs.utils";
|
|
47
47
|
import { getPractitionerProfileByUserRef } from "./practitioner.utils";
|
|
48
|
+
import { getClinicAdminByUserRef } from "../../clinic/utils/admin.utils";
|
|
48
49
|
|
|
49
50
|
// Pomoćna funkcija za proveru i inicijalizaciju medical info dokumenta
|
|
50
51
|
export const ensureMedicalInfoExists = async (
|
|
51
52
|
db: Firestore,
|
|
52
53
|
patientId: string,
|
|
53
|
-
|
|
54
|
+
requesterId: string
|
|
54
55
|
): Promise<void> => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
console.log(
|
|
67
|
-
`[ensureMedicalInfoExists] Document exists: ${medicalInfoDoc.exists()}`
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (!medicalInfoDoc.exists()) {
|
|
71
|
-
const defaultData = {
|
|
72
|
-
...DEFAULT_MEDICAL_INFO,
|
|
73
|
-
patientId,
|
|
74
|
-
lastUpdated: serverTimestamp(),
|
|
75
|
-
updatedBy: userRef,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
console.log(
|
|
79
|
-
`[ensureMedicalInfoExists] Creating new document with data:`,
|
|
80
|
-
JSON.stringify(defaultData, (key, value) =>
|
|
81
|
-
value &&
|
|
82
|
-
typeof value === "object" &&
|
|
83
|
-
value.constructor &&
|
|
84
|
-
value.constructor.name === "Object"
|
|
85
|
-
? "[serverTimestamp]"
|
|
86
|
-
: value
|
|
87
|
-
)
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
await setDoc(medicalInfoRef, defaultData);
|
|
91
|
-
console.log(`[ensureMedicalInfoExists] Document created successfully`);
|
|
92
|
-
|
|
93
|
-
// Verify document was created
|
|
94
|
-
const verifyDoc = await getDoc(medicalInfoRef);
|
|
95
|
-
console.log(
|
|
96
|
-
`[ensureMedicalInfoExists] Verification - document exists: ${verifyDoc.exists()}`
|
|
97
|
-
);
|
|
98
|
-
} else {
|
|
99
|
-
console.log(
|
|
100
|
-
`[ensureMedicalInfoExists] Document already exists, no action taken`
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error(`[ensureMedicalInfoExists] Error:`, error);
|
|
105
|
-
throw error;
|
|
56
|
+
const medicalInfoRef = getMedicalInfoDocRef(db, patientId);
|
|
57
|
+
const medicalInfoDoc = await getDoc(medicalInfoRef);
|
|
58
|
+
|
|
59
|
+
if (!medicalInfoDoc.exists()) {
|
|
60
|
+
const defaultData = {
|
|
61
|
+
...DEFAULT_MEDICAL_INFO,
|
|
62
|
+
patientId,
|
|
63
|
+
lastUpdated: serverTimestamp(),
|
|
64
|
+
updatedBy: requesterId, // Koristimo ID onoga ko zahteva akciju
|
|
65
|
+
};
|
|
66
|
+
await setDoc(medicalInfoRef, defaultData);
|
|
106
67
|
}
|
|
107
68
|
};
|
|
108
69
|
|
|
@@ -110,48 +71,54 @@ export const ensureMedicalInfoExists = async (
|
|
|
110
71
|
const checkMedicalAccessUtil = async (
|
|
111
72
|
db: Firestore,
|
|
112
73
|
patientId: string,
|
|
113
|
-
|
|
114
|
-
|
|
74
|
+
requesterId: string,
|
|
75
|
+
requesterRoles: UserRole[]
|
|
115
76
|
): Promise<void> => {
|
|
116
|
-
// Prvo pronađi profil pacijenta
|
|
117
77
|
const patientDoc = await getDoc(getPatientDocRef(db, patientId));
|
|
118
78
|
if (!patientDoc.exists()) {
|
|
119
79
|
throw new Error("Patient profile not found");
|
|
120
80
|
}
|
|
121
|
-
|
|
122
81
|
const patientData = patientDoc.data() as PatientProfile;
|
|
123
82
|
|
|
124
|
-
//
|
|
125
|
-
if (patientData.userRef ===
|
|
83
|
+
// 1. Provera da li je pacijent vlasnik profila (ako profil nije manuelan)
|
|
84
|
+
if (patientData.userRef && patientData.userRef === requesterId) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
126
87
|
|
|
127
|
-
//
|
|
128
|
-
if (
|
|
88
|
+
// 2. Provera za doktora
|
|
89
|
+
if (requesterRoles.includes(UserRole.PRACTITIONER)) {
|
|
129
90
|
const practitionerProfile = await getPractitionerProfileByUserRef(
|
|
130
91
|
db,
|
|
131
|
-
|
|
92
|
+
requesterId
|
|
132
93
|
);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
403
|
|
139
|
-
);
|
|
94
|
+
if (
|
|
95
|
+
practitionerProfile &&
|
|
96
|
+
patientData.doctorIds?.includes(practitionerProfile.id)
|
|
97
|
+
) {
|
|
98
|
+
return;
|
|
140
99
|
}
|
|
100
|
+
}
|
|
141
101
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"Doktor nije povezan sa pacijentom",
|
|
150
|
-
"AUTH/UNAUTHORIZED_ACCESS",
|
|
151
|
-
403
|
|
102
|
+
// 3. Provera za admina klinike
|
|
103
|
+
if (requesterRoles.includes(UserRole.CLINIC_ADMIN)) {
|
|
104
|
+
const adminProfile = await getClinicAdminByUserRef(db, requesterId);
|
|
105
|
+
if (adminProfile && adminProfile.clinicsManaged) {
|
|
106
|
+
// Check if any of the admin's managed clinics are in the patient's clinic list
|
|
107
|
+
const hasAccess = adminProfile.clinicsManaged.some((managedClinicId) =>
|
|
108
|
+
patientData.clinicIds?.includes(managedClinicId)
|
|
152
109
|
);
|
|
110
|
+
if (hasAccess) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
153
113
|
}
|
|
154
114
|
}
|
|
115
|
+
|
|
116
|
+
// Ako nijedan uslov nije zadovoljen
|
|
117
|
+
throw new AuthError(
|
|
118
|
+
"Unauthorized access to medical information.",
|
|
119
|
+
"AUTH/UNAUTHORIZED_ACCESS",
|
|
120
|
+
403
|
|
121
|
+
);
|
|
155
122
|
};
|
|
156
123
|
|
|
157
124
|
// Osnovne metode za medicinske informacije
|
|
@@ -159,37 +126,35 @@ export const createMedicalInfoUtil = async (
|
|
|
159
126
|
db: Firestore,
|
|
160
127
|
patientId: string,
|
|
161
128
|
data: CreatePatientMedicalInfoData,
|
|
162
|
-
|
|
163
|
-
|
|
129
|
+
requesterId: string,
|
|
130
|
+
requesterRoles: UserRole[]
|
|
164
131
|
): Promise<void> => {
|
|
165
|
-
await checkMedicalAccessUtil(db, patientId,
|
|
166
|
-
|
|
132
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
167
133
|
const validatedData = createPatientMedicalInfoSchema.parse(data);
|
|
168
134
|
|
|
169
135
|
await setDoc(getMedicalInfoDocRef(db, patientId), {
|
|
170
136
|
...validatedData,
|
|
171
137
|
patientId: patientId,
|
|
172
|
-
lastUpdated:
|
|
173
|
-
updatedBy:
|
|
138
|
+
lastUpdated: serverTimestamp(),
|
|
139
|
+
updatedBy: requesterId,
|
|
174
140
|
});
|
|
175
141
|
};
|
|
176
142
|
|
|
177
143
|
export const getMedicalInfoUtil = async (
|
|
178
144
|
db: Firestore,
|
|
179
145
|
patientId: string,
|
|
180
|
-
|
|
181
|
-
|
|
146
|
+
requesterId: string,
|
|
147
|
+
requesterRoles: UserRole[]
|
|
182
148
|
): Promise<PatientMedicalInfo> => {
|
|
183
|
-
await checkMedicalAccessUtil(db, patientId,
|
|
184
|
-
|
|
149
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
185
150
|
const docRef = getMedicalInfoDocRef(db, patientId);
|
|
186
151
|
const snapshot = await getDoc(docRef);
|
|
187
|
-
|
|
188
152
|
if (!snapshot.exists()) {
|
|
189
|
-
|
|
153
|
+
// Ako ne postoji, kreiraj prazan dokument
|
|
154
|
+
await ensureMedicalInfoExists(db, patientId, requesterId);
|
|
155
|
+
const newSnapshot = await getDoc(docRef);
|
|
156
|
+
return patientMedicalInfoSchema.parse(newSnapshot.data());
|
|
190
157
|
}
|
|
191
|
-
|
|
192
|
-
// The schema will transform raw timestamp objects to Timestamp instances
|
|
193
158
|
return patientMedicalInfoSchema.parse(snapshot.data());
|
|
194
159
|
};
|
|
195
160
|
|
|
@@ -198,15 +163,17 @@ export const updateVitalStatsUtil = async (
|
|
|
198
163
|
db: Firestore,
|
|
199
164
|
patientId: string,
|
|
200
165
|
data: UpdateVitalStatsData,
|
|
201
|
-
|
|
166
|
+
requesterId: string,
|
|
167
|
+
requesterRoles: UserRole[] // Dodajemo role za proveru pristupa
|
|
202
168
|
): Promise<void> => {
|
|
203
|
-
await
|
|
169
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
170
|
+
await ensureMedicalInfoExists(db, patientId, requesterId);
|
|
204
171
|
const validatedData = updateVitalStatsSchema.parse(data);
|
|
205
172
|
|
|
206
173
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
207
174
|
vitalStats: validatedData,
|
|
208
175
|
lastUpdated: serverTimestamp(),
|
|
209
|
-
updatedBy:
|
|
176
|
+
updatedBy: requesterId,
|
|
210
177
|
});
|
|
211
178
|
};
|
|
212
179
|
|
|
@@ -215,15 +182,16 @@ export const addAllergyUtil = async (
|
|
|
215
182
|
db: Firestore,
|
|
216
183
|
patientId: string,
|
|
217
184
|
data: AddAllergyData,
|
|
218
|
-
|
|
185
|
+
requesterId: string,
|
|
186
|
+
requesterRoles: UserRole[]
|
|
219
187
|
): Promise<void> => {
|
|
220
|
-
await
|
|
188
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
189
|
+
await ensureMedicalInfoExists(db, patientId, requesterId);
|
|
221
190
|
const validatedData = addAllergySchema.parse(data);
|
|
222
|
-
|
|
223
191
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
224
192
|
allergies: arrayUnion(validatedData),
|
|
225
193
|
lastUpdated: serverTimestamp(),
|
|
226
|
-
updatedBy:
|
|
194
|
+
updatedBy: requesterId,
|
|
227
195
|
});
|
|
228
196
|
};
|
|
229
197
|
|
|
@@ -231,31 +199,27 @@ export const updateAllergyUtil = async (
|
|
|
231
199
|
db: Firestore,
|
|
232
200
|
patientId: string,
|
|
233
201
|
data: UpdateAllergyData,
|
|
234
|
-
|
|
202
|
+
requesterId: string,
|
|
203
|
+
requesterRoles: UserRole[]
|
|
235
204
|
): Promise<void> => {
|
|
205
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
236
206
|
const validatedData = updateAllergySchema.parse(data);
|
|
237
207
|
const { allergyIndex, ...updateData } = validatedData;
|
|
238
|
-
|
|
239
208
|
const docSnapshot = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
240
209
|
if (!docSnapshot.exists()) throw new Error("Medical info not found");
|
|
241
|
-
|
|
242
|
-
// Parse through schema to ensure proper Timestamp objects
|
|
243
210
|
const medicalInfo = patientMedicalInfoSchema.parse(docSnapshot.data());
|
|
244
|
-
|
|
245
211
|
if (allergyIndex >= medicalInfo.allergies.length) {
|
|
246
212
|
throw new Error("Invalid allergy index");
|
|
247
213
|
}
|
|
248
|
-
|
|
249
214
|
const updatedAllergies = [...medicalInfo.allergies];
|
|
250
215
|
updatedAllergies[allergyIndex] = {
|
|
251
216
|
...updatedAllergies[allergyIndex],
|
|
252
217
|
...updateData,
|
|
253
218
|
};
|
|
254
|
-
|
|
255
219
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
256
220
|
allergies: updatedAllergies,
|
|
257
221
|
lastUpdated: serverTimestamp(),
|
|
258
|
-
updatedBy:
|
|
222
|
+
updatedBy: requesterId,
|
|
259
223
|
});
|
|
260
224
|
};
|
|
261
225
|
|
|
@@ -263,24 +227,23 @@ export const removeAllergyUtil = async (
|
|
|
263
227
|
db: Firestore,
|
|
264
228
|
patientId: string,
|
|
265
229
|
allergyIndex: number,
|
|
266
|
-
|
|
230
|
+
requesterId: string,
|
|
231
|
+
requesterRoles: UserRole[]
|
|
267
232
|
): Promise<void> => {
|
|
233
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
268
234
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
269
235
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
270
|
-
|
|
271
236
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
272
237
|
if (allergyIndex >= medicalInfo.allergies.length) {
|
|
273
238
|
throw new Error("Invalid allergy index");
|
|
274
239
|
}
|
|
275
|
-
|
|
276
240
|
const updatedAllergies = medicalInfo.allergies.filter(
|
|
277
241
|
(_, index) => index !== allergyIndex
|
|
278
242
|
);
|
|
279
|
-
|
|
280
243
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
281
244
|
allergies: updatedAllergies,
|
|
282
245
|
lastUpdated: serverTimestamp(),
|
|
283
|
-
updatedBy:
|
|
246
|
+
updatedBy: requesterId,
|
|
284
247
|
});
|
|
285
248
|
};
|
|
286
249
|
|
|
@@ -289,15 +252,16 @@ export const addBlockingConditionUtil = async (
|
|
|
289
252
|
db: Firestore,
|
|
290
253
|
patientId: string,
|
|
291
254
|
data: AddBlockingConditionData,
|
|
292
|
-
|
|
255
|
+
requesterId: string,
|
|
256
|
+
requesterRoles: UserRole[]
|
|
293
257
|
): Promise<void> => {
|
|
294
|
-
await
|
|
258
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
259
|
+
await ensureMedicalInfoExists(db, patientId, requesterId);
|
|
295
260
|
const validatedData = addBlockingConditionSchema.parse(data);
|
|
296
|
-
|
|
297
261
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
298
262
|
blockingConditions: arrayUnion(validatedData),
|
|
299
263
|
lastUpdated: serverTimestamp(),
|
|
300
|
-
updatedBy:
|
|
264
|
+
updatedBy: requesterId,
|
|
301
265
|
});
|
|
302
266
|
};
|
|
303
267
|
|
|
@@ -305,29 +269,27 @@ export const updateBlockingConditionUtil = async (
|
|
|
305
269
|
db: Firestore,
|
|
306
270
|
patientId: string,
|
|
307
271
|
data: UpdateBlockingConditionData,
|
|
308
|
-
|
|
272
|
+
requesterId: string,
|
|
273
|
+
requesterRoles: UserRole[]
|
|
309
274
|
): Promise<void> => {
|
|
275
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
310
276
|
const validatedData = updateBlockingConditionSchema.parse(data);
|
|
311
277
|
const { conditionIndex, ...updateData } = validatedData;
|
|
312
|
-
|
|
313
278
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
314
279
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
315
|
-
|
|
316
280
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
317
281
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
318
282
|
throw new Error("Invalid blocking condition index");
|
|
319
283
|
}
|
|
320
|
-
|
|
321
284
|
const updatedConditions = [...medicalInfo.blockingConditions];
|
|
322
285
|
updatedConditions[conditionIndex] = {
|
|
323
286
|
...updatedConditions[conditionIndex],
|
|
324
287
|
...updateData,
|
|
325
288
|
};
|
|
326
|
-
|
|
327
289
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
328
290
|
blockingConditions: updatedConditions,
|
|
329
291
|
lastUpdated: serverTimestamp(),
|
|
330
|
-
updatedBy:
|
|
292
|
+
updatedBy: requesterId,
|
|
331
293
|
});
|
|
332
294
|
};
|
|
333
295
|
|
|
@@ -335,24 +297,23 @@ export const removeBlockingConditionUtil = async (
|
|
|
335
297
|
db: Firestore,
|
|
336
298
|
patientId: string,
|
|
337
299
|
conditionIndex: number,
|
|
338
|
-
|
|
300
|
+
requesterId: string,
|
|
301
|
+
requesterRoles: UserRole[]
|
|
339
302
|
): Promise<void> => {
|
|
303
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
340
304
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
341
305
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
342
|
-
|
|
343
306
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
344
307
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
345
308
|
throw new Error("Invalid blocking condition index");
|
|
346
309
|
}
|
|
347
|
-
|
|
348
310
|
const updatedConditions = medicalInfo.blockingConditions.filter(
|
|
349
311
|
(_, index) => index !== conditionIndex
|
|
350
312
|
);
|
|
351
|
-
|
|
352
313
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
353
314
|
blockingConditions: updatedConditions,
|
|
354
315
|
lastUpdated: serverTimestamp(),
|
|
355
|
-
updatedBy:
|
|
316
|
+
updatedBy: requesterId,
|
|
356
317
|
});
|
|
357
318
|
};
|
|
358
319
|
|
|
@@ -361,15 +322,16 @@ export const addContraindicationUtil = async (
|
|
|
361
322
|
db: Firestore,
|
|
362
323
|
patientId: string,
|
|
363
324
|
data: AddContraindicationData,
|
|
364
|
-
|
|
325
|
+
requesterId: string,
|
|
326
|
+
requesterRoles: UserRole[]
|
|
365
327
|
): Promise<void> => {
|
|
366
|
-
await
|
|
328
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
329
|
+
await ensureMedicalInfoExists(db, patientId, requesterId);
|
|
367
330
|
const validatedData = addContraindicationSchema.parse(data);
|
|
368
|
-
|
|
369
331
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
370
332
|
contraindications: arrayUnion(validatedData),
|
|
371
333
|
lastUpdated: serverTimestamp(),
|
|
372
|
-
updatedBy:
|
|
334
|
+
updatedBy: requesterId,
|
|
373
335
|
});
|
|
374
336
|
};
|
|
375
337
|
|
|
@@ -377,29 +339,27 @@ export const updateContraindicationUtil = async (
|
|
|
377
339
|
db: Firestore,
|
|
378
340
|
patientId: string,
|
|
379
341
|
data: UpdateContraindicationData,
|
|
380
|
-
|
|
342
|
+
requesterId: string,
|
|
343
|
+
requesterRoles: UserRole[]
|
|
381
344
|
): Promise<void> => {
|
|
345
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
382
346
|
const validatedData = updateContraindicationSchema.parse(data);
|
|
383
347
|
const { contraindicationIndex, ...updateData } = validatedData;
|
|
384
|
-
|
|
385
348
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
386
349
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
387
|
-
|
|
388
350
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
389
351
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
390
352
|
throw new Error("Invalid contraindication index");
|
|
391
353
|
}
|
|
392
|
-
|
|
393
354
|
const updatedContraindications = [...medicalInfo.contraindications];
|
|
394
355
|
updatedContraindications[contraindicationIndex] = {
|
|
395
356
|
...updatedContraindications[contraindicationIndex],
|
|
396
357
|
...updateData,
|
|
397
358
|
};
|
|
398
|
-
|
|
399
359
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
400
360
|
contraindications: updatedContraindications,
|
|
401
361
|
lastUpdated: serverTimestamp(),
|
|
402
|
-
updatedBy:
|
|
362
|
+
updatedBy: requesterId,
|
|
403
363
|
});
|
|
404
364
|
};
|
|
405
365
|
|
|
@@ -407,24 +367,23 @@ export const removeContraindicationUtil = async (
|
|
|
407
367
|
db: Firestore,
|
|
408
368
|
patientId: string,
|
|
409
369
|
contraindicationIndex: number,
|
|
410
|
-
|
|
370
|
+
requesterId: string,
|
|
371
|
+
requesterRoles: UserRole[]
|
|
411
372
|
): Promise<void> => {
|
|
373
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
412
374
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
413
375
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
414
|
-
|
|
415
376
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
416
377
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
417
378
|
throw new Error("Invalid contraindication index");
|
|
418
379
|
}
|
|
419
|
-
|
|
420
380
|
const updatedContraindications = medicalInfo.contraindications.filter(
|
|
421
381
|
(_, index) => index !== contraindicationIndex
|
|
422
382
|
);
|
|
423
|
-
|
|
424
383
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
425
384
|
contraindications: updatedContraindications,
|
|
426
385
|
lastUpdated: serverTimestamp(),
|
|
427
|
-
updatedBy:
|
|
386
|
+
updatedBy: requesterId,
|
|
428
387
|
});
|
|
429
388
|
};
|
|
430
389
|
|
|
@@ -433,15 +392,16 @@ export const addMedicationUtil = async (
|
|
|
433
392
|
db: Firestore,
|
|
434
393
|
patientId: string,
|
|
435
394
|
data: AddMedicationData,
|
|
436
|
-
|
|
395
|
+
requesterId: string,
|
|
396
|
+
requesterRoles: UserRole[]
|
|
437
397
|
): Promise<void> => {
|
|
438
|
-
await
|
|
398
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
399
|
+
await ensureMedicalInfoExists(db, patientId, requesterId);
|
|
439
400
|
const validatedData = addMedicationSchema.parse(data);
|
|
440
|
-
|
|
441
401
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
442
402
|
currentMedications: arrayUnion(validatedData),
|
|
443
403
|
lastUpdated: serverTimestamp(),
|
|
444
|
-
updatedBy:
|
|
404
|
+
updatedBy: requesterId,
|
|
445
405
|
});
|
|
446
406
|
};
|
|
447
407
|
|
|
@@ -449,29 +409,27 @@ export const updateMedicationUtil = async (
|
|
|
449
409
|
db: Firestore,
|
|
450
410
|
patientId: string,
|
|
451
411
|
data: UpdateMedicationData,
|
|
452
|
-
|
|
412
|
+
requesterId: string,
|
|
413
|
+
requesterRoles: UserRole[]
|
|
453
414
|
): Promise<void> => {
|
|
415
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
454
416
|
const validatedData = updateMedicationSchema.parse(data);
|
|
455
417
|
const { medicationIndex, ...updateData } = validatedData;
|
|
456
|
-
|
|
457
418
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
458
419
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
459
|
-
|
|
460
420
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
461
421
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
462
422
|
throw new Error("Invalid medication index");
|
|
463
423
|
}
|
|
464
|
-
|
|
465
424
|
const updatedMedications = [...medicalInfo.currentMedications];
|
|
466
425
|
updatedMedications[medicationIndex] = {
|
|
467
426
|
...updatedMedications[medicationIndex],
|
|
468
427
|
...updateData,
|
|
469
428
|
};
|
|
470
|
-
|
|
471
429
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
472
430
|
currentMedications: updatedMedications,
|
|
473
431
|
lastUpdated: serverTimestamp(),
|
|
474
|
-
updatedBy:
|
|
432
|
+
updatedBy: requesterId,
|
|
475
433
|
});
|
|
476
434
|
};
|
|
477
435
|
|
|
@@ -479,23 +437,22 @@ export const removeMedicationUtil = async (
|
|
|
479
437
|
db: Firestore,
|
|
480
438
|
patientId: string,
|
|
481
439
|
medicationIndex: number,
|
|
482
|
-
|
|
440
|
+
requesterId: string,
|
|
441
|
+
requesterRoles: UserRole[]
|
|
483
442
|
): Promise<void> => {
|
|
443
|
+
await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
|
|
484
444
|
const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
|
|
485
445
|
if (!doc.exists()) throw new Error("Medical info not found");
|
|
486
|
-
|
|
487
446
|
const medicalInfo = doc.data() as PatientMedicalInfo;
|
|
488
447
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
489
448
|
throw new Error("Invalid medication index");
|
|
490
449
|
}
|
|
491
|
-
|
|
492
450
|
const updatedMedications = medicalInfo.currentMedications.filter(
|
|
493
451
|
(_, index) => index !== medicationIndex
|
|
494
452
|
);
|
|
495
|
-
|
|
496
453
|
await updateDoc(getMedicalInfoDocRef(db, patientId), {
|
|
497
454
|
currentMedications: updatedMedications,
|
|
498
455
|
lastUpdated: serverTimestamp(),
|
|
499
|
-
updatedBy:
|
|
456
|
+
updatedBy: requesterId,
|
|
500
457
|
});
|
|
501
458
|
};
|
|
@@ -51,6 +51,14 @@ export const createPatientProfileUtil = async (
|
|
|
51
51
|
try {
|
|
52
52
|
console.log("[createPatientProfileUtil] Starting patient profile creation");
|
|
53
53
|
const validatedData = createPatientProfileSchema.parse(data);
|
|
54
|
+
|
|
55
|
+
// This utility is for creating standard profiles, so userRef is required here.
|
|
56
|
+
if (!validatedData.userRef) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
"userRef is required to create a standard patient profile."
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
const patientId = generateId();
|
|
55
63
|
console.log(`[createPatientProfileUtil] Generated patientId: ${patientId}`);
|
|
56
64
|
|
|
@@ -68,6 +76,7 @@ export const createPatientProfileUtil = async (
|
|
|
68
76
|
},
|
|
69
77
|
isActive: validatedData.isActive,
|
|
70
78
|
isVerified: validatedData.isVerified,
|
|
79
|
+
isManual: validatedData.isManual,
|
|
71
80
|
doctors: validatedData.doctors || [],
|
|
72
81
|
clinics: validatedData.clinics || [],
|
|
73
82
|
doctorIds: validatedData.doctors?.map((d) => d.userRef) || [],
|