@blackcode_sa/metaestetics-api 1.6.12 → 1.6.14
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 +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.js +4612 -37506
- package/dist/admin/index.mjs +4592 -37509
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +5 -11
- package/src/admin/index.ts +3 -0
- package/src/admin/notifications/notifications.admin.ts +19 -24
- package/src/admin/requirements/patient-requirements.admin.service.ts +25 -32
- package/src/types/clinic/index.ts +19 -27
- package/src/types/patient/index.ts +20 -22
- package/src/utils/TIMESTAMPS.md +176 -0
- package/src/utils/TimestampUtils.ts +136 -0
package/dist/index.d.mts
CHANGED
|
@@ -1905,7 +1905,7 @@ interface CreatePatientProfileData {
|
|
|
1905
1905
|
/**
|
|
1906
1906
|
* Tip za ažuriranje Patient profila
|
|
1907
1907
|
*/
|
|
1908
|
-
interface UpdatePatientProfileData extends Partial<Omit<PatientProfile,
|
|
1908
|
+
interface UpdatePatientProfileData extends Partial<Omit<PatientProfile, 'id' | 'createdAt' | 'updatedAt'>> {
|
|
1909
1909
|
updatedAt?: FieldValue;
|
|
1910
1910
|
}
|
|
1911
1911
|
/**
|
|
@@ -1924,7 +1924,7 @@ interface RequesterInfo {
|
|
|
1924
1924
|
/** ID of the clinic admin user or practitioner user making the request. */
|
|
1925
1925
|
id: string;
|
|
1926
1926
|
/** Role of the requester, determining the search context. */
|
|
1927
|
-
role:
|
|
1927
|
+
role: 'clinic_admin' | 'practitioner';
|
|
1928
1928
|
/** If role is 'clinic_admin', this is the associated clinic ID. */
|
|
1929
1929
|
associatedClinicId?: string;
|
|
1930
1930
|
/** If role is 'practitioner', this is the associated practitioner profile ID. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1905,7 +1905,7 @@ interface CreatePatientProfileData {
|
|
|
1905
1905
|
/**
|
|
1906
1906
|
* Tip za ažuriranje Patient profila
|
|
1907
1907
|
*/
|
|
1908
|
-
interface UpdatePatientProfileData extends Partial<Omit<PatientProfile,
|
|
1908
|
+
interface UpdatePatientProfileData extends Partial<Omit<PatientProfile, 'id' | 'createdAt' | 'updatedAt'>> {
|
|
1909
1909
|
updatedAt?: FieldValue;
|
|
1910
1910
|
}
|
|
1911
1911
|
/**
|
|
@@ -1924,7 +1924,7 @@ interface RequesterInfo {
|
|
|
1924
1924
|
/** ID of the clinic admin user or practitioner user making the request. */
|
|
1925
1925
|
id: string;
|
|
1926
1926
|
/** Role of the requester, determining the search context. */
|
|
1927
|
-
role:
|
|
1927
|
+
role: 'clinic_admin' | 'practitioner';
|
|
1928
1928
|
/** If role is 'clinic_admin', this is the associated clinic ID. */
|
|
1929
1929
|
associatedClinicId?: string;
|
|
1930
1930
|
/** If role is 'practitioner', this is the associated practitioner profile ID. */
|
package/package.json
CHANGED
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
} from "../../types/calendar";
|
|
63
63
|
import { DocumentManagerAdminService } from "../documentation-templates/document-manager.admin";
|
|
64
64
|
import { LinkedFormInfo } from "../../types/appointment";
|
|
65
|
+
import { TimestampUtils } from "../../utils/TimestampUtils";
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* Interface for the data required by orchestrateAppointmentCreation
|
|
@@ -205,15 +206,8 @@ export class BookingAdmin {
|
|
|
205
206
|
private adminTimestampToClientTimestamp(
|
|
206
207
|
timestamp: admin.firestore.Timestamp
|
|
207
208
|
): any {
|
|
208
|
-
//
|
|
209
|
-
return
|
|
210
|
-
seconds: timestamp.seconds,
|
|
211
|
-
nanoseconds: timestamp.nanoseconds,
|
|
212
|
-
toDate: () => timestamp.toDate(),
|
|
213
|
-
toMillis: () => timestamp.toMillis(),
|
|
214
|
-
valueOf: () => timestamp.valueOf(),
|
|
215
|
-
// Add any other required methods/properties
|
|
216
|
-
};
|
|
209
|
+
// Use TimestampUtils instead of custom implementation
|
|
210
|
+
return TimestampUtils.adminToClient(timestamp);
|
|
217
211
|
}
|
|
218
212
|
|
|
219
213
|
/**
|
|
@@ -223,8 +217,8 @@ export class BookingAdmin {
|
|
|
223
217
|
return events.map((event) => ({
|
|
224
218
|
...event,
|
|
225
219
|
eventTime: {
|
|
226
|
-
start:
|
|
227
|
-
end:
|
|
220
|
+
start: TimestampUtils.adminToClient(event.eventTime.start),
|
|
221
|
+
end: TimestampUtils.adminToClient(event.eventTime.end),
|
|
228
222
|
},
|
|
229
223
|
// Convert any other timestamps in the event if needed
|
|
230
224
|
}));
|
package/src/admin/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { DoctorInfo } from "../types/clinic";
|
|
13
13
|
import { Procedure, ProcedureSummaryInfo } from "../types/procedure";
|
|
14
14
|
import { PatientProfile } from "../types/patient";
|
|
15
|
+
import { Appointment, AppointmentStatus } from "../types/appointment";
|
|
15
16
|
|
|
16
17
|
// Explicitly import the services to re-export them by name
|
|
17
18
|
import { ClinicAggregationService } from "./aggregation/clinic/clinic.aggregation.service";
|
|
@@ -46,6 +47,7 @@ export type { Practitioner, PractitionerToken } from "../types/practitioner";
|
|
|
46
47
|
export type { DoctorInfo } from "../types/clinic";
|
|
47
48
|
export type { Procedure, ProcedureSummaryInfo } from "../types/procedure";
|
|
48
49
|
export type { PatientProfile as Patient } from "../types/patient";
|
|
50
|
+
export type { Appointment } from "../types/appointment";
|
|
49
51
|
|
|
50
52
|
// Re-export enums/consts
|
|
51
53
|
export {
|
|
@@ -55,6 +57,7 @@ export {
|
|
|
55
57
|
} from "../types/notifications";
|
|
56
58
|
export { UserRole } from "../types";
|
|
57
59
|
export { PractitionerTokenStatus } from "../types/practitioner";
|
|
60
|
+
export { AppointmentStatus } from "../types/appointment";
|
|
58
61
|
|
|
59
62
|
// Export admin classes/services explicitly by name
|
|
60
63
|
export {
|
|
@@ -8,6 +8,7 @@ import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";
|
|
|
8
8
|
import { Appointment, PaymentStatus } from "../../types/appointment";
|
|
9
9
|
import { UserRole } from "../../types";
|
|
10
10
|
import { Timestamp as FirebaseClientTimestamp } from "@firebase/firestore";
|
|
11
|
+
import { TimestampUtils } from "../../utils/TimestampUtils";
|
|
11
12
|
|
|
12
13
|
export class NotificationsAdmin {
|
|
13
14
|
private expo: Expo;
|
|
@@ -277,10 +278,9 @@ export class NotificationsAdmin {
|
|
|
277
278
|
}
|
|
278
279
|
|
|
279
280
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
280
|
-
const clientCompatibleNotificationTime =
|
|
281
|
-
adminTsNow
|
|
282
|
-
|
|
283
|
-
);
|
|
281
|
+
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
282
|
+
adminTsNow
|
|
283
|
+
) as FirebaseClientTimestamp;
|
|
284
284
|
|
|
285
285
|
const notificationData: Omit<
|
|
286
286
|
Notification,
|
|
@@ -354,10 +354,9 @@ export class NotificationsAdmin {
|
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
357
|
-
const clientCompatibleNotificationTime =
|
|
358
|
-
adminTsNow
|
|
359
|
-
|
|
360
|
-
);
|
|
357
|
+
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
358
|
+
adminTsNow
|
|
359
|
+
) as FirebaseClientTimestamp;
|
|
361
360
|
|
|
362
361
|
const notificationData: Omit<
|
|
363
362
|
Notification,
|
|
@@ -406,10 +405,9 @@ export class NotificationsAdmin {
|
|
|
406
405
|
const body = `Action Required: A new time has been proposed for your appointment for ${appointment.procedureInfo.name}. Please review in the app.`;
|
|
407
406
|
|
|
408
407
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
409
|
-
const clientCompatibleNotificationTime =
|
|
410
|
-
adminTsNow
|
|
411
|
-
|
|
412
|
-
);
|
|
408
|
+
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
409
|
+
adminTsNow
|
|
410
|
+
) as FirebaseClientTimestamp;
|
|
413
411
|
|
|
414
412
|
const notificationData: Omit<
|
|
415
413
|
Notification,
|
|
@@ -461,10 +459,9 @@ export class NotificationsAdmin {
|
|
|
461
459
|
.toLocaleDateString()} is now ${appointment.paymentStatus}.`;
|
|
462
460
|
|
|
463
461
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
464
|
-
const clientCompatibleNotificationTime =
|
|
465
|
-
adminTsNow
|
|
466
|
-
|
|
467
|
-
);
|
|
462
|
+
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
463
|
+
adminTsNow
|
|
464
|
+
) as FirebaseClientTimestamp;
|
|
468
465
|
|
|
469
466
|
const notificationType =
|
|
470
467
|
appointment.paymentStatus === PaymentStatus.PAID
|
|
@@ -518,10 +515,9 @@ export class NotificationsAdmin {
|
|
|
518
515
|
const body = `How was your recent appointment for ${appointment.procedureInfo.name}? We'd love to hear your feedback!`;
|
|
519
516
|
|
|
520
517
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
521
|
-
const clientCompatibleNotificationTime =
|
|
522
|
-
adminTsNow
|
|
523
|
-
|
|
524
|
-
);
|
|
518
|
+
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
519
|
+
adminTsNow
|
|
520
|
+
) as FirebaseClientTimestamp;
|
|
525
521
|
|
|
526
522
|
const notificationData: Omit<
|
|
527
523
|
Notification,
|
|
@@ -581,10 +577,9 @@ export class NotificationsAdmin {
|
|
|
581
577
|
.toLocaleDateString()}.`;
|
|
582
578
|
|
|
583
579
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
584
|
-
const clientCompatibleNotificationTime =
|
|
585
|
-
adminTsNow
|
|
586
|
-
|
|
587
|
-
);
|
|
580
|
+
const clientCompatibleNotificationTime = TimestampUtils.adminToClient(
|
|
581
|
+
adminTsNow
|
|
582
|
+
) as FirebaseClientTimestamp;
|
|
588
583
|
|
|
589
584
|
const tempNotificationType = NotificationType.GENERAL_MESSAGE;
|
|
590
585
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { PatientProfile, PATIENTS_COLLECTION } from "../../types/patient";
|
|
18
18
|
import { UserRole } from "../../types"; // Assuming UserRole is in the main types index
|
|
19
19
|
import { NotificationsAdmin } from "../notifications/notifications.admin";
|
|
20
|
+
import { TimestampUtils } from "../../utils/TimestampUtils";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @class PatientRequirementsAdminService
|
|
@@ -106,10 +107,9 @@ export class PatientRequirementsAdminService {
|
|
|
106
107
|
...currentInstruction,
|
|
107
108
|
notificationId: undefined,
|
|
108
109
|
status: PatientInstructionStatus.CANCELLED,
|
|
109
|
-
updatedAt:
|
|
110
|
-
adminTsNow
|
|
111
|
-
|
|
112
|
-
),
|
|
110
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
111
|
+
adminTsNow
|
|
112
|
+
) as FirebaseClientTimestamp,
|
|
113
113
|
};
|
|
114
114
|
updatedInstructions[i] = currentInstruction;
|
|
115
115
|
instructionUpdatesMade = true;
|
|
@@ -144,10 +144,9 @@ export class PatientRequirementsAdminService {
|
|
|
144
144
|
currentInstruction = {
|
|
145
145
|
...currentInstruction,
|
|
146
146
|
notificationId: undefined,
|
|
147
|
-
updatedAt:
|
|
148
|
-
adminTsNow
|
|
149
|
-
|
|
150
|
-
),
|
|
147
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
148
|
+
adminTsNow
|
|
149
|
+
) as FirebaseClientTimestamp,
|
|
151
150
|
};
|
|
152
151
|
updatedInstructions[i] = currentInstruction;
|
|
153
152
|
instructionUpdatesMade = true;
|
|
@@ -196,10 +195,9 @@ export class PatientRequirementsAdminService {
|
|
|
196
195
|
...currentInstruction,
|
|
197
196
|
notificationId: createdNotificationId,
|
|
198
197
|
status: PatientInstructionStatus.PENDING_NOTIFICATION,
|
|
199
|
-
updatedAt:
|
|
200
|
-
adminTsNow
|
|
201
|
-
|
|
202
|
-
),
|
|
198
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
199
|
+
adminTsNow
|
|
200
|
+
) as FirebaseClientTimestamp,
|
|
203
201
|
};
|
|
204
202
|
updatedInstructions[i] = currentInstruction;
|
|
205
203
|
instructionUpdatesMade = true;
|
|
@@ -225,10 +223,9 @@ export class PatientRequirementsAdminService {
|
|
|
225
223
|
const finalAdminTsNow = admin.firestore.Timestamp.now();
|
|
226
224
|
await instanceDocRef.update({
|
|
227
225
|
instructions: updatedInstructions, // Array of instructions with actual Timestamps
|
|
228
|
-
updatedAt:
|
|
229
|
-
finalAdminTsNow
|
|
230
|
-
|
|
231
|
-
),
|
|
226
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
227
|
+
finalAdminTsNow
|
|
228
|
+
) as FirebaseClientTimestamp,
|
|
232
229
|
});
|
|
233
230
|
}
|
|
234
231
|
}
|
|
@@ -332,10 +329,9 @@ export class PatientRequirementsAdminService {
|
|
|
332
329
|
currentInstruction = {
|
|
333
330
|
...currentInstruction,
|
|
334
331
|
status: PatientInstructionStatus.MISSED,
|
|
335
|
-
updatedAt:
|
|
336
|
-
adminNowForMissed
|
|
337
|
-
|
|
338
|
-
),
|
|
332
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
333
|
+
adminNowForMissed
|
|
334
|
+
) as FirebaseClientTimestamp,
|
|
339
335
|
};
|
|
340
336
|
updatedInstructions[i] = currentInstruction;
|
|
341
337
|
changesMade = true;
|
|
@@ -349,10 +345,9 @@ export class PatientRequirementsAdminService {
|
|
|
349
345
|
const finalAdminNowForMissedUpdate = admin.firestore.Timestamp.now();
|
|
350
346
|
await instanceRef.update({
|
|
351
347
|
instructions: updatedInstructions, // Array of instructions with actual Timestamps
|
|
352
|
-
updatedAt:
|
|
353
|
-
finalAdminNowForMissedUpdate
|
|
354
|
-
|
|
355
|
-
),
|
|
348
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
349
|
+
finalAdminNowForMissedUpdate
|
|
350
|
+
) as FirebaseClientTimestamp,
|
|
356
351
|
});
|
|
357
352
|
console.log(
|
|
358
353
|
`[PRA_Service] Updated missed instructions for instance ${instanceId}.`
|
|
@@ -416,10 +411,9 @@ export class PatientRequirementsAdminService {
|
|
|
416
411
|
);
|
|
417
412
|
await instanceRef.update({
|
|
418
413
|
overallStatus: PatientRequirementOverallStatus.ACTIVE,
|
|
419
|
-
updatedAt:
|
|
420
|
-
admin.firestore.Timestamp.now()
|
|
421
|
-
|
|
422
|
-
),
|
|
414
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
415
|
+
admin.firestore.Timestamp.now()
|
|
416
|
+
) as FirebaseClientTimestamp,
|
|
423
417
|
});
|
|
424
418
|
}
|
|
425
419
|
return;
|
|
@@ -468,10 +462,9 @@ export class PatientRequirementsAdminService {
|
|
|
468
462
|
const adminTsNow = admin.firestore.Timestamp.now();
|
|
469
463
|
await instanceRef.update({
|
|
470
464
|
overallStatus: newOverallStatus,
|
|
471
|
-
updatedAt:
|
|
472
|
-
adminTsNow
|
|
473
|
-
|
|
474
|
-
),
|
|
465
|
+
updatedAt: TimestampUtils.adminToClient(
|
|
466
|
+
adminTsNow
|
|
467
|
+
) as FirebaseClientTimestamp,
|
|
475
468
|
});
|
|
476
469
|
} else {
|
|
477
470
|
console.log(
|
|
@@ -1,26 +1,18 @@
|
|
|
1
|
-
import { Timestamp, FieldValue } from
|
|
2
|
-
import type { ProcedureFamily } from
|
|
3
|
-
import type { TreatmentBenefit } from
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from
|
|
8
|
-
import type { ClinicInfo } from "../profile";
|
|
9
|
-
import { ClinicReviewInfo } from "../reviews";
|
|
10
|
-
import { ProcedureSummaryInfo } from "../procedure";
|
|
1
|
+
import { Timestamp, FieldValue } from 'firebase/firestore';
|
|
2
|
+
import type { ProcedureFamily } from '../../backoffice/types/static/procedure-family.types';
|
|
3
|
+
import type { TreatmentBenefit } from '../../backoffice/types/static/treatment-benefit.types';
|
|
4
|
+
import type { Currency, PricingMeasure } from '../../backoffice/types/static/pricing.types';
|
|
5
|
+
import type { ClinicInfo } from '../profile';
|
|
6
|
+
import { ClinicReviewInfo } from '../reviews';
|
|
7
|
+
import { ProcedureSummaryInfo } from '../procedure';
|
|
11
8
|
|
|
12
|
-
export const CLINIC_GROUPS_COLLECTION =
|
|
13
|
-
export const CLINIC_ADMINS_COLLECTION =
|
|
14
|
-
export const CLINICS_COLLECTION =
|
|
9
|
+
export const CLINIC_GROUPS_COLLECTION = 'clinic_groups';
|
|
10
|
+
export const CLINIC_ADMINS_COLLECTION = 'clinic_admins';
|
|
11
|
+
export const CLINICS_COLLECTION = 'clinics';
|
|
15
12
|
|
|
16
|
-
import {
|
|
17
|
-
PracticeType,
|
|
18
|
-
Language,
|
|
19
|
-
ClinicTag,
|
|
20
|
-
ClinicPhotoTag,
|
|
21
|
-
} from "./preferences.types";
|
|
13
|
+
import { PracticeType, Language, ClinicTag, ClinicPhotoTag } from './preferences.types';
|
|
22
14
|
|
|
23
|
-
export * from
|
|
15
|
+
export * from './preferences.types';
|
|
24
16
|
|
|
25
17
|
/**
|
|
26
18
|
* Interface for clinic contact information
|
|
@@ -140,9 +132,9 @@ export interface UpdateClinicAdminData extends Partial<CreateClinicAdminData> {
|
|
|
140
132
|
* Enum for admin token status
|
|
141
133
|
*/
|
|
142
134
|
export enum AdminTokenStatus {
|
|
143
|
-
ACTIVE =
|
|
144
|
-
USED =
|
|
145
|
-
EXPIRED =
|
|
135
|
+
ACTIVE = 'active',
|
|
136
|
+
USED = 'used',
|
|
137
|
+
EXPIRED = 'expired',
|
|
146
138
|
}
|
|
147
139
|
|
|
148
140
|
/**
|
|
@@ -171,10 +163,10 @@ export interface AdminInfo {
|
|
|
171
163
|
* Enum for subscription models
|
|
172
164
|
*/
|
|
173
165
|
export enum SubscriptionModel {
|
|
174
|
-
NO_SUBSCRIPTION =
|
|
175
|
-
BASIC =
|
|
176
|
-
PREMIUM =
|
|
177
|
-
ENTERPRISE =
|
|
166
|
+
NO_SUBSCRIPTION = 'no_subscription',
|
|
167
|
+
BASIC = 'basic',
|
|
168
|
+
PREMIUM = 'premium',
|
|
169
|
+
ENTERPRISE = 'enterprise',
|
|
178
170
|
}
|
|
179
171
|
|
|
180
172
|
/**
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { Timestamp, FieldValue } from
|
|
2
|
-
import { User } from
|
|
3
|
-
import type { PatientMedicalInfo } from
|
|
4
|
-
import { PATIENT_MEDICAL_INFO_COLLECTION } from
|
|
1
|
+
import { Timestamp, FieldValue } from 'firebase/firestore';
|
|
2
|
+
import { User } from '..';
|
|
3
|
+
import type { PatientMedicalInfo } from './medical-info.types';
|
|
4
|
+
import { PATIENT_MEDICAL_INFO_COLLECTION } from './medical-info.types';
|
|
5
5
|
|
|
6
|
-
export const PATIENTS_COLLECTION =
|
|
7
|
-
export const PATIENT_SENSITIVE_INFO_COLLECTION =
|
|
8
|
-
export const PATIENT_MEDICAL_HISTORY_COLLECTION =
|
|
9
|
-
export const PATIENT_APPOINTMENTS_COLLECTION =
|
|
10
|
-
export const PATIENT_LOCATION_INFO_COLLECTION =
|
|
6
|
+
export const PATIENTS_COLLECTION = 'patients';
|
|
7
|
+
export const PATIENT_SENSITIVE_INFO_COLLECTION = 'sensitive-info';
|
|
8
|
+
export const PATIENT_MEDICAL_HISTORY_COLLECTION = 'medical-history';
|
|
9
|
+
export const PATIENT_APPOINTMENTS_COLLECTION = 'appointments';
|
|
10
|
+
export const PATIENT_LOCATION_INFO_COLLECTION = 'location-info';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Enumeracija za pol pacijenta
|
|
14
14
|
*/
|
|
15
15
|
export enum Gender {
|
|
16
|
-
MALE =
|
|
17
|
-
FEMALE =
|
|
18
|
-
TRANSGENDER_MALE =
|
|
19
|
-
TRANSGENDER_FEMALE =
|
|
20
|
-
PREFER_NOT_TO_SAY =
|
|
21
|
-
OTHER =
|
|
16
|
+
MALE = 'male',
|
|
17
|
+
FEMALE = 'female',
|
|
18
|
+
TRANSGENDER_MALE = 'transgender_male',
|
|
19
|
+
TRANSGENDER_FEMALE = 'transgender_female',
|
|
20
|
+
PREFER_NOT_TO_SAY = 'prefer_not_to_say',
|
|
21
|
+
OTHER = 'other',
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -83,8 +83,7 @@ export interface CreatePatientLocationInfoData {
|
|
|
83
83
|
/**
|
|
84
84
|
* Tip za ažuriranje lokacijskih informacija
|
|
85
85
|
*/
|
|
86
|
-
export interface UpdatePatientLocationInfoData
|
|
87
|
-
extends Partial<CreatePatientLocationInfoData> {
|
|
86
|
+
export interface UpdatePatientLocationInfoData extends Partial<CreatePatientLocationInfoData> {
|
|
88
87
|
updatedAt?: FieldValue;
|
|
89
88
|
}
|
|
90
89
|
|
|
@@ -129,8 +128,7 @@ export interface CreatePatientSensitiveInfoData {
|
|
|
129
128
|
/**
|
|
130
129
|
* Tip za ažuriranje osetljivih informacija
|
|
131
130
|
*/
|
|
132
|
-
export interface UpdatePatientSensitiveInfoData
|
|
133
|
-
extends Partial<CreatePatientSensitiveInfoData> {
|
|
131
|
+
export interface UpdatePatientSensitiveInfoData extends Partial<CreatePatientSensitiveInfoData> {
|
|
134
132
|
updatedAt?: FieldValue;
|
|
135
133
|
}
|
|
136
134
|
|
|
@@ -198,7 +196,7 @@ export interface CreatePatientProfileData {
|
|
|
198
196
|
* Tip za ažuriranje Patient profila
|
|
199
197
|
*/
|
|
200
198
|
export interface UpdatePatientProfileData
|
|
201
|
-
extends Partial<Omit<PatientProfile,
|
|
199
|
+
extends Partial<Omit<PatientProfile, 'id' | 'createdAt' | 'updatedAt'>> {
|
|
202
200
|
// Use Omit to exclude base fields
|
|
203
201
|
updatedAt?: FieldValue;
|
|
204
202
|
// Note: doctors, clinics, doctorIds, clinicIds should ideally be updated via specific methods (add/removeDoctor/Clinic)
|
|
@@ -221,14 +219,14 @@ export interface RequesterInfo {
|
|
|
221
219
|
/** ID of the clinic admin user or practitioner user making the request. */
|
|
222
220
|
id: string;
|
|
223
221
|
/** Role of the requester, determining the search context. */
|
|
224
|
-
role:
|
|
222
|
+
role: 'clinic_admin' | 'practitioner';
|
|
225
223
|
/** If role is 'clinic_admin', this is the associated clinic ID. */
|
|
226
224
|
associatedClinicId?: string;
|
|
227
225
|
/** If role is 'practitioner', this is the associated practitioner profile ID. */
|
|
228
226
|
associatedPractitionerId?: string;
|
|
229
227
|
}
|
|
230
228
|
|
|
231
|
-
export * from
|
|
229
|
+
export * from './medical-info.types';
|
|
232
230
|
|
|
233
231
|
// This is a type that combines all the patient data - used only in UI Frontend App
|
|
234
232
|
export interface PatientProfileComplete {
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Timestamp Management Strategy
|
|
2
|
+
|
|
3
|
+
## Problem Statement
|
|
4
|
+
|
|
5
|
+
Firebase provides two different Timestamp implementations across its SDKs:
|
|
6
|
+
|
|
7
|
+
1. **Client-side Timestamp**: `import { Timestamp } from 'firebase/firestore'`
|
|
8
|
+
|
|
9
|
+
- Used in client applications (web, mobile)
|
|
10
|
+
- Lives in the `firebase/firestore` package
|
|
11
|
+
|
|
12
|
+
2. **Admin-side Timestamp**: `import * as admin from 'firebase-admin'; admin.firestore.Timestamp`
|
|
13
|
+
- Used in server-side code (Cloud Functions)
|
|
14
|
+
- Lives in the `firebase-admin` package
|
|
15
|
+
|
|
16
|
+
These types are structurally similar but are different JavaScript classes, causing type conflicts when:
|
|
17
|
+
|
|
18
|
+
- Data with client Timestamps is processed in admin code
|
|
19
|
+
- Data with admin Timestamps is sent to client code
|
|
20
|
+
- Shared type definitions across client and admin code
|
|
21
|
+
|
|
22
|
+
## Solution: TimestampUtils
|
|
23
|
+
|
|
24
|
+
We've created a central `TimestampUtils` class providing:
|
|
25
|
+
|
|
26
|
+
1. Conversion utilities between admin and client Timestamps
|
|
27
|
+
2. Best practices for timestamp handling
|
|
28
|
+
3. Consistent patterns for timestamp operations
|
|
29
|
+
|
|
30
|
+
## Usage Guidelines
|
|
31
|
+
|
|
32
|
+
### 1. Type Definitions
|
|
33
|
+
|
|
34
|
+
All shared type definitions (in `src/types/*`) should:
|
|
35
|
+
|
|
36
|
+
- Import Timestamp from client SDK: `import { Timestamp } from 'firebase/firestore'`
|
|
37
|
+
- Define fields using this client Timestamp: `timestamp: Timestamp`
|
|
38
|
+
|
|
39
|
+
This ensures types are consumable by both client and admin code.
|
|
40
|
+
|
|
41
|
+
### 2. Admin Code (Cloud Functions)
|
|
42
|
+
|
|
43
|
+
When writing admin code that:
|
|
44
|
+
|
|
45
|
+
- **Reads data from Firestore**: No conversion needed, as Firestore returns admin Timestamps
|
|
46
|
+
- **Processes timestamps**: Use standard admin timestamp methods
|
|
47
|
+
- **Writes data to shared types**: Convert admin → client using `TimestampUtils.adminToClient()`
|
|
48
|
+
- **Creates server timestamps**: For fields created with `serverTimestamp()`, use Firebase Admin's version: `admin.firestore.FieldValue.serverTimestamp()`
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import * as admin from "firebase-admin";
|
|
54
|
+
import { TimestampUtils } from "../../utils/TimestampUtils";
|
|
55
|
+
import { YourSharedType } from "../../types/shared";
|
|
56
|
+
|
|
57
|
+
// In your admin service
|
|
58
|
+
async function processAndUpdateData() {
|
|
59
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
60
|
+
|
|
61
|
+
// When populating a shared type that clients will use
|
|
62
|
+
const data: YourSharedType = {
|
|
63
|
+
// Convert admin timestamp to client timestamp
|
|
64
|
+
createdAt: TimestampUtils.adminToClient(adminTsNow),
|
|
65
|
+
|
|
66
|
+
// For server timestamps, use admin version
|
|
67
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
68
|
+
|
|
69
|
+
// Other fields...
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// When you need to process objects with nested timestamps
|
|
73
|
+
const objectWithNestedTimestamps = {
|
|
74
|
+
// Complex data from another source
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Convert all timestamps in the object from admin to client
|
|
78
|
+
const clientCompatible = TimestampUtils.convertObjectTimestampsAdminToClient(
|
|
79
|
+
objectWithNestedTimestamps
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Client Code (Web/Mobile Apps)
|
|
85
|
+
|
|
86
|
+
Client code should:
|
|
87
|
+
|
|
88
|
+
- Use `firebase/firestore` Timestamp when needed
|
|
89
|
+
- No conversion is necessary for data returned by `getDocs()` or similar client SDK methods
|
|
90
|
+
- For new timestamps, use `Timestamp.now()` or `Timestamp.fromDate()`
|
|
91
|
+
|
|
92
|
+
### 4. Utility Functions
|
|
93
|
+
|
|
94
|
+
The `TimestampUtils` class provides these key functions:
|
|
95
|
+
|
|
96
|
+
- `adminToClient(adminTimestamp)`: Converts admin → client Timestamp
|
|
97
|
+
- `clientToAdmin(clientTimestamp)`: Converts client → admin Timestamp
|
|
98
|
+
- `nowAsClient()`: Creates current timestamp as client Timestamp
|
|
99
|
+
- `dateToClientTimestamp(date)`: Converts Date → client Timestamp
|
|
100
|
+
- `dateToAdminTimestamp(date)`: Converts Date → admin Timestamp
|
|
101
|
+
- `convertObjectTimestampsAdminToClient(obj)`: Deep conversion of all timestamps in an object from admin → client
|
|
102
|
+
- `convertObjectTimestampsClientToAdmin(obj)`: Deep conversion of all timestamps in an object from client → admin
|
|
103
|
+
|
|
104
|
+
## Common Patterns
|
|
105
|
+
|
|
106
|
+
### 1. Creating New Documents
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// In admin code
|
|
110
|
+
const adminTimestamp = admin.firestore.Timestamp.now();
|
|
111
|
+
const clientCompatibleTimestamp = TimestampUtils.adminToClient(adminTimestamp);
|
|
112
|
+
|
|
113
|
+
const newDocument = {
|
|
114
|
+
id: "doc123",
|
|
115
|
+
createdAt: clientCompatibleTimestamp,
|
|
116
|
+
// For server-managed timestamps, use serverTimestamp()
|
|
117
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
await docRef.set(newDocument);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 2. Updating Fields in a Document
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// In admin code
|
|
127
|
+
await docRef.update({
|
|
128
|
+
someField: "new value",
|
|
129
|
+
// For immediate timestamp, convert admin to client
|
|
130
|
+
processedAt: TimestampUtils.adminToClient(admin.firestore.Timestamp.now()),
|
|
131
|
+
// For server-managed timestamps, use serverTimestamp()
|
|
132
|
+
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 3. Processing Calendar Events
|
|
137
|
+
|
|
138
|
+
Calendar events and date ranges should be consistently handled:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Calendar event time conversion
|
|
142
|
+
const firestoreCompatibleEventTime = {
|
|
143
|
+
start: {
|
|
144
|
+
seconds: newEventTime.start.seconds,
|
|
145
|
+
nanoseconds: newEventTime.start.nanoseconds,
|
|
146
|
+
},
|
|
147
|
+
end: {
|
|
148
|
+
seconds: newEventTime.end.seconds,
|
|
149
|
+
nanoseconds: newEventTime.end.nanoseconds,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Edge Cases and Limitations
|
|
155
|
+
|
|
156
|
+
1. **null/undefined Handling**: All utility methods handle null safely
|
|
157
|
+
2. **Serialization**: If serializing for JSON, convert to ISO strings first
|
|
158
|
+
3. **Performance**: The conversion is lightweight, but for large batches, process efficiently
|
|
159
|
+
|
|
160
|
+
## Migration Strategy
|
|
161
|
+
|
|
162
|
+
1. Identify timestamp usage in your code using static analysis or grep
|
|
163
|
+
2. Replace direct timestamp conversions with TimestampUtils methods
|
|
164
|
+
3. Update methods like:
|
|
165
|
+
- `adminTimestampToClientTimestamp` → `TimestampUtils.adminToClient`
|
|
166
|
+
- Direct creation of client timestamps → `TimestampUtils.nowAsClient`
|
|
167
|
+
4. Test thoroughly after each change
|
|
168
|
+
|
|
169
|
+
## Conclusion
|
|
170
|
+
|
|
171
|
+
Following this strategy will:
|
|
172
|
+
|
|
173
|
+
- Eliminate type errors between admin and client code
|
|
174
|
+
- Provide consistent timestamp handling throughout the codebase
|
|
175
|
+
- Minimize conversion bugs by centralizing conversion logic
|
|
176
|
+
- Make it easier to onboard new developers to the codebase
|