@blackcode_sa/metaestetics-api 1.12.65 → 1.12.66
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 +45 -4
- package/dist/admin/index.mjs +45 -4
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +53 -11
- package/dist/index.mjs +53 -11
- package/package.json +119 -119
- package/src/__mocks__/firstore.ts +10 -10
- package/src/admin/aggregation/README.md +79 -79
- package/src/admin/aggregation/appointment/README.md +128 -128
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +1844 -1844
- package/src/admin/aggregation/appointment/index.ts +1 -1
- package/src/admin/aggregation/clinic/README.md +52 -52
- package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +703 -703
- package/src/admin/aggregation/clinic/index.ts +1 -1
- package/src/admin/aggregation/forms/README.md +13 -13
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +322 -322
- package/src/admin/aggregation/forms/index.ts +1 -1
- package/src/admin/aggregation/index.ts +8 -8
- package/src/admin/aggregation/patient/README.md +27 -27
- package/src/admin/aggregation/patient/index.ts +1 -1
- package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -141
- package/src/admin/aggregation/practitioner/README.md +42 -42
- package/src/admin/aggregation/practitioner/index.ts +1 -1
- package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -433
- package/src/admin/aggregation/practitioner-invite/index.ts +1 -1
- package/src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts +961 -961
- package/src/admin/aggregation/procedure/README.md +43 -43
- package/src/admin/aggregation/procedure/index.ts +1 -1
- package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +702 -702
- package/src/admin/aggregation/reviews/index.ts +1 -1
- package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +689 -641
- package/src/admin/booking/README.md +125 -125
- package/src/admin/booking/booking.admin.ts +1037 -1037
- package/src/admin/booking/booking.calculator.ts +712 -712
- package/src/admin/booking/booking.types.ts +59 -59
- package/src/admin/booking/index.ts +3 -3
- package/src/admin/booking/timezones-problem.md +185 -185
- package/src/admin/calendar/README.md +7 -7
- package/src/admin/calendar/calendar.admin.service.ts +345 -345
- package/src/admin/calendar/index.ts +1 -1
- package/src/admin/documentation-templates/document-manager.admin.ts +260 -260
- package/src/admin/documentation-templates/index.ts +1 -1
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +148 -148
- package/src/admin/free-consultation/index.ts +1 -1
- package/src/admin/index.ts +75 -75
- package/src/admin/logger/index.ts +78 -78
- package/src/admin/mailing/README.md +95 -95
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +732 -732
- package/src/admin/mailing/appointment/index.ts +1 -1
- package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -40
- package/src/admin/mailing/base.mailing.service.ts +208 -208
- package/src/admin/mailing/index.ts +3 -3
- package/src/admin/mailing/practitionerInvite/existing-practitioner-invite.mailing.ts +611 -611
- package/src/admin/mailing/practitionerInvite/index.ts +2 -2
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +395 -395
- package/src/admin/mailing/practitionerInvite/templates/existing-practitioner-invitation.template.ts +155 -155
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -101
- package/src/admin/mailing/practitionerInvite/templates/invite-accepted-notification.template.ts +228 -228
- package/src/admin/mailing/practitionerInvite/templates/invite-rejected-notification.template.ts +242 -242
- package/src/admin/notifications/index.ts +1 -1
- package/src/admin/notifications/notifications.admin.ts +710 -710
- package/src/admin/requirements/README.md +128 -128
- package/src/admin/requirements/index.ts +1 -1
- package/src/admin/requirements/patient-requirements.admin.service.ts +475 -475
- package/src/admin/users/index.ts +1 -1
- package/src/admin/users/user-profile.admin.ts +405 -405
- package/src/backoffice/constants/certification.constants.ts +13 -13
- package/src/backoffice/constants/index.ts +1 -1
- package/src/backoffice/errors/backoffice.errors.ts +181 -181
- package/src/backoffice/errors/index.ts +1 -1
- package/src/backoffice/expo-safe/README.md +26 -26
- package/src/backoffice/expo-safe/index.ts +41 -41
- package/src/backoffice/index.ts +5 -5
- package/src/backoffice/services/FIXES_README.md +102 -102
- package/src/backoffice/services/README.md +40 -40
- package/src/backoffice/services/brand.service.ts +256 -256
- package/src/backoffice/services/category.service.ts +318 -318
- package/src/backoffice/services/constants.service.ts +385 -385
- package/src/backoffice/services/documentation-template.service.ts +202 -202
- package/src/backoffice/services/index.ts +10 -10
- package/src/backoffice/services/migrate-products.ts +116 -116
- package/src/backoffice/services/product.service.ts +553 -553
- package/src/backoffice/services/requirement.service.ts +235 -235
- package/src/backoffice/services/subcategory.service.ts +395 -395
- package/src/backoffice/services/technology.service.ts +1083 -1083
- package/src/backoffice/types/README.md +12 -12
- package/src/backoffice/types/admin-constants.types.ts +69 -69
- package/src/backoffice/types/brand.types.ts +29 -29
- package/src/backoffice/types/category.types.ts +62 -62
- package/src/backoffice/types/documentation-templates.types.ts +28 -28
- package/src/backoffice/types/index.ts +10 -10
- package/src/backoffice/types/procedure-product.types.ts +38 -38
- package/src/backoffice/types/product.types.ts +240 -240
- package/src/backoffice/types/requirement.types.ts +63 -63
- package/src/backoffice/types/static/README.md +18 -18
- package/src/backoffice/types/static/blocking-condition.types.ts +21 -21
- package/src/backoffice/types/static/certification.types.ts +37 -37
- package/src/backoffice/types/static/contraindication.types.ts +19 -19
- package/src/backoffice/types/static/index.ts +6 -6
- package/src/backoffice/types/static/pricing.types.ts +16 -16
- package/src/backoffice/types/static/procedure-family.types.ts +14 -14
- package/src/backoffice/types/static/treatment-benefit.types.ts +22 -22
- package/src/backoffice/types/subcategory.types.ts +34 -34
- package/src/backoffice/types/technology.types.ts +163 -163
- package/src/backoffice/validations/index.ts +1 -1
- package/src/backoffice/validations/schemas.ts +164 -164
- package/src/config/__mocks__/firebase.ts +99 -99
- package/src/config/firebase.ts +78 -78
- package/src/config/index.ts +9 -9
- package/src/errors/auth.error.ts +6 -6
- package/src/errors/auth.errors.ts +200 -200
- package/src/errors/clinic.errors.ts +32 -32
- package/src/errors/firebase.errors.ts +47 -47
- package/src/errors/user.errors.ts +99 -99
- package/src/index.backup.ts +407 -407
- package/src/index.ts +6 -6
- package/src/locales/en.ts +31 -31
- package/src/recommender/admin/index.ts +1 -1
- package/src/recommender/admin/services/recommender.service.admin.ts +5 -5
- package/src/recommender/front/index.ts +1 -1
- package/src/recommender/front/services/onboarding.service.ts +5 -5
- package/src/recommender/front/services/recommender.service.ts +3 -3
- package/src/recommender/index.ts +1 -1
- package/src/services/PATIENTAUTH.MD +197 -197
- package/src/services/README.md +106 -106
- package/src/services/__tests__/auth/auth.mock.test.ts +17 -17
- package/src/services/__tests__/auth/auth.setup.ts +293 -293
- package/src/services/__tests__/auth.service.test.ts +346 -346
- package/src/services/__tests__/base.service.test.ts +77 -77
- package/src/services/__tests__/user.service.test.ts +528 -528
- package/src/services/appointment/README.md +17 -17
- package/src/services/appointment/appointment.service.ts +2505 -2505
- package/src/services/appointment/index.ts +1 -1
- package/src/services/appointment/utils/appointment.utils.ts +552 -552
- package/src/services/appointment/utils/extended-procedure.utils.ts +314 -314
- package/src/services/appointment/utils/form-initialization.utils.ts +225 -225
- package/src/services/appointment/utils/recommended-procedure.utils.ts +195 -195
- package/src/services/appointment/utils/zone-management.utils.ts +353 -353
- package/src/services/appointment/utils/zone-photo.utils.ts +152 -152
- package/src/services/auth/auth.service.ts +989 -989
- package/src/services/auth/auth.v2.service.ts +961 -961
- package/src/services/auth/index.ts +7 -7
- package/src/services/auth/utils/error.utils.ts +90 -90
- package/src/services/auth/utils/firebase.utils.ts +49 -49
- package/src/services/auth/utils/index.ts +21 -21
- package/src/services/auth/utils/practitioner.utils.ts +125 -125
- package/src/services/base.service.ts +41 -41
- package/src/services/calendar/calendar.service.ts +1077 -1077
- package/src/services/calendar/calendar.v2.service.ts +1683 -1683
- package/src/services/calendar/calendar.v3.service.ts +313 -313
- package/src/services/calendar/externalCalendar.service.ts +178 -178
- package/src/services/calendar/index.ts +5 -5
- package/src/services/calendar/synced-calendars.service.ts +743 -743
- package/src/services/calendar/utils/appointment.utils.ts +265 -265
- package/src/services/calendar/utils/calendar-event.utils.ts +646 -646
- package/src/services/calendar/utils/clinic.utils.ts +237 -237
- package/src/services/calendar/utils/docs.utils.ts +157 -157
- package/src/services/calendar/utils/google-calendar.utils.ts +697 -697
- package/src/services/calendar/utils/index.ts +8 -8
- package/src/services/calendar/utils/patient.utils.ts +198 -198
- package/src/services/calendar/utils/practitioner.utils.ts +221 -221
- package/src/services/calendar/utils/synced-calendar.utils.ts +472 -472
- package/src/services/clinic/README.md +204 -204
- package/src/services/clinic/__tests__/clinic-admin.service.test.ts +287 -287
- package/src/services/clinic/__tests__/clinic-group.service.test.ts +352 -352
- package/src/services/clinic/__tests__/clinic.service.test.ts +354 -354
- package/src/services/clinic/billing-transactions.service.ts +217 -217
- package/src/services/clinic/clinic-admin.service.ts +202 -202
- package/src/services/clinic/clinic-group.service.ts +310 -310
- package/src/services/clinic/clinic.service.ts +708 -708
- package/src/services/clinic/index.ts +5 -5
- package/src/services/clinic/practitioner-invite.service.ts +519 -519
- package/src/services/clinic/utils/admin.utils.ts +551 -551
- package/src/services/clinic/utils/clinic-group.utils.ts +646 -646
- package/src/services/clinic/utils/clinic.utils.ts +949 -949
- package/src/services/clinic/utils/filter.utils.d.ts +23 -23
- package/src/services/clinic/utils/filter.utils.ts +446 -446
- package/src/services/clinic/utils/index.ts +11 -11
- package/src/services/clinic/utils/photos.utils.ts +188 -188
- package/src/services/clinic/utils/search.utils.ts +84 -84
- package/src/services/clinic/utils/tag.utils.ts +124 -124
- package/src/services/documentation-templates/documentation-template.service.ts +537 -537
- package/src/services/documentation-templates/filled-document.service.ts +587 -587
- package/src/services/documentation-templates/index.ts +2 -2
- package/src/services/index.ts +13 -13
- package/src/services/media/index.ts +1 -1
- package/src/services/media/media.service.ts +418 -418
- package/src/services/notifications/__tests__/notification.service.test.ts +242 -242
- package/src/services/notifications/index.ts +1 -1
- package/src/services/notifications/notification.service.ts +215 -215
- package/src/services/patient/README.md +48 -48
- package/src/services/patient/To-Do.md +43 -43
- package/src/services/patient/__tests__/patient.service.test.ts +294 -294
- package/src/services/patient/index.ts +2 -2
- package/src/services/patient/patient.service.ts +883 -883
- package/src/services/patient/patientRequirements.service.ts +285 -285
- package/src/services/patient/utils/aesthetic-analysis.utils.ts +176 -176
- package/src/services/patient/utils/clinic.utils.ts +80 -80
- package/src/services/patient/utils/docs.utils.ts +142 -142
- package/src/services/patient/utils/index.ts +9 -9
- package/src/services/patient/utils/location.utils.ts +126 -126
- package/src/services/patient/utils/medical-stuff.utils.ts +143 -143
- package/src/services/patient/utils/medical.utils.ts +458 -458
- package/src/services/patient/utils/practitioner.utils.ts +260 -260
- package/src/services/patient/utils/profile.utils.ts +510 -510
- package/src/services/patient/utils/sensitive.utils.ts +260 -260
- package/src/services/patient/utils/token.utils.ts +211 -211
- package/src/services/practitioner/README.md +145 -145
- package/src/services/practitioner/index.ts +1 -1
- package/src/services/practitioner/practitioner.service.ts +1742 -1742
- package/src/services/procedure/README.md +163 -163
- package/src/services/procedure/index.ts +1 -1
- package/src/services/procedure/procedure.service.ts +1715 -1715
- package/src/services/reviews/index.ts +1 -1
- package/src/services/reviews/reviews.service.ts +683 -636
- package/src/services/user/index.ts +1 -1
- package/src/services/user/user.service.ts +489 -489
- package/src/services/user/user.v2.service.ts +466 -466
- package/src/types/appointment/index.ts +480 -480
- package/src/types/calendar/index.ts +258 -258
- package/src/types/calendar/synced-calendar.types.ts +66 -66
- package/src/types/clinic/index.ts +489 -489
- package/src/types/clinic/practitioner-invite.types.ts +91 -91
- package/src/types/clinic/preferences.types.ts +159 -159
- package/src/types/clinic/to-do +3 -3
- package/src/types/documentation-templates/index.ts +308 -308
- package/src/types/index.ts +44 -44
- package/src/types/notifications/README.md +77 -77
- package/src/types/notifications/index.ts +265 -265
- package/src/types/patient/aesthetic-analysis.types.ts +66 -66
- package/src/types/patient/allergies.ts +58 -58
- package/src/types/patient/index.ts +275 -275
- package/src/types/patient/medical-info.types.ts +152 -152
- package/src/types/patient/patient-requirements.ts +92 -92
- package/src/types/patient/token.types.ts +61 -61
- package/src/types/practitioner/index.ts +206 -206
- package/src/types/procedure/index.ts +181 -181
- package/src/types/profile/index.ts +39 -39
- package/src/types/reviews/index.ts +132 -130
- package/src/types/tz-lookup.d.ts +4 -4
- package/src/types/user/index.ts +38 -38
- package/src/utils/TIMESTAMPS.md +176 -176
- package/src/utils/TimestampUtils.ts +241 -241
- package/src/utils/index.ts +1 -1
- package/src/validations/appointment.schema.ts +574 -574
- package/src/validations/calendar.schema.ts +225 -225
- package/src/validations/clinic.schema.ts +493 -493
- package/src/validations/common.schema.ts +25 -25
- package/src/validations/documentation-templates/index.ts +1 -1
- package/src/validations/documentation-templates/template.schema.ts +220 -220
- package/src/validations/documentation-templates.schema.ts +10 -10
- package/src/validations/index.ts +20 -20
- package/src/validations/media.schema.ts +10 -10
- package/src/validations/notification.schema.ts +90 -90
- package/src/validations/patient/aesthetic-analysis.schema.ts +55 -55
- package/src/validations/patient/medical-info.schema.ts +125 -125
- package/src/validations/patient/patient-requirements.schema.ts +84 -84
- package/src/validations/patient/token.schema.ts +29 -29
- package/src/validations/patient.schema.ts +217 -217
- package/src/validations/practitioner.schema.ts +222 -222
- package/src/validations/procedure-product.schema.ts +41 -41
- package/src/validations/procedure.schema.ts +124 -124
- package/src/validations/profile-info.schema.ts +41 -41
- package/src/validations/reviews.schema.ts +195 -189
- package/src/validations/schemas.ts +104 -104
- package/src/validations/shared.schema.ts +78 -78
|
@@ -1,646 +1,646 @@
|
|
|
1
|
-
import {
|
|
2
|
-
collection,
|
|
3
|
-
doc,
|
|
4
|
-
getDoc,
|
|
5
|
-
getDocs,
|
|
6
|
-
setDoc,
|
|
7
|
-
updateDoc,
|
|
8
|
-
deleteDoc,
|
|
9
|
-
query,
|
|
10
|
-
where,
|
|
11
|
-
orderBy,
|
|
12
|
-
Timestamp,
|
|
13
|
-
serverTimestamp,
|
|
14
|
-
Firestore,
|
|
15
|
-
DocumentReference,
|
|
16
|
-
QueryConstraint,
|
|
17
|
-
} from "firebase/firestore";
|
|
18
|
-
import {
|
|
19
|
-
CalendarEvent,
|
|
20
|
-
CalendarEventStatus,
|
|
21
|
-
CalendarEventTime,
|
|
22
|
-
CalendarEventType,
|
|
23
|
-
CalendarSyncStatus,
|
|
24
|
-
CreateCalendarEventData,
|
|
25
|
-
UpdateCalendarEventData,
|
|
26
|
-
CALENDAR_COLLECTION,
|
|
27
|
-
SearchCalendarEventsParams,
|
|
28
|
-
SearchLocationEnum,
|
|
29
|
-
} from "../../../types/calendar";
|
|
30
|
-
import { PRACTITIONERS_COLLECTION } from "../../../types/practitioner";
|
|
31
|
-
import { PATIENTS_COLLECTION } from "../../../types/patient";
|
|
32
|
-
import { CLINICS_COLLECTION } from "../../../types/clinic";
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Creates a calendar event for a practitioner
|
|
36
|
-
* @param db - Firestore instance
|
|
37
|
-
* @param practitionerId - ID of the practitioner
|
|
38
|
-
* @param eventData - Calendar event data
|
|
39
|
-
* @param generateId - Function to generate a unique ID
|
|
40
|
-
* @returns Created calendar event
|
|
41
|
-
*/
|
|
42
|
-
export async function createPractitionerCalendarEventUtil(
|
|
43
|
-
db: Firestore,
|
|
44
|
-
practitionerId: string,
|
|
45
|
-
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">,
|
|
46
|
-
generateId: () => string
|
|
47
|
-
): Promise<CalendarEvent> {
|
|
48
|
-
// TODO: Add validation for event data
|
|
49
|
-
// - Check if practitioner exists
|
|
50
|
-
// - Validate event time (start < end)
|
|
51
|
-
// - Check for overlapping events
|
|
52
|
-
// - Validate required fields
|
|
53
|
-
|
|
54
|
-
const eventId = generateId();
|
|
55
|
-
const eventRef = doc(
|
|
56
|
-
db,
|
|
57
|
-
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const newEvent: CreateCalendarEventData = {
|
|
61
|
-
id: eventId,
|
|
62
|
-
...eventData,
|
|
63
|
-
createdAt: serverTimestamp(),
|
|
64
|
-
updatedAt: serverTimestamp(),
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
await setDoc(eventRef, newEvent);
|
|
68
|
-
|
|
69
|
-
// Convert server timestamp to Timestamp for return value
|
|
70
|
-
return {
|
|
71
|
-
...newEvent,
|
|
72
|
-
createdAt: Timestamp.now(),
|
|
73
|
-
updatedAt: Timestamp.now(),
|
|
74
|
-
} as CalendarEvent;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Creates a calendar event for a patient
|
|
79
|
-
* @param db - Firestore instance
|
|
80
|
-
* @param patientId - ID of the patient
|
|
81
|
-
* @param eventData - Calendar event data
|
|
82
|
-
* @param generateId - Function to generate a unique ID
|
|
83
|
-
* @returns Created calendar event
|
|
84
|
-
*/
|
|
85
|
-
export async function createPatientCalendarEventUtil(
|
|
86
|
-
db: Firestore,
|
|
87
|
-
patientId: string,
|
|
88
|
-
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">,
|
|
89
|
-
generateId: () => string
|
|
90
|
-
): Promise<CalendarEvent> {
|
|
91
|
-
// TODO: Add validation for event data
|
|
92
|
-
// - Check if patient exists
|
|
93
|
-
// - Validate event time (start < end)
|
|
94
|
-
// - Check for overlapping events
|
|
95
|
-
// - Validate required fields
|
|
96
|
-
|
|
97
|
-
const eventId = generateId();
|
|
98
|
-
const eventRef = doc(
|
|
99
|
-
db,
|
|
100
|
-
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const newEvent: CreateCalendarEventData = {
|
|
104
|
-
id: eventId,
|
|
105
|
-
...eventData,
|
|
106
|
-
createdAt: serverTimestamp(),
|
|
107
|
-
updatedAt: serverTimestamp(),
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
await setDoc(eventRef, newEvent);
|
|
111
|
-
|
|
112
|
-
// Convert server timestamp to Timestamp for return value
|
|
113
|
-
return {
|
|
114
|
-
...newEvent,
|
|
115
|
-
createdAt: Timestamp.now(),
|
|
116
|
-
updatedAt: Timestamp.now(),
|
|
117
|
-
} as CalendarEvent;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Creates a calendar event for a clinic
|
|
122
|
-
* @param db - Firestore instance
|
|
123
|
-
* @param clinicId - ID of the clinic
|
|
124
|
-
* @param eventData - Calendar event data
|
|
125
|
-
* @param generateId - Function to generate a unique ID
|
|
126
|
-
* @returns Created calendar event
|
|
127
|
-
*/
|
|
128
|
-
export async function createClinicCalendarEventUtil(
|
|
129
|
-
db: Firestore,
|
|
130
|
-
clinicId: string,
|
|
131
|
-
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">,
|
|
132
|
-
generateId: () => string
|
|
133
|
-
): Promise<CalendarEvent> {
|
|
134
|
-
// TODO: Add validation for event data
|
|
135
|
-
// - Check if clinic exists
|
|
136
|
-
// - Validate event time (start < end)
|
|
137
|
-
// - Check for overlapping events
|
|
138
|
-
// - Validate required fields
|
|
139
|
-
|
|
140
|
-
const eventId = generateId();
|
|
141
|
-
const eventRef = doc(
|
|
142
|
-
db,
|
|
143
|
-
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const newEvent: CreateCalendarEventData = {
|
|
147
|
-
id: eventId,
|
|
148
|
-
...eventData,
|
|
149
|
-
createdAt: serverTimestamp(),
|
|
150
|
-
updatedAt: serverTimestamp(),
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
await setDoc(eventRef, newEvent);
|
|
154
|
-
|
|
155
|
-
// Convert server timestamp to Timestamp for return value
|
|
156
|
-
return {
|
|
157
|
-
...newEvent,
|
|
158
|
-
createdAt: Timestamp.now(),
|
|
159
|
-
updatedAt: Timestamp.now(),
|
|
160
|
-
} as CalendarEvent;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Gets a calendar event for a practitioner
|
|
165
|
-
* @param db - Firestore instance
|
|
166
|
-
* @param practitionerId - ID of the practitioner
|
|
167
|
-
* @param eventId - ID of the event
|
|
168
|
-
* @returns Calendar event or null if not found
|
|
169
|
-
*/
|
|
170
|
-
export async function getPractitionerCalendarEventUtil(
|
|
171
|
-
db: Firestore,
|
|
172
|
-
practitionerId: string,
|
|
173
|
-
eventId: string
|
|
174
|
-
): Promise<CalendarEvent | null> {
|
|
175
|
-
const eventRef = doc(
|
|
176
|
-
db,
|
|
177
|
-
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
178
|
-
);
|
|
179
|
-
const eventDoc = await getDoc(eventRef);
|
|
180
|
-
|
|
181
|
-
if (!eventDoc.exists()) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return eventDoc.data() as CalendarEvent;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Gets a calendar event for a patient
|
|
190
|
-
* @param db - Firestore instance
|
|
191
|
-
* @param patientId - ID of the patient
|
|
192
|
-
* @param eventId - ID of the event
|
|
193
|
-
* @returns Calendar event or null if not found
|
|
194
|
-
*/
|
|
195
|
-
export async function getPatientCalendarEventUtil(
|
|
196
|
-
db: Firestore,
|
|
197
|
-
patientId: string,
|
|
198
|
-
eventId: string
|
|
199
|
-
): Promise<CalendarEvent | null> {
|
|
200
|
-
const eventRef = doc(
|
|
201
|
-
db,
|
|
202
|
-
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
203
|
-
);
|
|
204
|
-
const eventDoc = await getDoc(eventRef);
|
|
205
|
-
|
|
206
|
-
if (!eventDoc.exists()) {
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return eventDoc.data() as CalendarEvent;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Gets a calendar event for a clinic
|
|
215
|
-
* @param db - Firestore instance
|
|
216
|
-
* @param clinicId - ID of the clinic
|
|
217
|
-
* @param eventId - ID of the event
|
|
218
|
-
* @returns Calendar event or null if not found
|
|
219
|
-
*/
|
|
220
|
-
export async function getClinicCalendarEventUtil(
|
|
221
|
-
db: Firestore,
|
|
222
|
-
clinicId: string,
|
|
223
|
-
eventId: string
|
|
224
|
-
): Promise<CalendarEvent | null> {
|
|
225
|
-
const eventRef = doc(
|
|
226
|
-
db,
|
|
227
|
-
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
228
|
-
);
|
|
229
|
-
const eventDoc = await getDoc(eventRef);
|
|
230
|
-
|
|
231
|
-
if (!eventDoc.exists()) {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return eventDoc.data() as CalendarEvent;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Gets calendar events for a practitioner within a date range
|
|
240
|
-
* @param db - Firestore instance
|
|
241
|
-
* @param practitionerId - ID of the practitioner
|
|
242
|
-
* @param startDate - Start date of the range
|
|
243
|
-
* @param endDate - End date of the range
|
|
244
|
-
* @returns Array of calendar events
|
|
245
|
-
*/
|
|
246
|
-
export async function getPractitionerCalendarEventsUtil(
|
|
247
|
-
db: Firestore,
|
|
248
|
-
practitionerId: string,
|
|
249
|
-
startDate: Date,
|
|
250
|
-
endDate: Date
|
|
251
|
-
): Promise<CalendarEvent[]> {
|
|
252
|
-
const startTimestamp = Timestamp.fromDate(startDate);
|
|
253
|
-
const endTimestamp = Timestamp.fromDate(endDate);
|
|
254
|
-
|
|
255
|
-
const eventsRef = collection(
|
|
256
|
-
db,
|
|
257
|
-
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}`
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
const q = query(
|
|
261
|
-
eventsRef,
|
|
262
|
-
where("eventTime.start", ">=", startTimestamp),
|
|
263
|
-
where("eventTime.end", "<=", endTimestamp),
|
|
264
|
-
orderBy("eventTime.start", "asc")
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
const querySnapshot = await getDocs(q);
|
|
268
|
-
return querySnapshot.docs.map((doc) => doc.data() as CalendarEvent);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Gets calendar events for a patient within a date range
|
|
273
|
-
* @param db - Firestore instance
|
|
274
|
-
* @param patientId - ID of the patient
|
|
275
|
-
* @param startDate - Start date of the range
|
|
276
|
-
* @param endDate - End date of the range
|
|
277
|
-
* @returns Array of calendar events
|
|
278
|
-
*/
|
|
279
|
-
export async function getPatientCalendarEventsUtil(
|
|
280
|
-
db: Firestore,
|
|
281
|
-
patientId: string,
|
|
282
|
-
startDate: Date,
|
|
283
|
-
endDate: Date
|
|
284
|
-
): Promise<CalendarEvent[]> {
|
|
285
|
-
const startTimestamp = Timestamp.fromDate(startDate);
|
|
286
|
-
const endTimestamp = Timestamp.fromDate(endDate);
|
|
287
|
-
|
|
288
|
-
const eventsRef = collection(
|
|
289
|
-
db,
|
|
290
|
-
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}`
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
const q = query(
|
|
294
|
-
eventsRef,
|
|
295
|
-
where("eventTime.start", ">=", startTimestamp),
|
|
296
|
-
where("eventTime.end", "<=", endTimestamp),
|
|
297
|
-
orderBy("eventTime.start", "asc")
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
const querySnapshot = await getDocs(q);
|
|
301
|
-
return querySnapshot.docs.map((doc) => doc.data() as CalendarEvent);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Gets calendar events for a clinic within a date range
|
|
306
|
-
* @param db - Firestore instance
|
|
307
|
-
* @param clinicId - ID of the clinic
|
|
308
|
-
* @param startDate - Start date of the range
|
|
309
|
-
* @param endDate - End date of the range
|
|
310
|
-
* @returns Array of calendar events
|
|
311
|
-
*/
|
|
312
|
-
export async function getClinicCalendarEventsUtil(
|
|
313
|
-
db: Firestore,
|
|
314
|
-
clinicId: string,
|
|
315
|
-
startDate: Date,
|
|
316
|
-
endDate: Date
|
|
317
|
-
): Promise<CalendarEvent[]> {
|
|
318
|
-
const startTimestamp = Timestamp.fromDate(startDate);
|
|
319
|
-
const endTimestamp = Timestamp.fromDate(endDate);
|
|
320
|
-
|
|
321
|
-
const eventsRef = collection(
|
|
322
|
-
db,
|
|
323
|
-
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}`
|
|
324
|
-
);
|
|
325
|
-
|
|
326
|
-
const q = query(
|
|
327
|
-
eventsRef,
|
|
328
|
-
where("eventTime.start", ">=", startTimestamp),
|
|
329
|
-
where("eventTime.end", "<=", endTimestamp),
|
|
330
|
-
orderBy("eventTime.start", "asc")
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
const querySnapshot = await getDocs(q);
|
|
334
|
-
return querySnapshot.docs.map((doc) => doc.data() as CalendarEvent);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Updates a calendar event for a practitioner
|
|
339
|
-
* @param db - Firestore instance
|
|
340
|
-
* @param practitionerId - ID of the practitioner
|
|
341
|
-
* @param eventId - ID of the event
|
|
342
|
-
* @param updateData - Data to update
|
|
343
|
-
* @returns Updated calendar event
|
|
344
|
-
*/
|
|
345
|
-
export async function updatePractitionerCalendarEventUtil(
|
|
346
|
-
db: Firestore,
|
|
347
|
-
practitionerId: string,
|
|
348
|
-
eventId: string,
|
|
349
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
350
|
-
): Promise<CalendarEvent> {
|
|
351
|
-
// TODO: Add validation for update data
|
|
352
|
-
// - Check if event exists
|
|
353
|
-
// - Validate event time (start < end)
|
|
354
|
-
// - Check for overlapping events
|
|
355
|
-
|
|
356
|
-
const eventRef = doc(
|
|
357
|
-
db,
|
|
358
|
-
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
const updates: UpdateCalendarEventData = {
|
|
362
|
-
...updateData,
|
|
363
|
-
updatedAt: serverTimestamp(),
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
await updateDoc(eventRef, updates as any);
|
|
367
|
-
|
|
368
|
-
// Get the updated document
|
|
369
|
-
const updatedDoc = await getDoc(eventRef);
|
|
370
|
-
|
|
371
|
-
if (!updatedDoc.exists()) {
|
|
372
|
-
throw new Error("Event not found after update");
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return updatedDoc.data() as CalendarEvent;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Updates a calendar event for a patient
|
|
380
|
-
* @param db - Firestore instance
|
|
381
|
-
* @param patientId - ID of the patient
|
|
382
|
-
* @param eventId - ID of the event
|
|
383
|
-
* @param updateData - Data to update
|
|
384
|
-
* @returns Updated calendar event
|
|
385
|
-
*/
|
|
386
|
-
export async function updatePatientCalendarEventUtil(
|
|
387
|
-
db: Firestore,
|
|
388
|
-
patientId: string,
|
|
389
|
-
eventId: string,
|
|
390
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
391
|
-
): Promise<CalendarEvent> {
|
|
392
|
-
// TODO: Add validation for update data
|
|
393
|
-
// - Check if event exists
|
|
394
|
-
// - Validate event time (start < end)
|
|
395
|
-
// - Check for overlapping events
|
|
396
|
-
|
|
397
|
-
const eventRef = doc(
|
|
398
|
-
db,
|
|
399
|
-
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
const updates: UpdateCalendarEventData = {
|
|
403
|
-
...updateData,
|
|
404
|
-
updatedAt: serverTimestamp(),
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
await updateDoc(eventRef, updates as any);
|
|
408
|
-
|
|
409
|
-
// Get the updated document
|
|
410
|
-
const updatedDoc = await getDoc(eventRef);
|
|
411
|
-
|
|
412
|
-
if (!updatedDoc.exists()) {
|
|
413
|
-
throw new Error("Event not found after update");
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return updatedDoc.data() as CalendarEvent;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Updates a calendar event for a clinic
|
|
421
|
-
* @param db - Firestore instance
|
|
422
|
-
* @param clinicId - ID of the clinic
|
|
423
|
-
* @param eventId - ID of the event
|
|
424
|
-
* @param updateData - Data to update
|
|
425
|
-
* @returns Updated calendar event
|
|
426
|
-
*/
|
|
427
|
-
export async function updateClinicCalendarEventUtil(
|
|
428
|
-
db: Firestore,
|
|
429
|
-
clinicId: string,
|
|
430
|
-
eventId: string,
|
|
431
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
432
|
-
): Promise<CalendarEvent> {
|
|
433
|
-
// TODO: Add validation for update data
|
|
434
|
-
// - Check if event exists
|
|
435
|
-
// - Validate event time (start < end)
|
|
436
|
-
// - Check for overlapping events
|
|
437
|
-
|
|
438
|
-
const eventRef = doc(
|
|
439
|
-
db,
|
|
440
|
-
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
const updates: UpdateCalendarEventData = {
|
|
444
|
-
...updateData,
|
|
445
|
-
updatedAt: serverTimestamp(),
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
await updateDoc(eventRef, updates as any);
|
|
449
|
-
|
|
450
|
-
// Get the updated document
|
|
451
|
-
const updatedDoc = await getDoc(eventRef);
|
|
452
|
-
|
|
453
|
-
if (!updatedDoc.exists()) {
|
|
454
|
-
throw new Error("Event not found after update");
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return updatedDoc.data() as CalendarEvent;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Deletes a calendar event for a practitioner
|
|
462
|
-
* @param db - Firestore instance
|
|
463
|
-
* @param practitionerId - ID of the practitioner
|
|
464
|
-
* @param eventId - ID of the event
|
|
465
|
-
*/
|
|
466
|
-
export async function deletePractitionerCalendarEventUtil(
|
|
467
|
-
db: Firestore,
|
|
468
|
-
practitionerId: string,
|
|
469
|
-
eventId: string
|
|
470
|
-
): Promise<void> {
|
|
471
|
-
const eventRef = doc(
|
|
472
|
-
db,
|
|
473
|
-
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
474
|
-
);
|
|
475
|
-
await deleteDoc(eventRef);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Deletes a calendar event for a patient
|
|
480
|
-
* @param db - Firestore instance
|
|
481
|
-
* @param patientId - ID of the patient
|
|
482
|
-
* @param eventId - ID of the event
|
|
483
|
-
*/
|
|
484
|
-
export async function deletePatientCalendarEventUtil(
|
|
485
|
-
db: Firestore,
|
|
486
|
-
patientId: string,
|
|
487
|
-
eventId: string
|
|
488
|
-
): Promise<void> {
|
|
489
|
-
const eventRef = doc(
|
|
490
|
-
db,
|
|
491
|
-
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
492
|
-
);
|
|
493
|
-
await deleteDoc(eventRef);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Deletes a calendar event for a clinic
|
|
498
|
-
* @param db - Firestore instance
|
|
499
|
-
* @param clinicId - ID of the clinic
|
|
500
|
-
* @param eventId - ID of the event
|
|
501
|
-
*/
|
|
502
|
-
export async function deleteClinicCalendarEventUtil(
|
|
503
|
-
db: Firestore,
|
|
504
|
-
clinicId: string,
|
|
505
|
-
eventId: string
|
|
506
|
-
): Promise<void> {
|
|
507
|
-
const eventRef = doc(
|
|
508
|
-
db,
|
|
509
|
-
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
510
|
-
);
|
|
511
|
-
await deleteDoc(eventRef);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Searches for calendar events based on specified criteria
|
|
516
|
-
* @param db - Firestore instance
|
|
517
|
-
* @param params - Search parameters
|
|
518
|
-
* @param params.searchLocation - The primary location to search (practitioner, patient, or clinic)
|
|
519
|
-
* @param params.entityId - The ID of the entity (practitioner, patient, or clinic) to search within/for
|
|
520
|
-
* @param params.clinicId - Optional clinic ID to filter by
|
|
521
|
-
* @param params.practitionerId - Optional practitioner ID to filter by
|
|
522
|
-
* @param params.patientId - Optional patient ID to filter by
|
|
523
|
-
* @param params.procedureId - Optional procedure ID to filter by
|
|
524
|
-
* @param params.dateRange - Optional date range to filter by (event start time)
|
|
525
|
-
* @param params.eventStatus - Optional event status to filter by
|
|
526
|
-
* @param params.eventType - Optional event type to filter by
|
|
527
|
-
* @returns Promise resolving to an array of matching calendar events
|
|
528
|
-
*/
|
|
529
|
-
export async function searchCalendarEventsUtil(
|
|
530
|
-
db: Firestore,
|
|
531
|
-
params: SearchCalendarEventsParams
|
|
532
|
-
): Promise<CalendarEvent[]> {
|
|
533
|
-
const { searchLocation, entityId, ...filters } = params;
|
|
534
|
-
|
|
535
|
-
let baseCollectionPath: string;
|
|
536
|
-
const constraints: QueryConstraint[] = [];
|
|
537
|
-
|
|
538
|
-
// Determine the base collection and apply initial filter based on searchLocation
|
|
539
|
-
switch (searchLocation) {
|
|
540
|
-
case SearchLocationEnum.PRACTITIONER:
|
|
541
|
-
if (!entityId) {
|
|
542
|
-
throw new Error(
|
|
543
|
-
"Practitioner ID (entityId) is required when searching practitioner calendar."
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
baseCollectionPath = `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
547
|
-
// If practitionerId filter is provided, it must match the entityId for this search location
|
|
548
|
-
if (filters.practitionerId && filters.practitionerId !== entityId) {
|
|
549
|
-
console.warn(
|
|
550
|
-
`Provided practitionerId filter (${filters.practitionerId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
551
|
-
);
|
|
552
|
-
return [];
|
|
553
|
-
}
|
|
554
|
-
// Ensure we don't add a redundant filter if the caller also specified it
|
|
555
|
-
filters.practitionerId = undefined;
|
|
556
|
-
break;
|
|
557
|
-
|
|
558
|
-
case SearchLocationEnum.PATIENT:
|
|
559
|
-
if (!entityId) {
|
|
560
|
-
throw new Error(
|
|
561
|
-
"Patient ID (entityId) is required when searching patient calendar."
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
baseCollectionPath = `${PATIENTS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
565
|
-
// If patientId filter is provided, it must match the entityId for this search location
|
|
566
|
-
if (filters.patientId && filters.patientId !== entityId) {
|
|
567
|
-
console.warn(
|
|
568
|
-
`Provided patientId filter (${filters.patientId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
569
|
-
);
|
|
570
|
-
return [];
|
|
571
|
-
}
|
|
572
|
-
// Ensure we don't add a redundant filter if the caller also specified it
|
|
573
|
-
filters.patientId = undefined;
|
|
574
|
-
break;
|
|
575
|
-
|
|
576
|
-
case SearchLocationEnum.CLINIC:
|
|
577
|
-
if (!entityId) {
|
|
578
|
-
throw new Error(
|
|
579
|
-
"Clinic ID (entityId) is required when searching clinic-related events."
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
// Search the clinic's calendar collection
|
|
583
|
-
baseCollectionPath = `${CLINICS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
584
|
-
constraints.push(where("clinicBranchId", "==", entityId));
|
|
585
|
-
// If clinicId filter is provided, it must match the entityId for this search location
|
|
586
|
-
if (filters.clinicId && filters.clinicId !== entityId) {
|
|
587
|
-
console.warn(
|
|
588
|
-
`Provided clinicId filter (${filters.clinicId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
589
|
-
);
|
|
590
|
-
return [];
|
|
591
|
-
}
|
|
592
|
-
// Ensure we don't add a redundant filter if the caller also specified it
|
|
593
|
-
filters.clinicId = undefined; // Already handled by the base query
|
|
594
|
-
break;
|
|
595
|
-
|
|
596
|
-
default:
|
|
597
|
-
throw new Error(`Invalid search location: ${searchLocation}`);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
const collectionRef = collection(db, baseCollectionPath);
|
|
601
|
-
|
|
602
|
-
// Apply optional filters
|
|
603
|
-
if (filters.clinicId) {
|
|
604
|
-
constraints.push(where("clinicBranchId", "==", filters.clinicId));
|
|
605
|
-
}
|
|
606
|
-
if (filters.practitionerId) {
|
|
607
|
-
constraints.push(
|
|
608
|
-
where("practitionerProfileId", "==", filters.practitionerId)
|
|
609
|
-
);
|
|
610
|
-
}
|
|
611
|
-
if (filters.patientId) {
|
|
612
|
-
constraints.push(where("patientProfileId", "==", filters.patientId));
|
|
613
|
-
}
|
|
614
|
-
if (filters.procedureId) {
|
|
615
|
-
constraints.push(where("procedureId", "==", filters.procedureId));
|
|
616
|
-
}
|
|
617
|
-
if (filters.eventStatus) {
|
|
618
|
-
constraints.push(where("status", "==", filters.eventStatus));
|
|
619
|
-
}
|
|
620
|
-
if (filters.eventType) {
|
|
621
|
-
constraints.push(where("eventType", "==", filters.eventType));
|
|
622
|
-
}
|
|
623
|
-
if (filters.dateRange) {
|
|
624
|
-
// Firestore requires range filters on the same field
|
|
625
|
-
constraints.push(where("eventTime.start", ">=", filters.dateRange.start));
|
|
626
|
-
constraints.push(where("eventTime.start", "<=", filters.dateRange.end));
|
|
627
|
-
// Note: You might need to order by eventTime.start for range filters to work efficiently
|
|
628
|
-
// constraints.push(orderBy("eventTime.start"));
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Build and execute the query
|
|
632
|
-
try {
|
|
633
|
-
const finalQuery = query(collectionRef, ...constraints);
|
|
634
|
-
const querySnapshot = await getDocs(finalQuery);
|
|
635
|
-
|
|
636
|
-
const events = querySnapshot.docs.map(
|
|
637
|
-
(doc) => ({ id: doc.id, ...doc.data() } as CalendarEvent)
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
return events;
|
|
641
|
-
} catch (error) {
|
|
642
|
-
console.error("Error searching calendar events:", error);
|
|
643
|
-
// Depending on requirements, you might want to return an empty array or re-throw
|
|
644
|
-
return [];
|
|
645
|
-
}
|
|
646
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
collection,
|
|
3
|
+
doc,
|
|
4
|
+
getDoc,
|
|
5
|
+
getDocs,
|
|
6
|
+
setDoc,
|
|
7
|
+
updateDoc,
|
|
8
|
+
deleteDoc,
|
|
9
|
+
query,
|
|
10
|
+
where,
|
|
11
|
+
orderBy,
|
|
12
|
+
Timestamp,
|
|
13
|
+
serverTimestamp,
|
|
14
|
+
Firestore,
|
|
15
|
+
DocumentReference,
|
|
16
|
+
QueryConstraint,
|
|
17
|
+
} from "firebase/firestore";
|
|
18
|
+
import {
|
|
19
|
+
CalendarEvent,
|
|
20
|
+
CalendarEventStatus,
|
|
21
|
+
CalendarEventTime,
|
|
22
|
+
CalendarEventType,
|
|
23
|
+
CalendarSyncStatus,
|
|
24
|
+
CreateCalendarEventData,
|
|
25
|
+
UpdateCalendarEventData,
|
|
26
|
+
CALENDAR_COLLECTION,
|
|
27
|
+
SearchCalendarEventsParams,
|
|
28
|
+
SearchLocationEnum,
|
|
29
|
+
} from "../../../types/calendar";
|
|
30
|
+
import { PRACTITIONERS_COLLECTION } from "../../../types/practitioner";
|
|
31
|
+
import { PATIENTS_COLLECTION } from "../../../types/patient";
|
|
32
|
+
import { CLINICS_COLLECTION } from "../../../types/clinic";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a calendar event for a practitioner
|
|
36
|
+
* @param db - Firestore instance
|
|
37
|
+
* @param practitionerId - ID of the practitioner
|
|
38
|
+
* @param eventData - Calendar event data
|
|
39
|
+
* @param generateId - Function to generate a unique ID
|
|
40
|
+
* @returns Created calendar event
|
|
41
|
+
*/
|
|
42
|
+
export async function createPractitionerCalendarEventUtil(
|
|
43
|
+
db: Firestore,
|
|
44
|
+
practitionerId: string,
|
|
45
|
+
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">,
|
|
46
|
+
generateId: () => string
|
|
47
|
+
): Promise<CalendarEvent> {
|
|
48
|
+
// TODO: Add validation for event data
|
|
49
|
+
// - Check if practitioner exists
|
|
50
|
+
// - Validate event time (start < end)
|
|
51
|
+
// - Check for overlapping events
|
|
52
|
+
// - Validate required fields
|
|
53
|
+
|
|
54
|
+
const eventId = generateId();
|
|
55
|
+
const eventRef = doc(
|
|
56
|
+
db,
|
|
57
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const newEvent: CreateCalendarEventData = {
|
|
61
|
+
id: eventId,
|
|
62
|
+
...eventData,
|
|
63
|
+
createdAt: serverTimestamp(),
|
|
64
|
+
updatedAt: serverTimestamp(),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await setDoc(eventRef, newEvent);
|
|
68
|
+
|
|
69
|
+
// Convert server timestamp to Timestamp for return value
|
|
70
|
+
return {
|
|
71
|
+
...newEvent,
|
|
72
|
+
createdAt: Timestamp.now(),
|
|
73
|
+
updatedAt: Timestamp.now(),
|
|
74
|
+
} as CalendarEvent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a calendar event for a patient
|
|
79
|
+
* @param db - Firestore instance
|
|
80
|
+
* @param patientId - ID of the patient
|
|
81
|
+
* @param eventData - Calendar event data
|
|
82
|
+
* @param generateId - Function to generate a unique ID
|
|
83
|
+
* @returns Created calendar event
|
|
84
|
+
*/
|
|
85
|
+
export async function createPatientCalendarEventUtil(
|
|
86
|
+
db: Firestore,
|
|
87
|
+
patientId: string,
|
|
88
|
+
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">,
|
|
89
|
+
generateId: () => string
|
|
90
|
+
): Promise<CalendarEvent> {
|
|
91
|
+
// TODO: Add validation for event data
|
|
92
|
+
// - Check if patient exists
|
|
93
|
+
// - Validate event time (start < end)
|
|
94
|
+
// - Check for overlapping events
|
|
95
|
+
// - Validate required fields
|
|
96
|
+
|
|
97
|
+
const eventId = generateId();
|
|
98
|
+
const eventRef = doc(
|
|
99
|
+
db,
|
|
100
|
+
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const newEvent: CreateCalendarEventData = {
|
|
104
|
+
id: eventId,
|
|
105
|
+
...eventData,
|
|
106
|
+
createdAt: serverTimestamp(),
|
|
107
|
+
updatedAt: serverTimestamp(),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
await setDoc(eventRef, newEvent);
|
|
111
|
+
|
|
112
|
+
// Convert server timestamp to Timestamp for return value
|
|
113
|
+
return {
|
|
114
|
+
...newEvent,
|
|
115
|
+
createdAt: Timestamp.now(),
|
|
116
|
+
updatedAt: Timestamp.now(),
|
|
117
|
+
} as CalendarEvent;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Creates a calendar event for a clinic
|
|
122
|
+
* @param db - Firestore instance
|
|
123
|
+
* @param clinicId - ID of the clinic
|
|
124
|
+
* @param eventData - Calendar event data
|
|
125
|
+
* @param generateId - Function to generate a unique ID
|
|
126
|
+
* @returns Created calendar event
|
|
127
|
+
*/
|
|
128
|
+
export async function createClinicCalendarEventUtil(
|
|
129
|
+
db: Firestore,
|
|
130
|
+
clinicId: string,
|
|
131
|
+
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">,
|
|
132
|
+
generateId: () => string
|
|
133
|
+
): Promise<CalendarEvent> {
|
|
134
|
+
// TODO: Add validation for event data
|
|
135
|
+
// - Check if clinic exists
|
|
136
|
+
// - Validate event time (start < end)
|
|
137
|
+
// - Check for overlapping events
|
|
138
|
+
// - Validate required fields
|
|
139
|
+
|
|
140
|
+
const eventId = generateId();
|
|
141
|
+
const eventRef = doc(
|
|
142
|
+
db,
|
|
143
|
+
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const newEvent: CreateCalendarEventData = {
|
|
147
|
+
id: eventId,
|
|
148
|
+
...eventData,
|
|
149
|
+
createdAt: serverTimestamp(),
|
|
150
|
+
updatedAt: serverTimestamp(),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
await setDoc(eventRef, newEvent);
|
|
154
|
+
|
|
155
|
+
// Convert server timestamp to Timestamp for return value
|
|
156
|
+
return {
|
|
157
|
+
...newEvent,
|
|
158
|
+
createdAt: Timestamp.now(),
|
|
159
|
+
updatedAt: Timestamp.now(),
|
|
160
|
+
} as CalendarEvent;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Gets a calendar event for a practitioner
|
|
165
|
+
* @param db - Firestore instance
|
|
166
|
+
* @param practitionerId - ID of the practitioner
|
|
167
|
+
* @param eventId - ID of the event
|
|
168
|
+
* @returns Calendar event or null if not found
|
|
169
|
+
*/
|
|
170
|
+
export async function getPractitionerCalendarEventUtil(
|
|
171
|
+
db: Firestore,
|
|
172
|
+
practitionerId: string,
|
|
173
|
+
eventId: string
|
|
174
|
+
): Promise<CalendarEvent | null> {
|
|
175
|
+
const eventRef = doc(
|
|
176
|
+
db,
|
|
177
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
178
|
+
);
|
|
179
|
+
const eventDoc = await getDoc(eventRef);
|
|
180
|
+
|
|
181
|
+
if (!eventDoc.exists()) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return eventDoc.data() as CalendarEvent;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets a calendar event for a patient
|
|
190
|
+
* @param db - Firestore instance
|
|
191
|
+
* @param patientId - ID of the patient
|
|
192
|
+
* @param eventId - ID of the event
|
|
193
|
+
* @returns Calendar event or null if not found
|
|
194
|
+
*/
|
|
195
|
+
export async function getPatientCalendarEventUtil(
|
|
196
|
+
db: Firestore,
|
|
197
|
+
patientId: string,
|
|
198
|
+
eventId: string
|
|
199
|
+
): Promise<CalendarEvent | null> {
|
|
200
|
+
const eventRef = doc(
|
|
201
|
+
db,
|
|
202
|
+
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
203
|
+
);
|
|
204
|
+
const eventDoc = await getDoc(eventRef);
|
|
205
|
+
|
|
206
|
+
if (!eventDoc.exists()) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return eventDoc.data() as CalendarEvent;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Gets a calendar event for a clinic
|
|
215
|
+
* @param db - Firestore instance
|
|
216
|
+
* @param clinicId - ID of the clinic
|
|
217
|
+
* @param eventId - ID of the event
|
|
218
|
+
* @returns Calendar event or null if not found
|
|
219
|
+
*/
|
|
220
|
+
export async function getClinicCalendarEventUtil(
|
|
221
|
+
db: Firestore,
|
|
222
|
+
clinicId: string,
|
|
223
|
+
eventId: string
|
|
224
|
+
): Promise<CalendarEvent | null> {
|
|
225
|
+
const eventRef = doc(
|
|
226
|
+
db,
|
|
227
|
+
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
228
|
+
);
|
|
229
|
+
const eventDoc = await getDoc(eventRef);
|
|
230
|
+
|
|
231
|
+
if (!eventDoc.exists()) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return eventDoc.data() as CalendarEvent;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Gets calendar events for a practitioner within a date range
|
|
240
|
+
* @param db - Firestore instance
|
|
241
|
+
* @param practitionerId - ID of the practitioner
|
|
242
|
+
* @param startDate - Start date of the range
|
|
243
|
+
* @param endDate - End date of the range
|
|
244
|
+
* @returns Array of calendar events
|
|
245
|
+
*/
|
|
246
|
+
export async function getPractitionerCalendarEventsUtil(
|
|
247
|
+
db: Firestore,
|
|
248
|
+
practitionerId: string,
|
|
249
|
+
startDate: Date,
|
|
250
|
+
endDate: Date
|
|
251
|
+
): Promise<CalendarEvent[]> {
|
|
252
|
+
const startTimestamp = Timestamp.fromDate(startDate);
|
|
253
|
+
const endTimestamp = Timestamp.fromDate(endDate);
|
|
254
|
+
|
|
255
|
+
const eventsRef = collection(
|
|
256
|
+
db,
|
|
257
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}`
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const q = query(
|
|
261
|
+
eventsRef,
|
|
262
|
+
where("eventTime.start", ">=", startTimestamp),
|
|
263
|
+
where("eventTime.end", "<=", endTimestamp),
|
|
264
|
+
orderBy("eventTime.start", "asc")
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const querySnapshot = await getDocs(q);
|
|
268
|
+
return querySnapshot.docs.map((doc) => doc.data() as CalendarEvent);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Gets calendar events for a patient within a date range
|
|
273
|
+
* @param db - Firestore instance
|
|
274
|
+
* @param patientId - ID of the patient
|
|
275
|
+
* @param startDate - Start date of the range
|
|
276
|
+
* @param endDate - End date of the range
|
|
277
|
+
* @returns Array of calendar events
|
|
278
|
+
*/
|
|
279
|
+
export async function getPatientCalendarEventsUtil(
|
|
280
|
+
db: Firestore,
|
|
281
|
+
patientId: string,
|
|
282
|
+
startDate: Date,
|
|
283
|
+
endDate: Date
|
|
284
|
+
): Promise<CalendarEvent[]> {
|
|
285
|
+
const startTimestamp = Timestamp.fromDate(startDate);
|
|
286
|
+
const endTimestamp = Timestamp.fromDate(endDate);
|
|
287
|
+
|
|
288
|
+
const eventsRef = collection(
|
|
289
|
+
db,
|
|
290
|
+
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}`
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const q = query(
|
|
294
|
+
eventsRef,
|
|
295
|
+
where("eventTime.start", ">=", startTimestamp),
|
|
296
|
+
where("eventTime.end", "<=", endTimestamp),
|
|
297
|
+
orderBy("eventTime.start", "asc")
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const querySnapshot = await getDocs(q);
|
|
301
|
+
return querySnapshot.docs.map((doc) => doc.data() as CalendarEvent);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Gets calendar events for a clinic within a date range
|
|
306
|
+
* @param db - Firestore instance
|
|
307
|
+
* @param clinicId - ID of the clinic
|
|
308
|
+
* @param startDate - Start date of the range
|
|
309
|
+
* @param endDate - End date of the range
|
|
310
|
+
* @returns Array of calendar events
|
|
311
|
+
*/
|
|
312
|
+
export async function getClinicCalendarEventsUtil(
|
|
313
|
+
db: Firestore,
|
|
314
|
+
clinicId: string,
|
|
315
|
+
startDate: Date,
|
|
316
|
+
endDate: Date
|
|
317
|
+
): Promise<CalendarEvent[]> {
|
|
318
|
+
const startTimestamp = Timestamp.fromDate(startDate);
|
|
319
|
+
const endTimestamp = Timestamp.fromDate(endDate);
|
|
320
|
+
|
|
321
|
+
const eventsRef = collection(
|
|
322
|
+
db,
|
|
323
|
+
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}`
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const q = query(
|
|
327
|
+
eventsRef,
|
|
328
|
+
where("eventTime.start", ">=", startTimestamp),
|
|
329
|
+
where("eventTime.end", "<=", endTimestamp),
|
|
330
|
+
orderBy("eventTime.start", "asc")
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const querySnapshot = await getDocs(q);
|
|
334
|
+
return querySnapshot.docs.map((doc) => doc.data() as CalendarEvent);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Updates a calendar event for a practitioner
|
|
339
|
+
* @param db - Firestore instance
|
|
340
|
+
* @param practitionerId - ID of the practitioner
|
|
341
|
+
* @param eventId - ID of the event
|
|
342
|
+
* @param updateData - Data to update
|
|
343
|
+
* @returns Updated calendar event
|
|
344
|
+
*/
|
|
345
|
+
export async function updatePractitionerCalendarEventUtil(
|
|
346
|
+
db: Firestore,
|
|
347
|
+
practitionerId: string,
|
|
348
|
+
eventId: string,
|
|
349
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
350
|
+
): Promise<CalendarEvent> {
|
|
351
|
+
// TODO: Add validation for update data
|
|
352
|
+
// - Check if event exists
|
|
353
|
+
// - Validate event time (start < end)
|
|
354
|
+
// - Check for overlapping events
|
|
355
|
+
|
|
356
|
+
const eventRef = doc(
|
|
357
|
+
db,
|
|
358
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const updates: UpdateCalendarEventData = {
|
|
362
|
+
...updateData,
|
|
363
|
+
updatedAt: serverTimestamp(),
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
await updateDoc(eventRef, updates as any);
|
|
367
|
+
|
|
368
|
+
// Get the updated document
|
|
369
|
+
const updatedDoc = await getDoc(eventRef);
|
|
370
|
+
|
|
371
|
+
if (!updatedDoc.exists()) {
|
|
372
|
+
throw new Error("Event not found after update");
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return updatedDoc.data() as CalendarEvent;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Updates a calendar event for a patient
|
|
380
|
+
* @param db - Firestore instance
|
|
381
|
+
* @param patientId - ID of the patient
|
|
382
|
+
* @param eventId - ID of the event
|
|
383
|
+
* @param updateData - Data to update
|
|
384
|
+
* @returns Updated calendar event
|
|
385
|
+
*/
|
|
386
|
+
export async function updatePatientCalendarEventUtil(
|
|
387
|
+
db: Firestore,
|
|
388
|
+
patientId: string,
|
|
389
|
+
eventId: string,
|
|
390
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
391
|
+
): Promise<CalendarEvent> {
|
|
392
|
+
// TODO: Add validation for update data
|
|
393
|
+
// - Check if event exists
|
|
394
|
+
// - Validate event time (start < end)
|
|
395
|
+
// - Check for overlapping events
|
|
396
|
+
|
|
397
|
+
const eventRef = doc(
|
|
398
|
+
db,
|
|
399
|
+
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
const updates: UpdateCalendarEventData = {
|
|
403
|
+
...updateData,
|
|
404
|
+
updatedAt: serverTimestamp(),
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
await updateDoc(eventRef, updates as any);
|
|
408
|
+
|
|
409
|
+
// Get the updated document
|
|
410
|
+
const updatedDoc = await getDoc(eventRef);
|
|
411
|
+
|
|
412
|
+
if (!updatedDoc.exists()) {
|
|
413
|
+
throw new Error("Event not found after update");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return updatedDoc.data() as CalendarEvent;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Updates a calendar event for a clinic
|
|
421
|
+
* @param db - Firestore instance
|
|
422
|
+
* @param clinicId - ID of the clinic
|
|
423
|
+
* @param eventId - ID of the event
|
|
424
|
+
* @param updateData - Data to update
|
|
425
|
+
* @returns Updated calendar event
|
|
426
|
+
*/
|
|
427
|
+
export async function updateClinicCalendarEventUtil(
|
|
428
|
+
db: Firestore,
|
|
429
|
+
clinicId: string,
|
|
430
|
+
eventId: string,
|
|
431
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
432
|
+
): Promise<CalendarEvent> {
|
|
433
|
+
// TODO: Add validation for update data
|
|
434
|
+
// - Check if event exists
|
|
435
|
+
// - Validate event time (start < end)
|
|
436
|
+
// - Check for overlapping events
|
|
437
|
+
|
|
438
|
+
const eventRef = doc(
|
|
439
|
+
db,
|
|
440
|
+
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const updates: UpdateCalendarEventData = {
|
|
444
|
+
...updateData,
|
|
445
|
+
updatedAt: serverTimestamp(),
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
await updateDoc(eventRef, updates as any);
|
|
449
|
+
|
|
450
|
+
// Get the updated document
|
|
451
|
+
const updatedDoc = await getDoc(eventRef);
|
|
452
|
+
|
|
453
|
+
if (!updatedDoc.exists()) {
|
|
454
|
+
throw new Error("Event not found after update");
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return updatedDoc.data() as CalendarEvent;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Deletes a calendar event for a practitioner
|
|
462
|
+
* @param db - Firestore instance
|
|
463
|
+
* @param practitionerId - ID of the practitioner
|
|
464
|
+
* @param eventId - ID of the event
|
|
465
|
+
*/
|
|
466
|
+
export async function deletePractitionerCalendarEventUtil(
|
|
467
|
+
db: Firestore,
|
|
468
|
+
practitionerId: string,
|
|
469
|
+
eventId: string
|
|
470
|
+
): Promise<void> {
|
|
471
|
+
const eventRef = doc(
|
|
472
|
+
db,
|
|
473
|
+
`${PRACTITIONERS_COLLECTION}/${practitionerId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
474
|
+
);
|
|
475
|
+
await deleteDoc(eventRef);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Deletes a calendar event for a patient
|
|
480
|
+
* @param db - Firestore instance
|
|
481
|
+
* @param patientId - ID of the patient
|
|
482
|
+
* @param eventId - ID of the event
|
|
483
|
+
*/
|
|
484
|
+
export async function deletePatientCalendarEventUtil(
|
|
485
|
+
db: Firestore,
|
|
486
|
+
patientId: string,
|
|
487
|
+
eventId: string
|
|
488
|
+
): Promise<void> {
|
|
489
|
+
const eventRef = doc(
|
|
490
|
+
db,
|
|
491
|
+
`${PATIENTS_COLLECTION}/${patientId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
492
|
+
);
|
|
493
|
+
await deleteDoc(eventRef);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Deletes a calendar event for a clinic
|
|
498
|
+
* @param db - Firestore instance
|
|
499
|
+
* @param clinicId - ID of the clinic
|
|
500
|
+
* @param eventId - ID of the event
|
|
501
|
+
*/
|
|
502
|
+
export async function deleteClinicCalendarEventUtil(
|
|
503
|
+
db: Firestore,
|
|
504
|
+
clinicId: string,
|
|
505
|
+
eventId: string
|
|
506
|
+
): Promise<void> {
|
|
507
|
+
const eventRef = doc(
|
|
508
|
+
db,
|
|
509
|
+
`${CLINICS_COLLECTION}/${clinicId}/${CALENDAR_COLLECTION}/${eventId}`
|
|
510
|
+
);
|
|
511
|
+
await deleteDoc(eventRef);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Searches for calendar events based on specified criteria
|
|
516
|
+
* @param db - Firestore instance
|
|
517
|
+
* @param params - Search parameters
|
|
518
|
+
* @param params.searchLocation - The primary location to search (practitioner, patient, or clinic)
|
|
519
|
+
* @param params.entityId - The ID of the entity (practitioner, patient, or clinic) to search within/for
|
|
520
|
+
* @param params.clinicId - Optional clinic ID to filter by
|
|
521
|
+
* @param params.practitionerId - Optional practitioner ID to filter by
|
|
522
|
+
* @param params.patientId - Optional patient ID to filter by
|
|
523
|
+
* @param params.procedureId - Optional procedure ID to filter by
|
|
524
|
+
* @param params.dateRange - Optional date range to filter by (event start time)
|
|
525
|
+
* @param params.eventStatus - Optional event status to filter by
|
|
526
|
+
* @param params.eventType - Optional event type to filter by
|
|
527
|
+
* @returns Promise resolving to an array of matching calendar events
|
|
528
|
+
*/
|
|
529
|
+
export async function searchCalendarEventsUtil(
|
|
530
|
+
db: Firestore,
|
|
531
|
+
params: SearchCalendarEventsParams
|
|
532
|
+
): Promise<CalendarEvent[]> {
|
|
533
|
+
const { searchLocation, entityId, ...filters } = params;
|
|
534
|
+
|
|
535
|
+
let baseCollectionPath: string;
|
|
536
|
+
const constraints: QueryConstraint[] = [];
|
|
537
|
+
|
|
538
|
+
// Determine the base collection and apply initial filter based on searchLocation
|
|
539
|
+
switch (searchLocation) {
|
|
540
|
+
case SearchLocationEnum.PRACTITIONER:
|
|
541
|
+
if (!entityId) {
|
|
542
|
+
throw new Error(
|
|
543
|
+
"Practitioner ID (entityId) is required when searching practitioner calendar."
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
baseCollectionPath = `${PRACTITIONERS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
547
|
+
// If practitionerId filter is provided, it must match the entityId for this search location
|
|
548
|
+
if (filters.practitionerId && filters.practitionerId !== entityId) {
|
|
549
|
+
console.warn(
|
|
550
|
+
`Provided practitionerId filter (${filters.practitionerId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
551
|
+
);
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
// Ensure we don't add a redundant filter if the caller also specified it
|
|
555
|
+
filters.practitionerId = undefined;
|
|
556
|
+
break;
|
|
557
|
+
|
|
558
|
+
case SearchLocationEnum.PATIENT:
|
|
559
|
+
if (!entityId) {
|
|
560
|
+
throw new Error(
|
|
561
|
+
"Patient ID (entityId) is required when searching patient calendar."
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
baseCollectionPath = `${PATIENTS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
565
|
+
// If patientId filter is provided, it must match the entityId for this search location
|
|
566
|
+
if (filters.patientId && filters.patientId !== entityId) {
|
|
567
|
+
console.warn(
|
|
568
|
+
`Provided patientId filter (${filters.patientId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
569
|
+
);
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
// Ensure we don't add a redundant filter if the caller also specified it
|
|
573
|
+
filters.patientId = undefined;
|
|
574
|
+
break;
|
|
575
|
+
|
|
576
|
+
case SearchLocationEnum.CLINIC:
|
|
577
|
+
if (!entityId) {
|
|
578
|
+
throw new Error(
|
|
579
|
+
"Clinic ID (entityId) is required when searching clinic-related events."
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
// Search the clinic's calendar collection
|
|
583
|
+
baseCollectionPath = `${CLINICS_COLLECTION}/${entityId}/${CALENDAR_COLLECTION}`;
|
|
584
|
+
constraints.push(where("clinicBranchId", "==", entityId));
|
|
585
|
+
// If clinicId filter is provided, it must match the entityId for this search location
|
|
586
|
+
if (filters.clinicId && filters.clinicId !== entityId) {
|
|
587
|
+
console.warn(
|
|
588
|
+
`Provided clinicId filter (${filters.clinicId}) does not match search entityId (${entityId}). Returning empty results.`
|
|
589
|
+
);
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
592
|
+
// Ensure we don't add a redundant filter if the caller also specified it
|
|
593
|
+
filters.clinicId = undefined; // Already handled by the base query
|
|
594
|
+
break;
|
|
595
|
+
|
|
596
|
+
default:
|
|
597
|
+
throw new Error(`Invalid search location: ${searchLocation}`);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const collectionRef = collection(db, baseCollectionPath);
|
|
601
|
+
|
|
602
|
+
// Apply optional filters
|
|
603
|
+
if (filters.clinicId) {
|
|
604
|
+
constraints.push(where("clinicBranchId", "==", filters.clinicId));
|
|
605
|
+
}
|
|
606
|
+
if (filters.practitionerId) {
|
|
607
|
+
constraints.push(
|
|
608
|
+
where("practitionerProfileId", "==", filters.practitionerId)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
if (filters.patientId) {
|
|
612
|
+
constraints.push(where("patientProfileId", "==", filters.patientId));
|
|
613
|
+
}
|
|
614
|
+
if (filters.procedureId) {
|
|
615
|
+
constraints.push(where("procedureId", "==", filters.procedureId));
|
|
616
|
+
}
|
|
617
|
+
if (filters.eventStatus) {
|
|
618
|
+
constraints.push(where("status", "==", filters.eventStatus));
|
|
619
|
+
}
|
|
620
|
+
if (filters.eventType) {
|
|
621
|
+
constraints.push(where("eventType", "==", filters.eventType));
|
|
622
|
+
}
|
|
623
|
+
if (filters.dateRange) {
|
|
624
|
+
// Firestore requires range filters on the same field
|
|
625
|
+
constraints.push(where("eventTime.start", ">=", filters.dateRange.start));
|
|
626
|
+
constraints.push(where("eventTime.start", "<=", filters.dateRange.end));
|
|
627
|
+
// Note: You might need to order by eventTime.start for range filters to work efficiently
|
|
628
|
+
// constraints.push(orderBy("eventTime.start"));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Build and execute the query
|
|
632
|
+
try {
|
|
633
|
+
const finalQuery = query(collectionRef, ...constraints);
|
|
634
|
+
const querySnapshot = await getDocs(finalQuery);
|
|
635
|
+
|
|
636
|
+
const events = querySnapshot.docs.map(
|
|
637
|
+
(doc) => ({ id: doc.id, ...doc.data() } as CalendarEvent)
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
return events;
|
|
641
|
+
} catch (error) {
|
|
642
|
+
console.error("Error searching calendar events:", error);
|
|
643
|
+
// Depending on requirements, you might want to return an empty array or re-throw
|
|
644
|
+
return [];
|
|
645
|
+
}
|
|
646
|
+
}
|