@blackcode_sa/metaestetics-api 1.12.26 → 1.12.28
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 +2 -0
- package/dist/admin/index.d.ts +2 -0
- package/dist/admin/index.js +31 -14
- package/dist/admin/index.mjs +31 -14
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +64 -140
- package/dist/index.mjs +65 -146
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +34 -12
- package/src/admin/booking/booking.calculator.ts +14 -9
- package/src/admin/booking/timezones-problem.md +49 -5
- package/src/services/clinic/clinic.service.ts +1 -1
- package/src/services/patient/patient.service.ts +117 -282
- package/src/services/patient/utils/sensitive.utils.ts +81 -67
- package/src/services/user/user.service.ts +9 -3
|
@@ -5,13 +5,9 @@ import {
|
|
|
5
5
|
updateDoc,
|
|
6
6
|
arrayUnion,
|
|
7
7
|
serverTimestamp,
|
|
8
|
-
} from
|
|
9
|
-
import { BaseService } from
|
|
10
|
-
import {
|
|
11
|
-
MediaService,
|
|
12
|
-
MediaAccessLevel,
|
|
13
|
-
MediaResource,
|
|
14
|
-
} from "../media/media.service";
|
|
8
|
+
} from 'firebase/firestore';
|
|
9
|
+
import { BaseService } from '../base.service';
|
|
10
|
+
import { MediaService, MediaAccessLevel, MediaResource } from '../media/media.service';
|
|
15
11
|
import {
|
|
16
12
|
PatientProfile,
|
|
17
13
|
PatientSensitiveInfo,
|
|
@@ -38,11 +34,11 @@ import {
|
|
|
38
34
|
RequesterInfo,
|
|
39
35
|
PatientProfileForDoctor,
|
|
40
36
|
CreateManualPatientData,
|
|
41
|
-
} from
|
|
42
|
-
import { Auth } from
|
|
43
|
-
import { Firestore } from
|
|
44
|
-
import { FirebaseApp } from
|
|
45
|
-
import { Timestamp } from
|
|
37
|
+
} from '../../types/patient';
|
|
38
|
+
import { Auth } from 'firebase/auth';
|
|
39
|
+
import { Firestore } from 'firebase/firestore';
|
|
40
|
+
import { FirebaseApp } from 'firebase/app';
|
|
41
|
+
import { Timestamp } from 'firebase/firestore';
|
|
46
42
|
|
|
47
43
|
// Importujemo utility funkcije
|
|
48
44
|
import {
|
|
@@ -63,6 +59,7 @@ import {
|
|
|
63
59
|
createSensitiveInfoUtil,
|
|
64
60
|
getSensitiveInfoUtil,
|
|
65
61
|
updateSensitiveInfoUtil,
|
|
62
|
+
claimPatientSensitiveInfoUtil,
|
|
66
63
|
createMedicalInfoUtil,
|
|
67
64
|
getMedicalInfoUtil,
|
|
68
65
|
updateVitalStatsUtil,
|
|
@@ -94,12 +91,9 @@ import {
|
|
|
94
91
|
markPatientTokenAsUsedUtil,
|
|
95
92
|
getActiveInviteTokensByClinicUtil,
|
|
96
93
|
getActiveInviteTokensByPatientUtil,
|
|
97
|
-
} from
|
|
94
|
+
} from './utils';
|
|
98
95
|
|
|
99
|
-
import {
|
|
100
|
-
CreatePatientTokenData,
|
|
101
|
-
PatientToken,
|
|
102
|
-
} from "../../types/patient/token.types";
|
|
96
|
+
import { CreatePatientTokenData, PatientToken } from '../../types/patient/token.types';
|
|
103
97
|
|
|
104
98
|
export class PatientService extends BaseService {
|
|
105
99
|
private mediaService: MediaService;
|
|
@@ -110,9 +104,7 @@ export class PatientService extends BaseService {
|
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
// Metode za rad sa profilom pacijenta
|
|
113
|
-
async createPatientProfile(
|
|
114
|
-
data: CreatePatientProfileData
|
|
115
|
-
): Promise<PatientProfile> {
|
|
107
|
+
async createPatientProfile(data: CreatePatientProfileData): Promise<PatientProfile> {
|
|
116
108
|
return createPatientProfileUtil(this.db, data, () => this.generateId());
|
|
117
109
|
}
|
|
118
110
|
|
|
@@ -131,21 +123,21 @@ export class PatientService extends BaseService {
|
|
|
131
123
|
*/
|
|
132
124
|
async createManualPatient(
|
|
133
125
|
data: CreateManualPatientData,
|
|
134
|
-
requester: RequesterInfo
|
|
126
|
+
requester: RequesterInfo,
|
|
135
127
|
): Promise<PatientProfile> {
|
|
136
128
|
console.log(
|
|
137
129
|
`[PatientService.createManualPatient] Attempting to create manual patient by requester:`,
|
|
138
|
-
requester
|
|
130
|
+
requester,
|
|
139
131
|
);
|
|
140
132
|
|
|
141
133
|
// Security Check: Ensure the requester is a clinic admin
|
|
142
134
|
if (
|
|
143
|
-
requester.role !==
|
|
135
|
+
requester.role !== 'clinic_admin' ||
|
|
144
136
|
!requester.associatedClinicId ||
|
|
145
137
|
requester.associatedClinicId !== data.clinicId
|
|
146
138
|
) {
|
|
147
139
|
throw new Error(
|
|
148
|
-
|
|
140
|
+
'Unauthorized: Requester must be a clinic admin and can only add patients to their own clinic.',
|
|
149
141
|
);
|
|
150
142
|
}
|
|
151
143
|
|
|
@@ -184,7 +176,7 @@ export class PatientService extends BaseService {
|
|
|
184
176
|
|
|
185
177
|
// 2. Create Patient Sensitive Info
|
|
186
178
|
const sensitiveInfoRef = getSensitiveInfoDocRef(this.db, patientId);
|
|
187
|
-
const newSensitiveInfo: Omit<PatientSensitiveInfo,
|
|
179
|
+
const newSensitiveInfo: Omit<PatientSensitiveInfo, 'photoUrl'> = {
|
|
188
180
|
patientId,
|
|
189
181
|
firstName: data.firstName,
|
|
190
182
|
lastName: data.lastName,
|
|
@@ -208,7 +200,7 @@ export class PatientService extends BaseService {
|
|
|
208
200
|
contraindications: [],
|
|
209
201
|
allergies: [],
|
|
210
202
|
currentMedications: [],
|
|
211
|
-
emergencyNotes:
|
|
203
|
+
emergencyNotes: '',
|
|
212
204
|
lastUpdated: now,
|
|
213
205
|
updatedBy: requester.id, // The admin who created the record
|
|
214
206
|
};
|
|
@@ -217,15 +209,13 @@ export class PatientService extends BaseService {
|
|
|
217
209
|
await batch.commit();
|
|
218
210
|
|
|
219
211
|
console.log(
|
|
220
|
-
`[PatientService.createManualPatient] Successfully created manual patient with ID: ${patientId}
|
|
212
|
+
`[PatientService.createManualPatient] Successfully created manual patient with ID: ${patientId}`,
|
|
221
213
|
);
|
|
222
214
|
|
|
223
215
|
return newProfile;
|
|
224
216
|
}
|
|
225
217
|
|
|
226
|
-
async getPatientProfileByUserRef(
|
|
227
|
-
userRef: string
|
|
228
|
-
): Promise<PatientProfile | null> {
|
|
218
|
+
async getPatientProfileByUserRef(userRef: string): Promise<PatientProfile | null> {
|
|
229
219
|
return getPatientProfileByUserRefUtil(this.db, userRef);
|
|
230
220
|
}
|
|
231
221
|
|
|
@@ -233,7 +223,7 @@ export class PatientService extends BaseService {
|
|
|
233
223
|
async updatePatientLocation(
|
|
234
224
|
patientId: string,
|
|
235
225
|
latitude: number,
|
|
236
|
-
longitude: number
|
|
226
|
+
longitude: number,
|
|
237
227
|
): Promise<void> {
|
|
238
228
|
await updatePatientLocationUtil(this.db, patientId, latitude, longitude);
|
|
239
229
|
}
|
|
@@ -241,30 +231,30 @@ export class PatientService extends BaseService {
|
|
|
241
231
|
async updatePatientLocationByUserRef(
|
|
242
232
|
userRef: string,
|
|
243
233
|
latitude: number,
|
|
244
|
-
longitude: number
|
|
234
|
+
longitude: number,
|
|
245
235
|
): Promise<void> {
|
|
246
236
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
247
|
-
if (!profile) throw new Error(
|
|
237
|
+
if (!profile) throw new Error('Patient profile not found');
|
|
248
238
|
await this.updatePatientLocation(profile.id, latitude, longitude);
|
|
249
239
|
}
|
|
250
240
|
|
|
251
241
|
async createLocationInfo(
|
|
252
242
|
data: CreatePatientLocationInfoData,
|
|
253
|
-
requesterId: string
|
|
243
|
+
requesterId: string,
|
|
254
244
|
): Promise<PatientLocationInfo> {
|
|
255
245
|
return createLocationInfoUtil(this.db, data, requesterId);
|
|
256
246
|
}
|
|
257
247
|
|
|
258
248
|
async getLocationInfo(
|
|
259
249
|
patientId: string,
|
|
260
|
-
requesterId: string
|
|
250
|
+
requesterId: string,
|
|
261
251
|
): Promise<PatientLocationInfo | null> {
|
|
262
252
|
return getLocationInfoUtil(this.db, patientId, requesterId);
|
|
263
253
|
}
|
|
264
254
|
|
|
265
255
|
async getLocationInfoByUserRef(
|
|
266
256
|
userRef: string,
|
|
267
|
-
requesterId: string
|
|
257
|
+
requesterId: string,
|
|
268
258
|
): Promise<PatientLocationInfo | null> {
|
|
269
259
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
270
260
|
if (!profile) return null;
|
|
@@ -274,7 +264,7 @@ export class PatientService extends BaseService {
|
|
|
274
264
|
async updateLocationInfo(
|
|
275
265
|
patientId: string,
|
|
276
266
|
data: UpdatePatientLocationInfoData,
|
|
277
|
-
requesterId: string
|
|
267
|
+
requesterId: string,
|
|
278
268
|
): Promise<PatientLocationInfo> {
|
|
279
269
|
return updateLocationInfoUtil(this.db, patientId, data, requesterId);
|
|
280
270
|
}
|
|
@@ -282,11 +272,11 @@ export class PatientService extends BaseService {
|
|
|
282
272
|
// Metode za rad sa osetljivim informacijama
|
|
283
273
|
async createSensitiveInfo(
|
|
284
274
|
data: CreatePatientSensitiveInfoData,
|
|
285
|
-
requesterUserId: string
|
|
275
|
+
requesterUserId: string,
|
|
286
276
|
): Promise<PatientSensitiveInfo> {
|
|
287
277
|
const currentUser = await this.getCurrentUser();
|
|
288
278
|
if (currentUser.uid !== requesterUserId) {
|
|
289
|
-
throw new Error(
|
|
279
|
+
throw new Error('Requester does not match authenticated user.');
|
|
290
280
|
}
|
|
291
281
|
|
|
292
282
|
return createSensitiveInfoUtil(
|
|
@@ -294,30 +284,25 @@ export class PatientService extends BaseService {
|
|
|
294
284
|
data,
|
|
295
285
|
requesterUserId,
|
|
296
286
|
currentUser.roles,
|
|
297
|
-
this.mediaService
|
|
287
|
+
this.mediaService,
|
|
298
288
|
);
|
|
299
289
|
}
|
|
300
290
|
|
|
301
291
|
async getSensitiveInfo(
|
|
302
292
|
patientId: string,
|
|
303
|
-
requesterUserId: string
|
|
293
|
+
requesterUserId: string,
|
|
304
294
|
): Promise<PatientSensitiveInfo | null> {
|
|
305
295
|
const currentUser = await this.getCurrentUser();
|
|
306
296
|
if (currentUser.uid !== requesterUserId) {
|
|
307
297
|
// Allow for read-only access if authorized, but for now we check identity
|
|
308
298
|
// This could be expanded later based on practitioner/admin roles
|
|
309
299
|
}
|
|
310
|
-
return getSensitiveInfoUtil(
|
|
311
|
-
this.db,
|
|
312
|
-
patientId,
|
|
313
|
-
requesterUserId,
|
|
314
|
-
currentUser.roles
|
|
315
|
-
);
|
|
300
|
+
return getSensitiveInfoUtil(this.db, patientId, requesterUserId, currentUser.roles);
|
|
316
301
|
}
|
|
317
302
|
|
|
318
303
|
async getSensitiveInfoByUserRef(
|
|
319
304
|
userRef: string,
|
|
320
|
-
requesterUserId: string
|
|
305
|
+
requesterUserId: string,
|
|
321
306
|
): Promise<PatientSensitiveInfo | null> {
|
|
322
307
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
323
308
|
if (!profile) return null;
|
|
@@ -328,11 +313,11 @@ export class PatientService extends BaseService {
|
|
|
328
313
|
async updateSensitiveInfo(
|
|
329
314
|
patientId: string,
|
|
330
315
|
data: UpdatePatientSensitiveInfoData,
|
|
331
|
-
requesterUserId: string
|
|
316
|
+
requesterUserId: string,
|
|
332
317
|
): Promise<PatientSensitiveInfo> {
|
|
333
318
|
const currentUser = await this.getCurrentUser();
|
|
334
319
|
if (currentUser.uid !== requesterUserId) {
|
|
335
|
-
throw new Error(
|
|
320
|
+
throw new Error('Requester does not match authenticated user.');
|
|
336
321
|
}
|
|
337
322
|
return updateSensitiveInfoUtil(
|
|
338
323
|
this.db,
|
|
@@ -340,219 +325,122 @@ export class PatientService extends BaseService {
|
|
|
340
325
|
data,
|
|
341
326
|
requesterUserId,
|
|
342
327
|
currentUser.roles,
|
|
343
|
-
this.mediaService
|
|
328
|
+
this.mediaService,
|
|
344
329
|
);
|
|
345
330
|
}
|
|
346
331
|
|
|
347
|
-
|
|
348
|
-
async createMedicalInfo(
|
|
332
|
+
async claimPatientSensitiveInfo(
|
|
349
333
|
patientId: string,
|
|
350
|
-
|
|
351
|
-
): Promise<
|
|
334
|
+
userId: string,
|
|
335
|
+
): Promise<PatientSensitiveInfo> {
|
|
336
|
+
return claimPatientSensitiveInfoUtil(this.db, patientId, userId);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Metode za rad sa medicinskim informacijama
|
|
340
|
+
async createMedicalInfo(patientId: string, data: CreatePatientMedicalInfoData): Promise<void> {
|
|
352
341
|
const currentUser = await this.getCurrentUser();
|
|
353
|
-
await createMedicalInfoUtil(
|
|
354
|
-
this.db,
|
|
355
|
-
patientId,
|
|
356
|
-
data,
|
|
357
|
-
currentUser.uid,
|
|
358
|
-
currentUser.roles
|
|
359
|
-
);
|
|
342
|
+
await createMedicalInfoUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
360
343
|
}
|
|
361
344
|
|
|
362
345
|
async getMedicalInfo(patientId: string): Promise<PatientMedicalInfo> {
|
|
363
346
|
const currentUser = await this.getCurrentUser();
|
|
364
|
-
return getMedicalInfoUtil(
|
|
365
|
-
this.db,
|
|
366
|
-
patientId,
|
|
367
|
-
currentUser.uid,
|
|
368
|
-
currentUser.roles
|
|
369
|
-
);
|
|
347
|
+
return getMedicalInfoUtil(this.db, patientId, currentUser.uid, currentUser.roles);
|
|
370
348
|
}
|
|
371
349
|
|
|
372
350
|
async getMedicalInfoByUserRef(userRef: string): Promise<PatientMedicalInfo> {
|
|
373
351
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
374
|
-
if (!profile) throw new Error(
|
|
352
|
+
if (!profile) throw new Error('Patient profile not found');
|
|
375
353
|
return this.getMedicalInfo(profile.id);
|
|
376
354
|
}
|
|
377
355
|
|
|
378
356
|
// Metode za rad sa vitalnim statistikama
|
|
379
|
-
async updateVitalStats(
|
|
380
|
-
patientId: string,
|
|
381
|
-
data: UpdateVitalStatsData
|
|
382
|
-
): Promise<void> {
|
|
357
|
+
async updateVitalStats(patientId: string, data: UpdateVitalStatsData): Promise<void> {
|
|
383
358
|
const currentUser = await this.getCurrentUser();
|
|
384
|
-
await updateVitalStatsUtil(
|
|
385
|
-
this.db,
|
|
386
|
-
patientId,
|
|
387
|
-
data,
|
|
388
|
-
currentUser.uid,
|
|
389
|
-
currentUser.roles
|
|
390
|
-
);
|
|
359
|
+
await updateVitalStatsUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
391
360
|
}
|
|
392
361
|
|
|
393
362
|
// Metode za rad sa alergijama
|
|
394
363
|
async addAllergy(patientId: string, data: AddAllergyData): Promise<void> {
|
|
395
364
|
const currentUser = await this.getCurrentUser();
|
|
396
|
-
await addAllergyUtil(
|
|
397
|
-
this.db,
|
|
398
|
-
patientId,
|
|
399
|
-
data,
|
|
400
|
-
currentUser.uid,
|
|
401
|
-
currentUser.roles
|
|
402
|
-
);
|
|
365
|
+
await addAllergyUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
403
366
|
}
|
|
404
367
|
|
|
405
|
-
async updateAllergy(
|
|
406
|
-
patientId: string,
|
|
407
|
-
data: UpdateAllergyData
|
|
408
|
-
): Promise<void> {
|
|
368
|
+
async updateAllergy(patientId: string, data: UpdateAllergyData): Promise<void> {
|
|
409
369
|
const currentUser = await this.getCurrentUser();
|
|
410
|
-
await updateAllergyUtil(
|
|
411
|
-
this.db,
|
|
412
|
-
patientId,
|
|
413
|
-
data,
|
|
414
|
-
currentUser.uid,
|
|
415
|
-
currentUser.roles
|
|
416
|
-
);
|
|
370
|
+
await updateAllergyUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
417
371
|
}
|
|
418
372
|
|
|
419
373
|
async removeAllergy(patientId: string, allergyIndex: number): Promise<void> {
|
|
420
374
|
const currentUser = await this.getCurrentUser();
|
|
421
|
-
await removeAllergyUtil(
|
|
422
|
-
this.db,
|
|
423
|
-
patientId,
|
|
424
|
-
allergyIndex,
|
|
425
|
-
currentUser.uid,
|
|
426
|
-
currentUser.roles
|
|
427
|
-
);
|
|
375
|
+
await removeAllergyUtil(this.db, patientId, allergyIndex, currentUser.uid, currentUser.roles);
|
|
428
376
|
}
|
|
429
377
|
|
|
430
378
|
// Metode za rad sa blocking conditions
|
|
431
|
-
async addBlockingCondition(
|
|
432
|
-
patientId: string,
|
|
433
|
-
data: AddBlockingConditionData
|
|
434
|
-
): Promise<void> {
|
|
379
|
+
async addBlockingCondition(patientId: string, data: AddBlockingConditionData): Promise<void> {
|
|
435
380
|
const currentUser = await this.getCurrentUser();
|
|
436
|
-
await addBlockingConditionUtil(
|
|
437
|
-
this.db,
|
|
438
|
-
patientId,
|
|
439
|
-
data,
|
|
440
|
-
currentUser.uid,
|
|
441
|
-
currentUser.roles
|
|
442
|
-
);
|
|
381
|
+
await addBlockingConditionUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
443
382
|
}
|
|
444
383
|
|
|
445
384
|
async updateBlockingCondition(
|
|
446
385
|
patientId: string,
|
|
447
|
-
data: UpdateBlockingConditionData
|
|
386
|
+
data: UpdateBlockingConditionData,
|
|
448
387
|
): Promise<void> {
|
|
449
388
|
const currentUser = await this.getCurrentUser();
|
|
450
|
-
await updateBlockingConditionUtil(
|
|
451
|
-
this.db,
|
|
452
|
-
patientId,
|
|
453
|
-
data,
|
|
454
|
-
currentUser.uid,
|
|
455
|
-
currentUser.roles
|
|
456
|
-
);
|
|
389
|
+
await updateBlockingConditionUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
457
390
|
}
|
|
458
391
|
|
|
459
|
-
async removeBlockingCondition(
|
|
460
|
-
patientId: string,
|
|
461
|
-
conditionIndex: number
|
|
462
|
-
): Promise<void> {
|
|
392
|
+
async removeBlockingCondition(patientId: string, conditionIndex: number): Promise<void> {
|
|
463
393
|
const currentUser = await this.getCurrentUser();
|
|
464
394
|
await removeBlockingConditionUtil(
|
|
465
395
|
this.db,
|
|
466
396
|
patientId,
|
|
467
397
|
conditionIndex,
|
|
468
398
|
currentUser.uid,
|
|
469
|
-
currentUser.roles
|
|
399
|
+
currentUser.roles,
|
|
470
400
|
);
|
|
471
401
|
}
|
|
472
402
|
|
|
473
403
|
// Metode za rad sa kontraindikacijama
|
|
474
|
-
async addContraindication(
|
|
475
|
-
patientId: string,
|
|
476
|
-
data: AddContraindicationData
|
|
477
|
-
): Promise<void> {
|
|
404
|
+
async addContraindication(patientId: string, data: AddContraindicationData): Promise<void> {
|
|
478
405
|
const currentUser = await this.getCurrentUser();
|
|
479
|
-
await addContraindicationUtil(
|
|
480
|
-
this.db,
|
|
481
|
-
patientId,
|
|
482
|
-
data,
|
|
483
|
-
currentUser.uid,
|
|
484
|
-
currentUser.roles
|
|
485
|
-
);
|
|
406
|
+
await addContraindicationUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
486
407
|
}
|
|
487
408
|
|
|
488
|
-
async updateContraindication(
|
|
489
|
-
patientId: string,
|
|
490
|
-
data: UpdateContraindicationData
|
|
491
|
-
): Promise<void> {
|
|
409
|
+
async updateContraindication(patientId: string, data: UpdateContraindicationData): Promise<void> {
|
|
492
410
|
const currentUser = await this.getCurrentUser();
|
|
493
|
-
await updateContraindicationUtil(
|
|
494
|
-
this.db,
|
|
495
|
-
patientId,
|
|
496
|
-
data,
|
|
497
|
-
currentUser.uid,
|
|
498
|
-
currentUser.roles
|
|
499
|
-
);
|
|
411
|
+
await updateContraindicationUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
500
412
|
}
|
|
501
413
|
|
|
502
|
-
async removeContraindication(
|
|
503
|
-
patientId: string,
|
|
504
|
-
contraindicationIndex: number
|
|
505
|
-
): Promise<void> {
|
|
414
|
+
async removeContraindication(patientId: string, contraindicationIndex: number): Promise<void> {
|
|
506
415
|
const currentUser = await this.getCurrentUser();
|
|
507
416
|
await removeContraindicationUtil(
|
|
508
417
|
this.db,
|
|
509
418
|
patientId,
|
|
510
419
|
contraindicationIndex,
|
|
511
420
|
currentUser.uid,
|
|
512
|
-
currentUser.roles
|
|
421
|
+
currentUser.roles,
|
|
513
422
|
);
|
|
514
423
|
}
|
|
515
424
|
|
|
516
425
|
// Metode za rad sa medikacijama
|
|
517
|
-
async addMedication(
|
|
518
|
-
patientId: string,
|
|
519
|
-
data: AddMedicationData
|
|
520
|
-
): Promise<void> {
|
|
426
|
+
async addMedication(patientId: string, data: AddMedicationData): Promise<void> {
|
|
521
427
|
const currentUser = await this.getCurrentUser();
|
|
522
|
-
await addMedicationUtil(
|
|
523
|
-
this.db,
|
|
524
|
-
patientId,
|
|
525
|
-
data,
|
|
526
|
-
currentUser.uid,
|
|
527
|
-
currentUser.roles
|
|
528
|
-
);
|
|
428
|
+
await addMedicationUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
529
429
|
}
|
|
530
430
|
|
|
531
|
-
async updateMedication(
|
|
532
|
-
patientId: string,
|
|
533
|
-
data: UpdateMedicationData
|
|
534
|
-
): Promise<void> {
|
|
431
|
+
async updateMedication(patientId: string, data: UpdateMedicationData): Promise<void> {
|
|
535
432
|
const currentUser = await this.getCurrentUser();
|
|
536
|
-
await updateMedicationUtil(
|
|
537
|
-
this.db,
|
|
538
|
-
patientId,
|
|
539
|
-
data,
|
|
540
|
-
currentUser.uid,
|
|
541
|
-
currentUser.roles
|
|
542
|
-
);
|
|
433
|
+
await updateMedicationUtil(this.db, patientId, data, currentUser.uid, currentUser.roles);
|
|
543
434
|
}
|
|
544
435
|
|
|
545
|
-
async removeMedication(
|
|
546
|
-
patientId: string,
|
|
547
|
-
medicationIndex: number
|
|
548
|
-
): Promise<void> {
|
|
436
|
+
async removeMedication(patientId: string, medicationIndex: number): Promise<void> {
|
|
549
437
|
const currentUser = await this.getCurrentUser();
|
|
550
438
|
await removeMedicationUtil(
|
|
551
439
|
this.db,
|
|
552
440
|
patientId,
|
|
553
441
|
medicationIndex,
|
|
554
442
|
currentUser.uid,
|
|
555
|
-
currentUser.roles
|
|
443
|
+
currentUser.roles,
|
|
556
444
|
);
|
|
557
445
|
}
|
|
558
446
|
|
|
@@ -563,7 +451,7 @@ export class PatientService extends BaseService {
|
|
|
563
451
|
|
|
564
452
|
async addExpoTokenByUserRef(userRef: string, token: string): Promise<void> {
|
|
565
453
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
566
|
-
if (!profile) throw new Error(
|
|
454
|
+
if (!profile) throw new Error('Patient profile not found');
|
|
567
455
|
await this.addExpoToken(profile.id, token);
|
|
568
456
|
}
|
|
569
457
|
|
|
@@ -571,12 +459,9 @@ export class PatientService extends BaseService {
|
|
|
571
459
|
await removeExpoTokenUtil(this.db, patientId, token);
|
|
572
460
|
}
|
|
573
461
|
|
|
574
|
-
async removeExpoTokenByUserRef(
|
|
575
|
-
userRef: string,
|
|
576
|
-
token: string
|
|
577
|
-
): Promise<void> {
|
|
462
|
+
async removeExpoTokenByUserRef(userRef: string, token: string): Promise<void> {
|
|
578
463
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
579
|
-
if (!profile) throw new Error(
|
|
464
|
+
if (!profile) throw new Error('Patient profile not found');
|
|
580
465
|
await this.removeExpoToken(profile.id, token);
|
|
581
466
|
}
|
|
582
467
|
|
|
@@ -586,19 +471,17 @@ export class PatientService extends BaseService {
|
|
|
586
471
|
|
|
587
472
|
async addPointsByUserRef(userRef: string, points: number): Promise<void> {
|
|
588
473
|
const profile = await this.getPatientProfileByUserRef(userRef);
|
|
589
|
-
if (!profile) throw new Error(
|
|
474
|
+
if (!profile) throw new Error('Patient profile not found');
|
|
590
475
|
await this.addPoints(profile.id, points);
|
|
591
476
|
}
|
|
592
477
|
|
|
593
478
|
private async getCurrentUser(): Promise<any> {
|
|
594
479
|
if (!this.auth.currentUser) {
|
|
595
|
-
throw new Error(
|
|
480
|
+
throw new Error('No authenticated user');
|
|
596
481
|
}
|
|
597
|
-
const userDoc = await getDoc(
|
|
598
|
-
doc(this.db, "users", this.auth.currentUser.uid)
|
|
599
|
-
);
|
|
482
|
+
const userDoc = await getDoc(doc(this.db, 'users', this.auth.currentUser.uid));
|
|
600
483
|
if (!userDoc.exists()) {
|
|
601
|
-
throw new Error(
|
|
484
|
+
throw new Error('User not found');
|
|
602
485
|
}
|
|
603
486
|
return userDoc.data();
|
|
604
487
|
}
|
|
@@ -651,20 +534,15 @@ export class PatientService extends BaseService {
|
|
|
651
534
|
* @param file - File or Blob to upload
|
|
652
535
|
* @returns URL of the uploaded photo
|
|
653
536
|
*/
|
|
654
|
-
async uploadProfilePhoto(
|
|
655
|
-
patientId
|
|
656
|
-
file: File | Blob
|
|
657
|
-
): Promise<string> {
|
|
658
|
-
console.log(
|
|
659
|
-
`[PatientService] Uploading profile photo for patient ${patientId}`
|
|
660
|
-
);
|
|
537
|
+
async uploadProfilePhoto(patientId: string, file: File | Blob): Promise<string> {
|
|
538
|
+
console.log(`[PatientService] Uploading profile photo for patient ${patientId}`);
|
|
661
539
|
|
|
662
540
|
const mediaMetadata = await this.mediaService.uploadMedia(
|
|
663
541
|
file,
|
|
664
542
|
patientId, // Using patientId as ownerId
|
|
665
543
|
MediaAccessLevel.PRIVATE, // Profile photos should be private
|
|
666
|
-
|
|
667
|
-
file instanceof File ? file.name : `profile_photo_${patientId}
|
|
544
|
+
'patient_profile_photos',
|
|
545
|
+
file instanceof File ? file.name : `profile_photo_${patientId}`,
|
|
668
546
|
);
|
|
669
547
|
|
|
670
548
|
// Update the patient sensitive info with the new photo URL
|
|
@@ -682,38 +560,26 @@ export class PatientService extends BaseService {
|
|
|
682
560
|
* @param file - New file or Blob to upload
|
|
683
561
|
* @returns URL of the new uploaded photo
|
|
684
562
|
*/
|
|
685
|
-
async updateProfilePhoto(
|
|
686
|
-
patientId
|
|
687
|
-
file: File | Blob
|
|
688
|
-
): Promise<string> {
|
|
689
|
-
console.log(
|
|
690
|
-
`[PatientService] Updating profile photo for patient ${patientId}`
|
|
691
|
-
);
|
|
563
|
+
async updateProfilePhoto(patientId: string, file: File | Blob): Promise<string> {
|
|
564
|
+
console.log(`[PatientService] Updating profile photo for patient ${patientId}`);
|
|
692
565
|
|
|
693
566
|
// Get current patient sensitive info to check for existing photo
|
|
694
567
|
const currentUser = await this.getCurrentUser();
|
|
695
|
-
const currentSensitiveInfo = await this.getSensitiveInfo(
|
|
696
|
-
patientId,
|
|
697
|
-
currentUser.uid
|
|
698
|
-
);
|
|
568
|
+
const currentSensitiveInfo = await this.getSensitiveInfo(patientId, currentUser.uid);
|
|
699
569
|
|
|
700
570
|
// Delete old photo if it exists and is managed by our MediaService
|
|
701
|
-
if (
|
|
702
|
-
currentSensitiveInfo?.photoUrl &&
|
|
703
|
-
typeof currentSensitiveInfo.photoUrl === "string"
|
|
704
|
-
) {
|
|
571
|
+
if (currentSensitiveInfo?.photoUrl && typeof currentSensitiveInfo.photoUrl === 'string') {
|
|
705
572
|
try {
|
|
706
|
-
const existingMediaMetadata =
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
);
|
|
573
|
+
const existingMediaMetadata = await this.mediaService.getMediaMetadataByUrl(
|
|
574
|
+
currentSensitiveInfo.photoUrl,
|
|
575
|
+
);
|
|
710
576
|
if (existingMediaMetadata) {
|
|
711
577
|
await this.mediaService.deleteMedia(existingMediaMetadata.id);
|
|
712
578
|
}
|
|
713
579
|
} catch (error) {
|
|
714
580
|
console.warn(
|
|
715
581
|
`[PatientService] Could not delete old profile photo for patient ${patientId}:`,
|
|
716
|
-
error
|
|
582
|
+
error,
|
|
717
583
|
);
|
|
718
584
|
// Continue with upload even if deletion fails
|
|
719
585
|
}
|
|
@@ -728,33 +594,24 @@ export class PatientService extends BaseService {
|
|
|
728
594
|
* @param patientId - ID of the patient
|
|
729
595
|
*/
|
|
730
596
|
async deleteProfilePhoto(patientId: string): Promise<void> {
|
|
731
|
-
console.log(
|
|
732
|
-
`[PatientService] Deleting profile photo for patient ${patientId}`
|
|
733
|
-
);
|
|
597
|
+
console.log(`[PatientService] Deleting profile photo for patient ${patientId}`);
|
|
734
598
|
|
|
735
599
|
// Get current patient sensitive info to find the photo URL
|
|
736
600
|
const currentUser = await this.getCurrentUser();
|
|
737
|
-
const currentSensitiveInfo = await this.getSensitiveInfo(
|
|
738
|
-
patientId,
|
|
739
|
-
currentUser.uid
|
|
740
|
-
);
|
|
601
|
+
const currentSensitiveInfo = await this.getSensitiveInfo(patientId, currentUser.uid);
|
|
741
602
|
|
|
742
|
-
if (
|
|
743
|
-
currentSensitiveInfo?.photoUrl &&
|
|
744
|
-
typeof currentSensitiveInfo.photoUrl === "string"
|
|
745
|
-
) {
|
|
603
|
+
if (currentSensitiveInfo?.photoUrl && typeof currentSensitiveInfo.photoUrl === 'string') {
|
|
746
604
|
try {
|
|
747
|
-
const existingMediaMetadata =
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
);
|
|
605
|
+
const existingMediaMetadata = await this.mediaService.getMediaMetadataByUrl(
|
|
606
|
+
currentSensitiveInfo.photoUrl,
|
|
607
|
+
);
|
|
751
608
|
if (existingMediaMetadata) {
|
|
752
609
|
await this.mediaService.deleteMedia(existingMediaMetadata.id);
|
|
753
610
|
}
|
|
754
611
|
} catch (error) {
|
|
755
612
|
console.warn(
|
|
756
613
|
`[PatientService] Could not delete profile photo for patient ${patientId}:`,
|
|
757
|
-
error
|
|
614
|
+
error,
|
|
758
615
|
);
|
|
759
616
|
}
|
|
760
617
|
|
|
@@ -774,14 +631,14 @@ export class PatientService extends BaseService {
|
|
|
774
631
|
*/
|
|
775
632
|
private async handleProfilePhotoUpload(
|
|
776
633
|
photoUrl: MediaResource | undefined,
|
|
777
|
-
patientId: string
|
|
634
|
+
patientId: string,
|
|
778
635
|
): Promise<string | undefined> {
|
|
779
636
|
if (!photoUrl) {
|
|
780
637
|
return undefined;
|
|
781
638
|
}
|
|
782
639
|
|
|
783
640
|
// If it's already a URL string, return it as is
|
|
784
|
-
if (typeof photoUrl ===
|
|
641
|
+
if (typeof photoUrl === 'string') {
|
|
785
642
|
return photoUrl;
|
|
786
643
|
}
|
|
787
644
|
|
|
@@ -796,14 +653,14 @@ export class PatientService extends BaseService {
|
|
|
796
653
|
// Metode za ažuriranje profila
|
|
797
654
|
async updatePatientProfile(
|
|
798
655
|
patientId: string,
|
|
799
|
-
data: Partial<Omit<PatientProfile,
|
|
656
|
+
data: Partial<Omit<PatientProfile, 'id' | 'createdAt' | 'updatedAt'>>,
|
|
800
657
|
): Promise<PatientProfile> {
|
|
801
658
|
return updatePatientProfileUtil(this.db, patientId, data);
|
|
802
659
|
}
|
|
803
660
|
|
|
804
661
|
async updatePatientProfileByUserRef(
|
|
805
662
|
userRef: string,
|
|
806
|
-
data: Partial<Omit<PatientProfile,
|
|
663
|
+
data: Partial<Omit<PatientProfile, 'id' | 'createdAt' | 'updatedAt'>>,
|
|
807
664
|
): Promise<PatientProfile> {
|
|
808
665
|
return updatePatientProfileByUserRefUtil(this.db, userRef, data);
|
|
809
666
|
}
|
|
@@ -818,7 +675,7 @@ export class PatientService extends BaseService {
|
|
|
818
675
|
*/
|
|
819
676
|
async searchPatients(
|
|
820
677
|
params: SearchPatientsParams,
|
|
821
|
-
requester: RequesterInfo
|
|
678
|
+
requester: RequesterInfo,
|
|
822
679
|
): Promise<PatientProfile[]> {
|
|
823
680
|
// We can potentially add more service-level logic here in the future,
|
|
824
681
|
// like fetching additional data or enriching the results.
|
|
@@ -827,7 +684,7 @@ export class PatientService extends BaseService {
|
|
|
827
684
|
`[PatientService.searchPatients] Initiating search with params:`,
|
|
828
685
|
params,
|
|
829
686
|
`by requester:`,
|
|
830
|
-
requester
|
|
687
|
+
requester,
|
|
831
688
|
);
|
|
832
689
|
|
|
833
690
|
// The utility function already handles validation and security checks.
|
|
@@ -846,10 +703,7 @@ export class PatientService extends BaseService {
|
|
|
846
703
|
limit?: number;
|
|
847
704
|
startAfter?: string;
|
|
848
705
|
}): Promise<PatientProfile[]> {
|
|
849
|
-
console.log(
|
|
850
|
-
`[PatientService.getAllPatients] Fetching patients with options:`,
|
|
851
|
-
options
|
|
852
|
-
);
|
|
706
|
+
console.log(`[PatientService.getAllPatients] Fetching patients with options:`, options);
|
|
853
707
|
return getAllPatientsUtil(this.db, options);
|
|
854
708
|
}
|
|
855
709
|
|
|
@@ -867,10 +721,10 @@ export class PatientService extends BaseService {
|
|
|
867
721
|
options?: {
|
|
868
722
|
limit?: number;
|
|
869
723
|
startAfter?: string;
|
|
870
|
-
}
|
|
724
|
+
},
|
|
871
725
|
): Promise<PatientProfile[]> {
|
|
872
726
|
console.log(
|
|
873
|
-
`[PatientService.getPatientsByPractitioner] Fetching patients for practitioner: ${practitionerId}
|
|
727
|
+
`[PatientService.getPatientsByPractitioner] Fetching patients for practitioner: ${practitionerId}`,
|
|
874
728
|
);
|
|
875
729
|
return getPatientsByPractitionerUtil(this.db, practitionerId, options);
|
|
876
730
|
}
|
|
@@ -889,16 +743,12 @@ export class PatientService extends BaseService {
|
|
|
889
743
|
options?: {
|
|
890
744
|
limit?: number;
|
|
891
745
|
startAfter?: string;
|
|
892
|
-
}
|
|
746
|
+
},
|
|
893
747
|
): Promise<PatientProfileForDoctor[]> {
|
|
894
748
|
console.log(
|
|
895
|
-
`[PatientService.getPatientsByPractitionerWithDetails] Fetching detailed patient profiles for practitioner: ${practitionerId}
|
|
896
|
-
);
|
|
897
|
-
return getPatientsByPractitionerWithDetailsUtil(
|
|
898
|
-
this.db,
|
|
899
|
-
practitionerId,
|
|
900
|
-
options
|
|
749
|
+
`[PatientService.getPatientsByPractitionerWithDetails] Fetching detailed patient profiles for practitioner: ${practitionerId}`,
|
|
901
750
|
);
|
|
751
|
+
return getPatientsByPractitionerWithDetailsUtil(this.db, practitionerId, options);
|
|
902
752
|
}
|
|
903
753
|
|
|
904
754
|
/**
|
|
@@ -915,11 +765,9 @@ export class PatientService extends BaseService {
|
|
|
915
765
|
options?: {
|
|
916
766
|
limit?: number;
|
|
917
767
|
startAfter?: string;
|
|
918
|
-
}
|
|
768
|
+
},
|
|
919
769
|
): Promise<PatientProfile[]> {
|
|
920
|
-
console.log(
|
|
921
|
-
`[PatientService.getPatientsByClinic] Fetching patients for clinic: ${clinicId}`
|
|
922
|
-
);
|
|
770
|
+
console.log(`[PatientService.getPatientsByClinic] Fetching patients for clinic: ${clinicId}`);
|
|
923
771
|
return getPatientsByClinicUtil(this.db, clinicId, options);
|
|
924
772
|
}
|
|
925
773
|
|
|
@@ -930,17 +778,14 @@ export class PatientService extends BaseService {
|
|
|
930
778
|
* @param {string} createdBy - ID of the admin user creating the token.
|
|
931
779
|
* @returns {Promise<PatientToken>} The created token.
|
|
932
780
|
*/
|
|
933
|
-
async createPatientToken(
|
|
934
|
-
data: CreatePatientTokenData,
|
|
935
|
-
createdBy: string
|
|
936
|
-
): Promise<PatientToken> {
|
|
781
|
+
async createPatientToken(data: CreatePatientTokenData, createdBy: string): Promise<PatientToken> {
|
|
937
782
|
// We assume the 'createdBy' user is validated to be a clinic admin
|
|
938
783
|
// in the calling context (e.g., a cloud function or API endpoint).
|
|
939
784
|
return createPatientTokenUtil(
|
|
940
785
|
this.db,
|
|
941
786
|
data,
|
|
942
787
|
createdBy,
|
|
943
|
-
() => this.generateId() // Pass the ID generation function
|
|
788
|
+
() => this.generateId(), // Pass the ID generation function
|
|
944
789
|
);
|
|
945
790
|
}
|
|
946
791
|
|
|
@@ -950,9 +795,7 @@ export class PatientService extends BaseService {
|
|
|
950
795
|
* @param {string} tokenString - The token string to validate.
|
|
951
796
|
* @returns {Promise<PatientToken | null>} The token if found and valid, otherwise null.
|
|
952
797
|
*/
|
|
953
|
-
async validatePatientToken(
|
|
954
|
-
tokenString: string
|
|
955
|
-
): Promise<PatientToken | null> {
|
|
798
|
+
async validatePatientToken(tokenString: string): Promise<PatientToken | null> {
|
|
956
799
|
return validatePatientTokenUtil(this.db, tokenString);
|
|
957
800
|
}
|
|
958
801
|
|
|
@@ -964,11 +807,7 @@ export class PatientService extends BaseService {
|
|
|
964
807
|
* @param {string} userId - The ID of the user who is using the token.
|
|
965
808
|
* @returns {Promise<void>}
|
|
966
809
|
*/
|
|
967
|
-
async markPatientTokenAsUsed(
|
|
968
|
-
tokenId: string,
|
|
969
|
-
patientId: string,
|
|
970
|
-
userId: string
|
|
971
|
-
): Promise<void> {
|
|
810
|
+
async markPatientTokenAsUsed(tokenId: string, patientId: string, userId: string): Promise<void> {
|
|
972
811
|
return markPatientTokenAsUsedUtil(this.db, tokenId, patientId, userId);
|
|
973
812
|
}
|
|
974
813
|
|
|
@@ -979,9 +818,7 @@ export class PatientService extends BaseService {
|
|
|
979
818
|
* @param {string} clinicId - The ID of the clinic.
|
|
980
819
|
* @returns {Promise<PatientToken[]>} An array of active tokens for the clinic.
|
|
981
820
|
*/
|
|
982
|
-
async getActiveInviteTokensByClinic(
|
|
983
|
-
clinicId: string
|
|
984
|
-
): Promise<PatientToken[]> {
|
|
821
|
+
async getActiveInviteTokensByClinic(clinicId: string): Promise<PatientToken[]> {
|
|
985
822
|
return getActiveInviteTokensByClinicUtil(this.db, clinicId);
|
|
986
823
|
}
|
|
987
824
|
|
|
@@ -992,9 +829,7 @@ export class PatientService extends BaseService {
|
|
|
992
829
|
* @param {string} patientId - The ID of the patient.
|
|
993
830
|
* @returns {Promise<PatientToken[]>} An array of active tokens for the patient.
|
|
994
831
|
*/
|
|
995
|
-
async getActiveInviteTokensByPatient(
|
|
996
|
-
patientId: string
|
|
997
|
-
): Promise<PatientToken[]> {
|
|
832
|
+
async getActiveInviteTokensByPatient(patientId: string): Promise<PatientToken[]> {
|
|
998
833
|
// Security check should be done in the calling context to ensure
|
|
999
834
|
// the admin has permission to view this patient's tokens.
|
|
1000
835
|
return getActiveInviteTokensByPatientUtil(this.db, patientId);
|