@blackcode_sa/metaestetics-api 1.13.3 → 1.13.5
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 +15 -28
- package/dist/admin/index.d.ts +15 -28
- package/dist/index.d.mts +18 -30
- package/dist/index.d.ts +18 -30
- package/dist/index.js +11 -3
- package/dist/index.mjs +11 -3
- package/package.json +121 -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 +1984 -1984
- 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 -689
- package/src/admin/analytics/analytics.admin.service.ts +278 -278
- package/src/admin/analytics/index.ts +2 -2
- 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 +81 -81
- 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 +57 -57
- package/src/backoffice/services/analytics.service.proposal.md +863 -863
- package/src/backoffice/services/analytics.service.summary.md +143 -143
- package/src/backoffice/services/brand.service.ts +256 -256
- package/src/backoffice/services/category.service.ts +384 -384
- 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 +461 -461
- package/src/backoffice/services/technology.service.ts +1151 -1151
- 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 +67 -67
- 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 +168 -168
- 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/analytics/ARCHITECTURE.md +199 -199
- package/src/services/analytics/CLOUD_FUNCTIONS.md +225 -225
- package/src/services/analytics/GROUPED_ANALYTICS.md +501 -501
- package/src/services/analytics/QUICK_START.md +393 -393
- package/src/services/analytics/README.md +304 -304
- package/src/services/analytics/SUMMARY.md +141 -141
- package/src/services/analytics/TRENDS.md +380 -380
- package/src/services/analytics/USAGE_GUIDE.md +518 -518
- package/src/services/analytics/analytics-cloud.service.ts +222 -222
- package/src/services/analytics/analytics.service.ts +2142 -2142
- package/src/services/analytics/index.ts +4 -4
- package/src/services/analytics/review-analytics.service.ts +941 -941
- package/src/services/analytics/utils/appointment-filtering.utils.ts +138 -138
- package/src/services/analytics/utils/cost-calculation.utils.ts +182 -182
- package/src/services/analytics/utils/grouping.utils.ts +434 -434
- package/src/services/analytics/utils/stored-analytics.utils.ts +347 -347
- package/src/services/analytics/utils/time-calculation.utils.ts +186 -186
- package/src/services/analytics/utils/trend-calculation.utils.ts +200 -200
- package/src/services/appointment/README.md +17 -17
- package/src/services/appointment/appointment.service.ts +2558 -2558
- 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 +14 -14
- 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 +2200 -2191
- package/src/services/reviews/index.ts +1 -1
- package/src/services/reviews/reviews.service.ts +734 -734
- 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/analytics/analytics.types.ts +597 -597
- package/src/types/analytics/grouped-analytics.types.ts +173 -173
- package/src/types/analytics/index.ts +4 -4
- package/src/types/analytics/stored-analytics.types.ts +137 -137
- 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 +498 -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 +47 -47
- package/src/types/notifications/README.md +77 -77
- package/src/types/notifications/index.ts +286 -286
- 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 -132
- package/src/types/tz-lookup.d.ts +4 -4
- package/src/types/user/index.ts +38 -38
- package/src/utils/TIMESTAMPS.md +176 -176
- package/src/utils/TimestampUtils.ts +241 -241
- package/src/utils/index.ts +1 -1
- package/src/validations/appointment.schema.ts +574 -574
- package/src/validations/calendar.schema.ts +225 -225
- package/src/validations/clinic.schema.ts +494 -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 -195
- package/src/validations/schemas.ts +104 -104
- package/src/validations/shared.schema.ts +78 -78
|
@@ -1,1077 +1,1077 @@
|
|
|
1
|
-
import { Auth } from "firebase/auth";
|
|
2
|
-
import { Firestore, Timestamp } from "firebase/firestore";
|
|
3
|
-
import { FirebaseApp } from "firebase/app";
|
|
4
|
-
import { BaseService } from "../base.service";
|
|
5
|
-
import {
|
|
6
|
-
CalendarEvent,
|
|
7
|
-
CalendarEventStatus,
|
|
8
|
-
CalendarEventTime,
|
|
9
|
-
CalendarEventType,
|
|
10
|
-
CalendarSyncStatus,
|
|
11
|
-
CreateCalendarEventData,
|
|
12
|
-
UpdateCalendarEventData,
|
|
13
|
-
CALENDAR_COLLECTION,
|
|
14
|
-
SyncedCalendarEvent,
|
|
15
|
-
} from "../../types/calendar";
|
|
16
|
-
import { PRACTITIONERS_COLLECTION } from "../../types/practitioner";
|
|
17
|
-
import { PATIENTS_COLLECTION } from "../../types/patient";
|
|
18
|
-
import { CLINICS_COLLECTION } from "../../types/clinic";
|
|
19
|
-
import { SyncedCalendarProvider } from "../../types/calendar/synced-calendar.types";
|
|
20
|
-
|
|
21
|
-
// Import utility functions from the index file
|
|
22
|
-
import {
|
|
23
|
-
createPractitionerCalendarEventUtil,
|
|
24
|
-
createPatientCalendarEventUtil,
|
|
25
|
-
createClinicCalendarEventUtil,
|
|
26
|
-
getPractitionerCalendarEventUtil,
|
|
27
|
-
getPatientCalendarEventUtil,
|
|
28
|
-
getClinicCalendarEventUtil,
|
|
29
|
-
getPractitionerCalendarEventsUtil,
|
|
30
|
-
getPatientCalendarEventsUtil,
|
|
31
|
-
getClinicCalendarEventsUtil,
|
|
32
|
-
updatePractitionerCalendarEventUtil,
|
|
33
|
-
updatePatientCalendarEventUtil,
|
|
34
|
-
updateClinicCalendarEventUtil,
|
|
35
|
-
deletePractitionerCalendarEventUtil,
|
|
36
|
-
deletePatientCalendarEventUtil,
|
|
37
|
-
deleteClinicCalendarEventUtil,
|
|
38
|
-
} from "./utils";
|
|
39
|
-
|
|
40
|
-
// Import the SyncedCalendarsService for calendar synchronization
|
|
41
|
-
import { SyncedCalendarsService } from "./synced-calendars.service";
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Service for managing calendar events
|
|
45
|
-
*/
|
|
46
|
-
export class CalendarService extends BaseService {
|
|
47
|
-
private syncedCalendarsService: SyncedCalendarsService;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Creates a new CalendarService instance
|
|
51
|
-
* @param db - Firestore instance
|
|
52
|
-
* @param auth - Firebase Auth instance
|
|
53
|
-
* @param app - Firebase App instance
|
|
54
|
-
*/
|
|
55
|
-
constructor(db: Firestore, auth: Auth, app: FirebaseApp) {
|
|
56
|
-
super(db, auth, app);
|
|
57
|
-
this.syncedCalendarsService = new SyncedCalendarsService(db, auth, app);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Creates a calendar event for a practitioner
|
|
62
|
-
* @param practitionerId - ID of the practitioner
|
|
63
|
-
* @param eventData - Calendar event data
|
|
64
|
-
* @returns Created calendar event
|
|
65
|
-
*/
|
|
66
|
-
async createPractitionerCalendarEvent(
|
|
67
|
-
practitionerId: string,
|
|
68
|
-
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">
|
|
69
|
-
): Promise<CalendarEvent> {
|
|
70
|
-
return createPractitionerCalendarEventUtil(
|
|
71
|
-
this.db,
|
|
72
|
-
practitionerId,
|
|
73
|
-
eventData,
|
|
74
|
-
this.generateId.bind(this)
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Creates a calendar event for a patient
|
|
80
|
-
* @param patientId - ID of the patient
|
|
81
|
-
* @param eventData - Calendar event data
|
|
82
|
-
* @returns Created calendar event
|
|
83
|
-
*/
|
|
84
|
-
async createPatientCalendarEvent(
|
|
85
|
-
patientId: string,
|
|
86
|
-
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">
|
|
87
|
-
): Promise<CalendarEvent> {
|
|
88
|
-
return createPatientCalendarEventUtil(
|
|
89
|
-
this.db,
|
|
90
|
-
patientId,
|
|
91
|
-
eventData,
|
|
92
|
-
this.generateId.bind(this)
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Creates a calendar event for a clinic
|
|
98
|
-
* @param clinicId - ID of the clinic
|
|
99
|
-
* @param eventData - Calendar event data
|
|
100
|
-
* @returns Created calendar event
|
|
101
|
-
*/
|
|
102
|
-
async createClinicCalendarEvent(
|
|
103
|
-
clinicId: string,
|
|
104
|
-
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">
|
|
105
|
-
): Promise<CalendarEvent> {
|
|
106
|
-
return createClinicCalendarEventUtil(
|
|
107
|
-
this.db,
|
|
108
|
-
clinicId,
|
|
109
|
-
eventData,
|
|
110
|
-
this.generateId.bind(this)
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Creates an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
116
|
-
* @param clinicId - ID of the clinic
|
|
117
|
-
* @param practitionerId - ID of the practitioner
|
|
118
|
-
* @param patientId - ID of the patient
|
|
119
|
-
* @param eventData - Calendar event data
|
|
120
|
-
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
121
|
-
* @returns Created calendar event
|
|
122
|
-
*/
|
|
123
|
-
async createAppointment(
|
|
124
|
-
clinicId: string,
|
|
125
|
-
practitionerId: string,
|
|
126
|
-
patientId: string,
|
|
127
|
-
eventData: Omit<
|
|
128
|
-
CreateCalendarEventData,
|
|
129
|
-
| "id"
|
|
130
|
-
| "createdAt"
|
|
131
|
-
| "updatedAt"
|
|
132
|
-
| "clinicBranchId"
|
|
133
|
-
| "practitionerProfileId"
|
|
134
|
-
| "patientProfileId"
|
|
135
|
-
>,
|
|
136
|
-
syncWithExternalCalendars: boolean = false
|
|
137
|
-
): Promise<CalendarEvent> {
|
|
138
|
-
// Generate a single ID to be used across all calendars
|
|
139
|
-
const eventId = this.generateId();
|
|
140
|
-
|
|
141
|
-
// Prepare the event data with all required IDs
|
|
142
|
-
const appointmentData: Omit<
|
|
143
|
-
CreateCalendarEventData,
|
|
144
|
-
"id" | "createdAt" | "updatedAt"
|
|
145
|
-
> = {
|
|
146
|
-
...eventData,
|
|
147
|
-
clinicBranchId: clinicId,
|
|
148
|
-
practitionerProfileId: practitionerId,
|
|
149
|
-
patientProfileId: patientId,
|
|
150
|
-
eventType: CalendarEventType.APPOINTMENT,
|
|
151
|
-
// Set sync status to INTERNAL as this is created in the app ecosystem
|
|
152
|
-
syncStatus: CalendarSyncStatus.INTERNAL,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Create the event in all three calendars
|
|
156
|
-
const clinicPromise = this.createClinicCalendarEvent(
|
|
157
|
-
clinicId,
|
|
158
|
-
appointmentData
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
const practitionerPromise = this.createPractitionerCalendarEvent(
|
|
162
|
-
practitionerId,
|
|
163
|
-
appointmentData
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
const patientPromise = this.createPatientCalendarEvent(
|
|
167
|
-
patientId,
|
|
168
|
-
appointmentData
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
// Wait for all operations to complete
|
|
172
|
-
const [clinicEvent] = await Promise.all([
|
|
173
|
-
clinicPromise,
|
|
174
|
-
practitionerPromise,
|
|
175
|
-
patientPromise,
|
|
176
|
-
]);
|
|
177
|
-
|
|
178
|
-
// Sync with external calendars if requested
|
|
179
|
-
if (syncWithExternalCalendars) {
|
|
180
|
-
try {
|
|
181
|
-
await this.syncAppointmentWithExternalCalendars(
|
|
182
|
-
clinicId,
|
|
183
|
-
practitionerId,
|
|
184
|
-
patientId,
|
|
185
|
-
clinicEvent.id
|
|
186
|
-
);
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.error(
|
|
189
|
-
"Error syncing appointment with external calendars:",
|
|
190
|
-
error
|
|
191
|
-
);
|
|
192
|
-
// Continue even if sync fails
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Return the event from the clinic calendar
|
|
197
|
-
return clinicEvent;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Gets a calendar event for a practitioner
|
|
202
|
-
* @param practitionerId - ID of the practitioner
|
|
203
|
-
* @param eventId - ID of the event
|
|
204
|
-
* @returns Calendar event or null if not found
|
|
205
|
-
*/
|
|
206
|
-
async getPractitionerCalendarEvent(
|
|
207
|
-
practitionerId: string,
|
|
208
|
-
eventId: string
|
|
209
|
-
): Promise<CalendarEvent | null> {
|
|
210
|
-
return getPractitionerCalendarEventUtil(this.db, practitionerId, eventId);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Gets a calendar event for a patient
|
|
215
|
-
* @param patientId - ID of the patient
|
|
216
|
-
* @param eventId - ID of the event
|
|
217
|
-
* @returns Calendar event or null if not found
|
|
218
|
-
*/
|
|
219
|
-
async getPatientCalendarEvent(
|
|
220
|
-
patientId: string,
|
|
221
|
-
eventId: string
|
|
222
|
-
): Promise<CalendarEvent | null> {
|
|
223
|
-
return getPatientCalendarEventUtil(this.db, patientId, eventId);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Gets a calendar event for a clinic
|
|
228
|
-
* @param clinicId - ID of the clinic
|
|
229
|
-
* @param eventId - ID of the event
|
|
230
|
-
* @returns Calendar event or null if not found
|
|
231
|
-
*/
|
|
232
|
-
async getClinicCalendarEvent(
|
|
233
|
-
clinicId: string,
|
|
234
|
-
eventId: string
|
|
235
|
-
): Promise<CalendarEvent | null> {
|
|
236
|
-
return getClinicCalendarEventUtil(this.db, clinicId, eventId);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Gets calendar events for a practitioner within a date range
|
|
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
|
-
async getPractitionerCalendarEvents(
|
|
247
|
-
practitionerId: string,
|
|
248
|
-
startDate: Date,
|
|
249
|
-
endDate: Date
|
|
250
|
-
): Promise<CalendarEvent[]> {
|
|
251
|
-
return getPractitionerCalendarEventsUtil(
|
|
252
|
-
this.db,
|
|
253
|
-
practitionerId,
|
|
254
|
-
startDate,
|
|
255
|
-
endDate
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Gets calendar events for a patient within a date range
|
|
261
|
-
* @param patientId - ID of the patient
|
|
262
|
-
* @param startDate - Start date of the range
|
|
263
|
-
* @param endDate - End date of the range
|
|
264
|
-
* @returns Array of calendar events
|
|
265
|
-
*/
|
|
266
|
-
async getPatientCalendarEvents(
|
|
267
|
-
patientId: string,
|
|
268
|
-
startDate: Date,
|
|
269
|
-
endDate: Date
|
|
270
|
-
): Promise<CalendarEvent[]> {
|
|
271
|
-
return getPatientCalendarEventsUtil(this.db, patientId, startDate, endDate);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Gets calendar events for a clinic within a date range
|
|
276
|
-
* @param clinicId - ID of the clinic
|
|
277
|
-
* @param startDate - Start date of the range
|
|
278
|
-
* @param endDate - End date of the range
|
|
279
|
-
* @returns Array of calendar events
|
|
280
|
-
*/
|
|
281
|
-
async getClinicCalendarEvents(
|
|
282
|
-
clinicId: string,
|
|
283
|
-
startDate: Date,
|
|
284
|
-
endDate: Date
|
|
285
|
-
): Promise<CalendarEvent[]> {
|
|
286
|
-
return getClinicCalendarEventsUtil(this.db, clinicId, startDate, endDate);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Updates a calendar event for a practitioner
|
|
291
|
-
* @param practitionerId - ID of the practitioner
|
|
292
|
-
* @param eventId - ID of the event
|
|
293
|
-
* @param updateData - Data to update
|
|
294
|
-
* @returns Updated calendar event
|
|
295
|
-
*/
|
|
296
|
-
async updatePractitionerCalendarEvent(
|
|
297
|
-
practitionerId: string,
|
|
298
|
-
eventId: string,
|
|
299
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
300
|
-
): Promise<CalendarEvent> {
|
|
301
|
-
return updatePractitionerCalendarEventUtil(
|
|
302
|
-
this.db,
|
|
303
|
-
practitionerId,
|
|
304
|
-
eventId,
|
|
305
|
-
updateData
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Updates a calendar event for a patient
|
|
311
|
-
* @param patientId - ID of the patient
|
|
312
|
-
* @param eventId - ID of the event
|
|
313
|
-
* @param updateData - Data to update
|
|
314
|
-
* @returns Updated calendar event
|
|
315
|
-
*/
|
|
316
|
-
async updatePatientCalendarEvent(
|
|
317
|
-
patientId: string,
|
|
318
|
-
eventId: string,
|
|
319
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
320
|
-
): Promise<CalendarEvent> {
|
|
321
|
-
return updatePatientCalendarEventUtil(
|
|
322
|
-
this.db,
|
|
323
|
-
patientId,
|
|
324
|
-
eventId,
|
|
325
|
-
updateData
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Updates a calendar event for a clinic
|
|
331
|
-
* @param clinicId - ID of the clinic
|
|
332
|
-
* @param eventId - ID of the event
|
|
333
|
-
* @param updateData - Data to update
|
|
334
|
-
* @returns Updated calendar event
|
|
335
|
-
*/
|
|
336
|
-
async updateClinicCalendarEvent(
|
|
337
|
-
clinicId: string,
|
|
338
|
-
eventId: string,
|
|
339
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
340
|
-
): Promise<CalendarEvent> {
|
|
341
|
-
return updateClinicCalendarEventUtil(
|
|
342
|
-
this.db,
|
|
343
|
-
clinicId,
|
|
344
|
-
eventId,
|
|
345
|
-
updateData
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Updates an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
351
|
-
* @param clinicId - ID of the clinic
|
|
352
|
-
* @param practitionerId - ID of the practitioner
|
|
353
|
-
* @param patientId - ID of the patient
|
|
354
|
-
* @param eventId - ID of the event
|
|
355
|
-
* @param updateData - Data to update
|
|
356
|
-
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
357
|
-
* @returns Updated calendar event
|
|
358
|
-
*/
|
|
359
|
-
async updateAppointment(
|
|
360
|
-
clinicId: string,
|
|
361
|
-
practitionerId: string,
|
|
362
|
-
patientId: string,
|
|
363
|
-
eventId: string,
|
|
364
|
-
updateData: Omit<UpdateCalendarEventData, "updatedAt">,
|
|
365
|
-
syncWithExternalCalendars: boolean = false
|
|
366
|
-
): Promise<CalendarEvent> {
|
|
367
|
-
// Update the event in all three calendars
|
|
368
|
-
const clinicPromise = this.updateClinicCalendarEvent(
|
|
369
|
-
clinicId,
|
|
370
|
-
eventId,
|
|
371
|
-
updateData
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
const practitionerPromise = this.updatePractitionerCalendarEvent(
|
|
375
|
-
practitionerId,
|
|
376
|
-
eventId,
|
|
377
|
-
updateData
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
const patientPromise = this.updatePatientCalendarEvent(
|
|
381
|
-
patientId,
|
|
382
|
-
eventId,
|
|
383
|
-
updateData
|
|
384
|
-
);
|
|
385
|
-
|
|
386
|
-
// Wait for all operations to complete
|
|
387
|
-
const [clinicEvent] = await Promise.all([
|
|
388
|
-
clinicPromise,
|
|
389
|
-
practitionerPromise,
|
|
390
|
-
patientPromise,
|
|
391
|
-
]);
|
|
392
|
-
|
|
393
|
-
// Sync with external calendars if requested
|
|
394
|
-
if (syncWithExternalCalendars) {
|
|
395
|
-
try {
|
|
396
|
-
await this.syncAppointmentWithExternalCalendars(
|
|
397
|
-
clinicId,
|
|
398
|
-
practitionerId,
|
|
399
|
-
patientId,
|
|
400
|
-
eventId
|
|
401
|
-
);
|
|
402
|
-
} catch (error) {
|
|
403
|
-
console.error(
|
|
404
|
-
"Error syncing appointment with external calendars:",
|
|
405
|
-
error
|
|
406
|
-
);
|
|
407
|
-
// Continue even if sync fails
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Return the event from the clinic calendar
|
|
412
|
-
return clinicEvent;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Deletes a calendar event for a practitioner
|
|
417
|
-
* @param practitionerId - ID of the practitioner
|
|
418
|
-
* @param eventId - ID of the event
|
|
419
|
-
*/
|
|
420
|
-
async deletePractitionerCalendarEvent(
|
|
421
|
-
practitionerId: string,
|
|
422
|
-
eventId: string
|
|
423
|
-
): Promise<void> {
|
|
424
|
-
return deletePractitionerCalendarEventUtil(
|
|
425
|
-
this.db,
|
|
426
|
-
practitionerId,
|
|
427
|
-
eventId
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Deletes a calendar event for a patient
|
|
433
|
-
* @param patientId - ID of the patient
|
|
434
|
-
* @param eventId - ID of the event
|
|
435
|
-
*/
|
|
436
|
-
async deletePatientCalendarEvent(
|
|
437
|
-
patientId: string,
|
|
438
|
-
eventId: string
|
|
439
|
-
): Promise<void> {
|
|
440
|
-
return deletePatientCalendarEventUtil(this.db, patientId, eventId);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Deletes a calendar event for a clinic
|
|
445
|
-
* @param clinicId - ID of the clinic
|
|
446
|
-
* @param eventId - ID of the event
|
|
447
|
-
*/
|
|
448
|
-
async deleteClinicCalendarEvent(
|
|
449
|
-
clinicId: string,
|
|
450
|
-
eventId: string
|
|
451
|
-
): Promise<void> {
|
|
452
|
-
return deleteClinicCalendarEventUtil(this.db, clinicId, eventId);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Deletes an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
457
|
-
* @param clinicId - ID of the clinic
|
|
458
|
-
* @param practitionerId - ID of the practitioner
|
|
459
|
-
* @param patientId - ID of the patient
|
|
460
|
-
* @param eventId - ID of the event
|
|
461
|
-
*/
|
|
462
|
-
async deleteAppointment(
|
|
463
|
-
clinicId: string,
|
|
464
|
-
practitionerId: string,
|
|
465
|
-
patientId: string,
|
|
466
|
-
eventId: string
|
|
467
|
-
): Promise<void> {
|
|
468
|
-
// First get the appointment to check if it's synced with external calendars
|
|
469
|
-
const appointment = await this.getClinicCalendarEvent(clinicId, eventId);
|
|
470
|
-
|
|
471
|
-
if (appointment && appointment.syncStatus === CalendarSyncStatus.EXTERNAL) {
|
|
472
|
-
// Delete from external calendars
|
|
473
|
-
await this.deleteEventFromExternalCalendars(
|
|
474
|
-
appointment,
|
|
475
|
-
"clinic",
|
|
476
|
-
clinicId
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
// Also try to delete from practitioner and patient external calendars
|
|
480
|
-
const practitionerEvent = await this.getPractitionerCalendarEvent(
|
|
481
|
-
practitionerId,
|
|
482
|
-
eventId
|
|
483
|
-
);
|
|
484
|
-
if (practitionerEvent) {
|
|
485
|
-
await this.deleteEventFromExternalCalendars(
|
|
486
|
-
practitionerEvent,
|
|
487
|
-
"practitioner",
|
|
488
|
-
practitionerId
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const patientEvent = await this.getPatientCalendarEvent(
|
|
493
|
-
patientId,
|
|
494
|
-
eventId
|
|
495
|
-
);
|
|
496
|
-
if (patientEvent) {
|
|
497
|
-
await this.deleteEventFromExternalCalendars(
|
|
498
|
-
patientEvent,
|
|
499
|
-
"patient",
|
|
500
|
-
patientId
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Delete the event from all three calendars
|
|
506
|
-
const clinicPromise = this.deleteClinicCalendarEvent(clinicId, eventId);
|
|
507
|
-
|
|
508
|
-
const practitionerPromise = this.deletePractitionerCalendarEvent(
|
|
509
|
-
practitionerId,
|
|
510
|
-
eventId
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
const patientPromise = this.deletePatientCalendarEvent(patientId, eventId);
|
|
514
|
-
|
|
515
|
-
// Wait for all operations to complete
|
|
516
|
-
await Promise.all([clinicPromise, practitionerPromise, patientPromise]);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Gets pending appointments for a clinic
|
|
521
|
-
* @param clinicId - ID of the clinic
|
|
522
|
-
* @returns Array of pending calendar events
|
|
523
|
-
*/
|
|
524
|
-
async getPendingClinicAppointments(
|
|
525
|
-
clinicId: string
|
|
526
|
-
): Promise<CalendarEvent[]> {
|
|
527
|
-
const events = await getClinicCalendarEventsUtil(
|
|
528
|
-
this.db,
|
|
529
|
-
clinicId,
|
|
530
|
-
new Date(0), // From the beginning of time
|
|
531
|
-
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // To one year from now
|
|
532
|
-
);
|
|
533
|
-
|
|
534
|
-
return events.filter(
|
|
535
|
-
(event: CalendarEvent) =>
|
|
536
|
-
event.status === CalendarEventStatus.PENDING &&
|
|
537
|
-
event.eventType === CalendarEventType.APPOINTMENT
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Gets confirmed appointments for a practitioner
|
|
543
|
-
* @param practitionerId - ID of the practitioner
|
|
544
|
-
* @returns Array of confirmed calendar events
|
|
545
|
-
*/
|
|
546
|
-
async getConfirmedPractitionerAppointments(
|
|
547
|
-
practitionerId: string
|
|
548
|
-
): Promise<CalendarEvent[]> {
|
|
549
|
-
const events = await getPractitionerCalendarEventsUtil(
|
|
550
|
-
this.db,
|
|
551
|
-
practitionerId,
|
|
552
|
-
new Date(0), // From the beginning of time
|
|
553
|
-
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // To one year from now
|
|
554
|
-
);
|
|
555
|
-
|
|
556
|
-
return events.filter(
|
|
557
|
-
(event: CalendarEvent) =>
|
|
558
|
-
event.status === CalendarEventStatus.CONFIRMED &&
|
|
559
|
-
event.eventType === CalendarEventType.APPOINTMENT
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Gets upcoming appointments for a patient
|
|
565
|
-
* @param patientId - ID of the patient
|
|
566
|
-
* @returns Array of upcoming calendar events
|
|
567
|
-
*/
|
|
568
|
-
async getUpcomingPatientAppointments(
|
|
569
|
-
patientId: string
|
|
570
|
-
): Promise<CalendarEvent[]> {
|
|
571
|
-
const now = new Date();
|
|
572
|
-
const events = await getPatientCalendarEventsUtil(
|
|
573
|
-
this.db,
|
|
574
|
-
patientId,
|
|
575
|
-
now,
|
|
576
|
-
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // To one year from now
|
|
577
|
-
);
|
|
578
|
-
|
|
579
|
-
return events.filter(
|
|
580
|
-
(event: CalendarEvent) =>
|
|
581
|
-
event.eventType === CalendarEventType.APPOINTMENT
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* Confirms an appointment
|
|
587
|
-
* @param clinicId - ID of the clinic
|
|
588
|
-
* @param practitionerId - ID of the practitioner
|
|
589
|
-
* @param patientId - ID of the patient
|
|
590
|
-
* @param eventId - ID of the event
|
|
591
|
-
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
592
|
-
* @returns Confirmed calendar event
|
|
593
|
-
*/
|
|
594
|
-
async confirmAppointment(
|
|
595
|
-
clinicId: string,
|
|
596
|
-
practitionerId: string,
|
|
597
|
-
patientId: string,
|
|
598
|
-
eventId: string,
|
|
599
|
-
syncWithExternalCalendars: boolean = true
|
|
600
|
-
): Promise<CalendarEvent> {
|
|
601
|
-
return this.updateAppointment(
|
|
602
|
-
clinicId,
|
|
603
|
-
practitionerId,
|
|
604
|
-
patientId,
|
|
605
|
-
eventId,
|
|
606
|
-
{ status: CalendarEventStatus.CONFIRMED },
|
|
607
|
-
syncWithExternalCalendars
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Rejects an appointment
|
|
613
|
-
* @param clinicId - ID of the clinic
|
|
614
|
-
* @param practitionerId - ID of the practitioner
|
|
615
|
-
* @param patientId - ID of the patient
|
|
616
|
-
* @param eventId - ID of the event
|
|
617
|
-
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
618
|
-
* @returns Rejected calendar event
|
|
619
|
-
*/
|
|
620
|
-
async rejectAppointment(
|
|
621
|
-
clinicId: string,
|
|
622
|
-
practitionerId: string,
|
|
623
|
-
patientId: string,
|
|
624
|
-
eventId: string,
|
|
625
|
-
syncWithExternalCalendars: boolean = true
|
|
626
|
-
): Promise<CalendarEvent> {
|
|
627
|
-
return this.updateAppointment(
|
|
628
|
-
clinicId,
|
|
629
|
-
practitionerId,
|
|
630
|
-
patientId,
|
|
631
|
-
eventId,
|
|
632
|
-
{ status: CalendarEventStatus.REJECTED },
|
|
633
|
-
syncWithExternalCalendars
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/**
|
|
638
|
-
* Cancels an appointment
|
|
639
|
-
* @param clinicId - ID of the clinic
|
|
640
|
-
* @param practitionerId - ID of the practitioner
|
|
641
|
-
* @param patientId - ID of the patient
|
|
642
|
-
* @param eventId - ID of the event
|
|
643
|
-
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
644
|
-
* @returns Canceled calendar event
|
|
645
|
-
*/
|
|
646
|
-
async cancelAppointment(
|
|
647
|
-
clinicId: string,
|
|
648
|
-
practitionerId: string,
|
|
649
|
-
patientId: string,
|
|
650
|
-
eventId: string,
|
|
651
|
-
syncWithExternalCalendars: boolean = true
|
|
652
|
-
): Promise<CalendarEvent> {
|
|
653
|
-
return this.updateAppointment(
|
|
654
|
-
clinicId,
|
|
655
|
-
practitionerId,
|
|
656
|
-
patientId,
|
|
657
|
-
eventId,
|
|
658
|
-
{ status: CalendarEventStatus.CANCELED },
|
|
659
|
-
syncWithExternalCalendars
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
* Reschedules an appointment
|
|
665
|
-
* @param clinicId - ID of the clinic
|
|
666
|
-
* @param practitionerId - ID of the practitioner
|
|
667
|
-
* @param patientId - ID of the patient
|
|
668
|
-
* @param eventId - ID of the event
|
|
669
|
-
* @param newEventTime - New event time
|
|
670
|
-
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
671
|
-
* @returns Rescheduled calendar event
|
|
672
|
-
*/
|
|
673
|
-
async rescheduleAppointment(
|
|
674
|
-
clinicId: string,
|
|
675
|
-
practitionerId: string,
|
|
676
|
-
patientId: string,
|
|
677
|
-
eventId: string,
|
|
678
|
-
newEventTime: CalendarEventTime,
|
|
679
|
-
syncWithExternalCalendars: boolean = true
|
|
680
|
-
): Promise<CalendarEvent> {
|
|
681
|
-
// First get the current event to preserve any other fields
|
|
682
|
-
const currentEvent = await this.getClinicCalendarEvent(clinicId, eventId);
|
|
683
|
-
|
|
684
|
-
if (!currentEvent) {
|
|
685
|
-
throw new Error(`Appointment with ID ${eventId} not found`);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Update the appointment with new time and status
|
|
689
|
-
return this.updateAppointment(
|
|
690
|
-
clinicId,
|
|
691
|
-
practitionerId,
|
|
692
|
-
patientId,
|
|
693
|
-
eventId,
|
|
694
|
-
{
|
|
695
|
-
status: CalendarEventStatus.RESCHEDULED,
|
|
696
|
-
eventTime: newEventTime,
|
|
697
|
-
},
|
|
698
|
-
syncWithExternalCalendars
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Gets the SyncedCalendarsService for managing synced calendars
|
|
704
|
-
* @returns SyncedCalendarsService instance
|
|
705
|
-
*/
|
|
706
|
-
getSyncedCalendarsService(): SyncedCalendarsService {
|
|
707
|
-
return this.syncedCalendarsService;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* Syncs an appointment with external calendars
|
|
712
|
-
* @param clinicId - ID of the clinic
|
|
713
|
-
* @param practitionerId - ID of the practitioner
|
|
714
|
-
* @param patientId - ID of the patient
|
|
715
|
-
* @param eventId - ID of the event
|
|
716
|
-
* @returns Updated calendar event with sync information
|
|
717
|
-
*/
|
|
718
|
-
async syncAppointmentWithExternalCalendars(
|
|
719
|
-
clinicId: string,
|
|
720
|
-
practitionerId: string,
|
|
721
|
-
patientId: string,
|
|
722
|
-
eventId: string
|
|
723
|
-
): Promise<CalendarEvent> {
|
|
724
|
-
// Get the appointment
|
|
725
|
-
const appointment = await this.getClinicCalendarEvent(clinicId, eventId);
|
|
726
|
-
|
|
727
|
-
if (!appointment) {
|
|
728
|
-
throw new Error(`Appointment with ID ${eventId} not found`);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// Get synced calendars for all entities
|
|
732
|
-
const clinicCalendars =
|
|
733
|
-
await this.syncedCalendarsService.getClinicSyncedCalendars(clinicId);
|
|
734
|
-
const practitionerCalendars =
|
|
735
|
-
await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
736
|
-
practitionerId
|
|
737
|
-
);
|
|
738
|
-
const patientCalendars =
|
|
739
|
-
await this.syncedCalendarsService.getPatientSyncedCalendars(patientId);
|
|
740
|
-
|
|
741
|
-
// Combine all active calendars
|
|
742
|
-
const activeCalendars = [
|
|
743
|
-
...clinicCalendars.filter((cal) => cal.isActive),
|
|
744
|
-
...practitionerCalendars.filter((cal) => cal.isActive),
|
|
745
|
-
...patientCalendars.filter((cal) => cal.isActive),
|
|
746
|
-
];
|
|
747
|
-
|
|
748
|
-
// If no active calendars, return the appointment as is
|
|
749
|
-
if (activeCalendars.length === 0) {
|
|
750
|
-
return appointment;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
// Array to store synced calendar event IDs
|
|
754
|
-
const syncedCalendarEvents: SyncedCalendarEvent[] = [];
|
|
755
|
-
const currentTime = Timestamp.now();
|
|
756
|
-
|
|
757
|
-
// Sync with each calendar
|
|
758
|
-
for (const calendar of activeCalendars) {
|
|
759
|
-
try {
|
|
760
|
-
// Sync based on provider
|
|
761
|
-
if (calendar.provider === SyncedCalendarProvider.GOOGLE) {
|
|
762
|
-
let syncResult;
|
|
763
|
-
|
|
764
|
-
// Determine which entity owns this calendar and sync accordingly
|
|
765
|
-
if (clinicCalendars.some((c) => c.id === calendar.id)) {
|
|
766
|
-
syncResult =
|
|
767
|
-
await this.syncedCalendarsService.syncClinicEventsToGoogleCalendar(
|
|
768
|
-
clinicId,
|
|
769
|
-
calendar.id,
|
|
770
|
-
[appointment]
|
|
771
|
-
);
|
|
772
|
-
} else if (practitionerCalendars.some((c) => c.id === calendar.id)) {
|
|
773
|
-
syncResult =
|
|
774
|
-
await this.syncedCalendarsService.syncPractitionerEventsToGoogleCalendar(
|
|
775
|
-
practitionerId,
|
|
776
|
-
calendar.id,
|
|
777
|
-
[appointment]
|
|
778
|
-
);
|
|
779
|
-
} else if (patientCalendars.some((c) => c.id === calendar.id)) {
|
|
780
|
-
syncResult =
|
|
781
|
-
await this.syncedCalendarsService.syncPatientEventsToGoogleCalendar(
|
|
782
|
-
patientId,
|
|
783
|
-
calendar.id,
|
|
784
|
-
[appointment]
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// If sync was successful and we have event IDs, add them to our list
|
|
789
|
-
if (syncResult && syncResult.success && syncResult.syncedEvents > 0) {
|
|
790
|
-
// Get the external event ID - this might need to be adjusted based on the actual return structure
|
|
791
|
-
const externalEventId =
|
|
792
|
-
(syncResult as any).eventIds?.[0] || appointment.id;
|
|
793
|
-
|
|
794
|
-
syncedCalendarEvents.push({
|
|
795
|
-
eventId: externalEventId,
|
|
796
|
-
syncedCalendarProvider: SyncedCalendarProvider.GOOGLE,
|
|
797
|
-
syncedAt: currentTime,
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
// Add other providers as needed (Outlook, Apple, etc.)
|
|
802
|
-
} catch (error) {
|
|
803
|
-
console.error(`Error syncing with calendar ${calendar.id}:`, error);
|
|
804
|
-
// Continue with other calendars even if one fails
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// Update the appointment with synced calendar information
|
|
809
|
-
if (syncedCalendarEvents.length > 0) {
|
|
810
|
-
return this.updateAppointment(
|
|
811
|
-
clinicId,
|
|
812
|
-
practitionerId,
|
|
813
|
-
patientId,
|
|
814
|
-
eventId,
|
|
815
|
-
{
|
|
816
|
-
syncedCalendarEventId: syncedCalendarEvents,
|
|
817
|
-
}
|
|
818
|
-
);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
return appointment;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
/**
|
|
825
|
-
* Imports events from external calendars
|
|
826
|
-
* @param entityType - Type of entity (practitioner, patient, clinic)
|
|
827
|
-
* @param entityId - ID of the entity
|
|
828
|
-
* @param startDate - Start date for fetching events
|
|
829
|
-
* @param endDate - End date for fetching events
|
|
830
|
-
* @returns Number of events imported
|
|
831
|
-
*/
|
|
832
|
-
async importEventsFromExternalCalendars(
|
|
833
|
-
entityType: "practitioner" | "patient" | "clinic",
|
|
834
|
-
entityId: string,
|
|
835
|
-
startDate: Date,
|
|
836
|
-
endDate: Date
|
|
837
|
-
): Promise<number> {
|
|
838
|
-
let syncedCalendars: any[] = [];
|
|
839
|
-
|
|
840
|
-
// Get synced calendars for the entity
|
|
841
|
-
switch (entityType) {
|
|
842
|
-
case "practitioner":
|
|
843
|
-
syncedCalendars =
|
|
844
|
-
await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
845
|
-
entityId
|
|
846
|
-
);
|
|
847
|
-
break;
|
|
848
|
-
case "patient":
|
|
849
|
-
syncedCalendars =
|
|
850
|
-
await this.syncedCalendarsService.getPatientSyncedCalendars(entityId);
|
|
851
|
-
break;
|
|
852
|
-
case "clinic":
|
|
853
|
-
syncedCalendars =
|
|
854
|
-
await this.syncedCalendarsService.getClinicSyncedCalendars(entityId);
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// Filter active calendars
|
|
859
|
-
const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
|
|
860
|
-
|
|
861
|
-
if (activeCalendars.length === 0) {
|
|
862
|
-
return 0;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
let importedEventsCount = 0;
|
|
866
|
-
const currentTime = Timestamp.now();
|
|
867
|
-
|
|
868
|
-
// Import from each calendar
|
|
869
|
-
for (const calendar of activeCalendars) {
|
|
870
|
-
try {
|
|
871
|
-
let externalEvents: any[] = [];
|
|
872
|
-
|
|
873
|
-
// Fetch events based on provider and entity type
|
|
874
|
-
if (calendar.provider === SyncedCalendarProvider.GOOGLE) {
|
|
875
|
-
switch (entityType) {
|
|
876
|
-
case "practitioner":
|
|
877
|
-
externalEvents =
|
|
878
|
-
await this.syncedCalendarsService.fetchEventsFromPractitionerGoogleCalendar(
|
|
879
|
-
entityId,
|
|
880
|
-
calendar.id,
|
|
881
|
-
startDate,
|
|
882
|
-
endDate
|
|
883
|
-
);
|
|
884
|
-
break;
|
|
885
|
-
case "patient":
|
|
886
|
-
externalEvents =
|
|
887
|
-
await this.syncedCalendarsService.fetchEventsFromPatientGoogleCalendar(
|
|
888
|
-
entityId,
|
|
889
|
-
calendar.id,
|
|
890
|
-
startDate,
|
|
891
|
-
endDate
|
|
892
|
-
);
|
|
893
|
-
break;
|
|
894
|
-
case "clinic":
|
|
895
|
-
externalEvents =
|
|
896
|
-
await this.syncedCalendarsService.fetchEventsFromClinicGoogleCalendar(
|
|
897
|
-
entityId,
|
|
898
|
-
calendar.id,
|
|
899
|
-
startDate,
|
|
900
|
-
endDate
|
|
901
|
-
);
|
|
902
|
-
break;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
// Add other providers as needed
|
|
906
|
-
|
|
907
|
-
// Process and import each event
|
|
908
|
-
for (const externalEvent of externalEvents) {
|
|
909
|
-
try {
|
|
910
|
-
// Create event data from external event
|
|
911
|
-
const eventData: Omit<
|
|
912
|
-
CreateCalendarEventData,
|
|
913
|
-
"id" | "createdAt" | "updatedAt"
|
|
914
|
-
> = {
|
|
915
|
-
eventName: externalEvent.summary || "Imported Event",
|
|
916
|
-
eventTime: {
|
|
917
|
-
start: Timestamp.fromDate(
|
|
918
|
-
new Date(
|
|
919
|
-
externalEvent.start.dateTime || externalEvent.start.date
|
|
920
|
-
)
|
|
921
|
-
),
|
|
922
|
-
end: Timestamp.fromDate(
|
|
923
|
-
new Date(externalEvent.end.dateTime || externalEvent.end.date)
|
|
924
|
-
),
|
|
925
|
-
},
|
|
926
|
-
description: externalEvent.description || "",
|
|
927
|
-
status: CalendarEventStatus.CONFIRMED,
|
|
928
|
-
// Set sync status to EXTERNAL as this is imported from an external calendar
|
|
929
|
-
syncStatus: CalendarSyncStatus.EXTERNAL,
|
|
930
|
-
eventType: CalendarEventType.BLOCKING, // Use BLOCKING type for external events
|
|
931
|
-
syncedCalendarEventId: [
|
|
932
|
-
{
|
|
933
|
-
eventId: externalEvent.id,
|
|
934
|
-
syncedCalendarProvider: calendar.provider,
|
|
935
|
-
syncedAt: currentTime,
|
|
936
|
-
},
|
|
937
|
-
],
|
|
938
|
-
};
|
|
939
|
-
|
|
940
|
-
// Create the event in our system
|
|
941
|
-
switch (entityType) {
|
|
942
|
-
case "practitioner":
|
|
943
|
-
await this.createPractitionerCalendarEvent(entityId, eventData);
|
|
944
|
-
break;
|
|
945
|
-
case "patient":
|
|
946
|
-
await this.createPatientCalendarEvent(entityId, eventData);
|
|
947
|
-
break;
|
|
948
|
-
case "clinic":
|
|
949
|
-
await this.createClinicCalendarEvent(entityId, eventData);
|
|
950
|
-
break;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
importedEventsCount++;
|
|
954
|
-
} catch (eventError) {
|
|
955
|
-
console.error("Error importing event:", eventError);
|
|
956
|
-
// Continue with other events even if one fails
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
} catch (calendarError) {
|
|
960
|
-
console.error(
|
|
961
|
-
`Error fetching events from calendar ${calendar.id}:`,
|
|
962
|
-
calendarError
|
|
963
|
-
);
|
|
964
|
-
// Continue with other calendars even if one fails
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
return importedEventsCount;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* Deletes an event from external calendars
|
|
973
|
-
* @param event - Calendar event to delete
|
|
974
|
-
* @param entityType - Type of entity (practitioner, patient, clinic)
|
|
975
|
-
* @param entityId - ID of the entity
|
|
976
|
-
* @returns Success status
|
|
977
|
-
*/
|
|
978
|
-
async deleteEventFromExternalCalendars(
|
|
979
|
-
event: CalendarEvent,
|
|
980
|
-
entityType: "practitioner" | "patient" | "clinic",
|
|
981
|
-
entityId: string
|
|
982
|
-
): Promise<boolean> {
|
|
983
|
-
// If the event doesn't have synced calendar event IDs, return success
|
|
984
|
-
if (
|
|
985
|
-
!event.syncedCalendarEventId ||
|
|
986
|
-
event.syncedCalendarEventId.length === 0
|
|
987
|
-
) {
|
|
988
|
-
return true;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// Get synced calendars for the entity
|
|
992
|
-
let syncedCalendars: any[] = [];
|
|
993
|
-
switch (entityType) {
|
|
994
|
-
case "practitioner":
|
|
995
|
-
syncedCalendars =
|
|
996
|
-
await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
997
|
-
entityId
|
|
998
|
-
);
|
|
999
|
-
break;
|
|
1000
|
-
case "patient":
|
|
1001
|
-
syncedCalendars =
|
|
1002
|
-
await this.syncedCalendarsService.getPatientSyncedCalendars(entityId);
|
|
1003
|
-
break;
|
|
1004
|
-
case "clinic":
|
|
1005
|
-
syncedCalendars =
|
|
1006
|
-
await this.syncedCalendarsService.getClinicSyncedCalendars(entityId);
|
|
1007
|
-
break;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
// Filter active calendars
|
|
1011
|
-
const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
|
|
1012
|
-
|
|
1013
|
-
if (activeCalendars.length === 0) {
|
|
1014
|
-
return true;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
let allSuccessful = true;
|
|
1018
|
-
|
|
1019
|
-
// Delete from each external calendar
|
|
1020
|
-
for (const syncedEvent of event.syncedCalendarEventId) {
|
|
1021
|
-
try {
|
|
1022
|
-
// Find the calendar for this provider
|
|
1023
|
-
const calendar = activeCalendars.find(
|
|
1024
|
-
(cal) => cal.provider === syncedEvent.syncedCalendarProvider
|
|
1025
|
-
);
|
|
1026
|
-
|
|
1027
|
-
if (!calendar) {
|
|
1028
|
-
continue;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
// Delete based on provider and entity type
|
|
1032
|
-
if (
|
|
1033
|
-
syncedEvent.syncedCalendarProvider === SyncedCalendarProvider.GOOGLE
|
|
1034
|
-
) {
|
|
1035
|
-
let success = false;
|
|
1036
|
-
|
|
1037
|
-
switch (entityType) {
|
|
1038
|
-
case "practitioner":
|
|
1039
|
-
success =
|
|
1040
|
-
await this.syncedCalendarsService.deletePractitionerGoogleCalendarEvent(
|
|
1041
|
-
entityId,
|
|
1042
|
-
calendar.id,
|
|
1043
|
-
syncedEvent.eventId
|
|
1044
|
-
);
|
|
1045
|
-
break;
|
|
1046
|
-
case "patient":
|
|
1047
|
-
success =
|
|
1048
|
-
await this.syncedCalendarsService.deletePatientGoogleCalendarEvent(
|
|
1049
|
-
entityId,
|
|
1050
|
-
calendar.id,
|
|
1051
|
-
syncedEvent.eventId
|
|
1052
|
-
);
|
|
1053
|
-
break;
|
|
1054
|
-
case "clinic":
|
|
1055
|
-
success =
|
|
1056
|
-
await this.syncedCalendarsService.deleteClinicGoogleCalendarEvent(
|
|
1057
|
-
entityId,
|
|
1058
|
-
calendar.id,
|
|
1059
|
-
syncedEvent.eventId
|
|
1060
|
-
);
|
|
1061
|
-
break;
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
if (!success) {
|
|
1065
|
-
allSuccessful = false;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
// Add other providers as needed
|
|
1069
|
-
} catch (error) {
|
|
1070
|
-
console.error(`Error deleting event from external calendar:`, error);
|
|
1071
|
-
allSuccessful = false;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
return allSuccessful;
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1
|
+
import { Auth } from "firebase/auth";
|
|
2
|
+
import { Firestore, Timestamp } from "firebase/firestore";
|
|
3
|
+
import { FirebaseApp } from "firebase/app";
|
|
4
|
+
import { BaseService } from "../base.service";
|
|
5
|
+
import {
|
|
6
|
+
CalendarEvent,
|
|
7
|
+
CalendarEventStatus,
|
|
8
|
+
CalendarEventTime,
|
|
9
|
+
CalendarEventType,
|
|
10
|
+
CalendarSyncStatus,
|
|
11
|
+
CreateCalendarEventData,
|
|
12
|
+
UpdateCalendarEventData,
|
|
13
|
+
CALENDAR_COLLECTION,
|
|
14
|
+
SyncedCalendarEvent,
|
|
15
|
+
} from "../../types/calendar";
|
|
16
|
+
import { PRACTITIONERS_COLLECTION } from "../../types/practitioner";
|
|
17
|
+
import { PATIENTS_COLLECTION } from "../../types/patient";
|
|
18
|
+
import { CLINICS_COLLECTION } from "../../types/clinic";
|
|
19
|
+
import { SyncedCalendarProvider } from "../../types/calendar/synced-calendar.types";
|
|
20
|
+
|
|
21
|
+
// Import utility functions from the index file
|
|
22
|
+
import {
|
|
23
|
+
createPractitionerCalendarEventUtil,
|
|
24
|
+
createPatientCalendarEventUtil,
|
|
25
|
+
createClinicCalendarEventUtil,
|
|
26
|
+
getPractitionerCalendarEventUtil,
|
|
27
|
+
getPatientCalendarEventUtil,
|
|
28
|
+
getClinicCalendarEventUtil,
|
|
29
|
+
getPractitionerCalendarEventsUtil,
|
|
30
|
+
getPatientCalendarEventsUtil,
|
|
31
|
+
getClinicCalendarEventsUtil,
|
|
32
|
+
updatePractitionerCalendarEventUtil,
|
|
33
|
+
updatePatientCalendarEventUtil,
|
|
34
|
+
updateClinicCalendarEventUtil,
|
|
35
|
+
deletePractitionerCalendarEventUtil,
|
|
36
|
+
deletePatientCalendarEventUtil,
|
|
37
|
+
deleteClinicCalendarEventUtil,
|
|
38
|
+
} from "./utils";
|
|
39
|
+
|
|
40
|
+
// Import the SyncedCalendarsService for calendar synchronization
|
|
41
|
+
import { SyncedCalendarsService } from "./synced-calendars.service";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Service for managing calendar events
|
|
45
|
+
*/
|
|
46
|
+
export class CalendarService extends BaseService {
|
|
47
|
+
private syncedCalendarsService: SyncedCalendarsService;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a new CalendarService instance
|
|
51
|
+
* @param db - Firestore instance
|
|
52
|
+
* @param auth - Firebase Auth instance
|
|
53
|
+
* @param app - Firebase App instance
|
|
54
|
+
*/
|
|
55
|
+
constructor(db: Firestore, auth: Auth, app: FirebaseApp) {
|
|
56
|
+
super(db, auth, app);
|
|
57
|
+
this.syncedCalendarsService = new SyncedCalendarsService(db, auth, app);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a calendar event for a practitioner
|
|
62
|
+
* @param practitionerId - ID of the practitioner
|
|
63
|
+
* @param eventData - Calendar event data
|
|
64
|
+
* @returns Created calendar event
|
|
65
|
+
*/
|
|
66
|
+
async createPractitionerCalendarEvent(
|
|
67
|
+
practitionerId: string,
|
|
68
|
+
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">
|
|
69
|
+
): Promise<CalendarEvent> {
|
|
70
|
+
return createPractitionerCalendarEventUtil(
|
|
71
|
+
this.db,
|
|
72
|
+
practitionerId,
|
|
73
|
+
eventData,
|
|
74
|
+
this.generateId.bind(this)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Creates a calendar event for a patient
|
|
80
|
+
* @param patientId - ID of the patient
|
|
81
|
+
* @param eventData - Calendar event data
|
|
82
|
+
* @returns Created calendar event
|
|
83
|
+
*/
|
|
84
|
+
async createPatientCalendarEvent(
|
|
85
|
+
patientId: string,
|
|
86
|
+
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">
|
|
87
|
+
): Promise<CalendarEvent> {
|
|
88
|
+
return createPatientCalendarEventUtil(
|
|
89
|
+
this.db,
|
|
90
|
+
patientId,
|
|
91
|
+
eventData,
|
|
92
|
+
this.generateId.bind(this)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Creates a calendar event for a clinic
|
|
98
|
+
* @param clinicId - ID of the clinic
|
|
99
|
+
* @param eventData - Calendar event data
|
|
100
|
+
* @returns Created calendar event
|
|
101
|
+
*/
|
|
102
|
+
async createClinicCalendarEvent(
|
|
103
|
+
clinicId: string,
|
|
104
|
+
eventData: Omit<CreateCalendarEventData, "id" | "createdAt" | "updatedAt">
|
|
105
|
+
): Promise<CalendarEvent> {
|
|
106
|
+
return createClinicCalendarEventUtil(
|
|
107
|
+
this.db,
|
|
108
|
+
clinicId,
|
|
109
|
+
eventData,
|
|
110
|
+
this.generateId.bind(this)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Creates an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
116
|
+
* @param clinicId - ID of the clinic
|
|
117
|
+
* @param practitionerId - ID of the practitioner
|
|
118
|
+
* @param patientId - ID of the patient
|
|
119
|
+
* @param eventData - Calendar event data
|
|
120
|
+
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
121
|
+
* @returns Created calendar event
|
|
122
|
+
*/
|
|
123
|
+
async createAppointment(
|
|
124
|
+
clinicId: string,
|
|
125
|
+
practitionerId: string,
|
|
126
|
+
patientId: string,
|
|
127
|
+
eventData: Omit<
|
|
128
|
+
CreateCalendarEventData,
|
|
129
|
+
| "id"
|
|
130
|
+
| "createdAt"
|
|
131
|
+
| "updatedAt"
|
|
132
|
+
| "clinicBranchId"
|
|
133
|
+
| "practitionerProfileId"
|
|
134
|
+
| "patientProfileId"
|
|
135
|
+
>,
|
|
136
|
+
syncWithExternalCalendars: boolean = false
|
|
137
|
+
): Promise<CalendarEvent> {
|
|
138
|
+
// Generate a single ID to be used across all calendars
|
|
139
|
+
const eventId = this.generateId();
|
|
140
|
+
|
|
141
|
+
// Prepare the event data with all required IDs
|
|
142
|
+
const appointmentData: Omit<
|
|
143
|
+
CreateCalendarEventData,
|
|
144
|
+
"id" | "createdAt" | "updatedAt"
|
|
145
|
+
> = {
|
|
146
|
+
...eventData,
|
|
147
|
+
clinicBranchId: clinicId,
|
|
148
|
+
practitionerProfileId: practitionerId,
|
|
149
|
+
patientProfileId: patientId,
|
|
150
|
+
eventType: CalendarEventType.APPOINTMENT,
|
|
151
|
+
// Set sync status to INTERNAL as this is created in the app ecosystem
|
|
152
|
+
syncStatus: CalendarSyncStatus.INTERNAL,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Create the event in all three calendars
|
|
156
|
+
const clinicPromise = this.createClinicCalendarEvent(
|
|
157
|
+
clinicId,
|
|
158
|
+
appointmentData
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const practitionerPromise = this.createPractitionerCalendarEvent(
|
|
162
|
+
practitionerId,
|
|
163
|
+
appointmentData
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const patientPromise = this.createPatientCalendarEvent(
|
|
167
|
+
patientId,
|
|
168
|
+
appointmentData
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Wait for all operations to complete
|
|
172
|
+
const [clinicEvent] = await Promise.all([
|
|
173
|
+
clinicPromise,
|
|
174
|
+
practitionerPromise,
|
|
175
|
+
patientPromise,
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
// Sync with external calendars if requested
|
|
179
|
+
if (syncWithExternalCalendars) {
|
|
180
|
+
try {
|
|
181
|
+
await this.syncAppointmentWithExternalCalendars(
|
|
182
|
+
clinicId,
|
|
183
|
+
practitionerId,
|
|
184
|
+
patientId,
|
|
185
|
+
clinicEvent.id
|
|
186
|
+
);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(
|
|
189
|
+
"Error syncing appointment with external calendars:",
|
|
190
|
+
error
|
|
191
|
+
);
|
|
192
|
+
// Continue even if sync fails
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Return the event from the clinic calendar
|
|
197
|
+
return clinicEvent;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Gets a calendar event for a practitioner
|
|
202
|
+
* @param practitionerId - ID of the practitioner
|
|
203
|
+
* @param eventId - ID of the event
|
|
204
|
+
* @returns Calendar event or null if not found
|
|
205
|
+
*/
|
|
206
|
+
async getPractitionerCalendarEvent(
|
|
207
|
+
practitionerId: string,
|
|
208
|
+
eventId: string
|
|
209
|
+
): Promise<CalendarEvent | null> {
|
|
210
|
+
return getPractitionerCalendarEventUtil(this.db, practitionerId, eventId);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Gets a calendar event for a patient
|
|
215
|
+
* @param patientId - ID of the patient
|
|
216
|
+
* @param eventId - ID of the event
|
|
217
|
+
* @returns Calendar event or null if not found
|
|
218
|
+
*/
|
|
219
|
+
async getPatientCalendarEvent(
|
|
220
|
+
patientId: string,
|
|
221
|
+
eventId: string
|
|
222
|
+
): Promise<CalendarEvent | null> {
|
|
223
|
+
return getPatientCalendarEventUtil(this.db, patientId, eventId);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Gets a calendar event for a clinic
|
|
228
|
+
* @param clinicId - ID of the clinic
|
|
229
|
+
* @param eventId - ID of the event
|
|
230
|
+
* @returns Calendar event or null if not found
|
|
231
|
+
*/
|
|
232
|
+
async getClinicCalendarEvent(
|
|
233
|
+
clinicId: string,
|
|
234
|
+
eventId: string
|
|
235
|
+
): Promise<CalendarEvent | null> {
|
|
236
|
+
return getClinicCalendarEventUtil(this.db, clinicId, eventId);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Gets calendar events for a practitioner within a date range
|
|
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
|
+
async getPractitionerCalendarEvents(
|
|
247
|
+
practitionerId: string,
|
|
248
|
+
startDate: Date,
|
|
249
|
+
endDate: Date
|
|
250
|
+
): Promise<CalendarEvent[]> {
|
|
251
|
+
return getPractitionerCalendarEventsUtil(
|
|
252
|
+
this.db,
|
|
253
|
+
practitionerId,
|
|
254
|
+
startDate,
|
|
255
|
+
endDate
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Gets calendar events for a patient within a date range
|
|
261
|
+
* @param patientId - ID of the patient
|
|
262
|
+
* @param startDate - Start date of the range
|
|
263
|
+
* @param endDate - End date of the range
|
|
264
|
+
* @returns Array of calendar events
|
|
265
|
+
*/
|
|
266
|
+
async getPatientCalendarEvents(
|
|
267
|
+
patientId: string,
|
|
268
|
+
startDate: Date,
|
|
269
|
+
endDate: Date
|
|
270
|
+
): Promise<CalendarEvent[]> {
|
|
271
|
+
return getPatientCalendarEventsUtil(this.db, patientId, startDate, endDate);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Gets calendar events for a clinic within a date range
|
|
276
|
+
* @param clinicId - ID of the clinic
|
|
277
|
+
* @param startDate - Start date of the range
|
|
278
|
+
* @param endDate - End date of the range
|
|
279
|
+
* @returns Array of calendar events
|
|
280
|
+
*/
|
|
281
|
+
async getClinicCalendarEvents(
|
|
282
|
+
clinicId: string,
|
|
283
|
+
startDate: Date,
|
|
284
|
+
endDate: Date
|
|
285
|
+
): Promise<CalendarEvent[]> {
|
|
286
|
+
return getClinicCalendarEventsUtil(this.db, clinicId, startDate, endDate);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Updates a calendar event for a practitioner
|
|
291
|
+
* @param practitionerId - ID of the practitioner
|
|
292
|
+
* @param eventId - ID of the event
|
|
293
|
+
* @param updateData - Data to update
|
|
294
|
+
* @returns Updated calendar event
|
|
295
|
+
*/
|
|
296
|
+
async updatePractitionerCalendarEvent(
|
|
297
|
+
practitionerId: string,
|
|
298
|
+
eventId: string,
|
|
299
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
300
|
+
): Promise<CalendarEvent> {
|
|
301
|
+
return updatePractitionerCalendarEventUtil(
|
|
302
|
+
this.db,
|
|
303
|
+
practitionerId,
|
|
304
|
+
eventId,
|
|
305
|
+
updateData
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Updates a calendar event for a patient
|
|
311
|
+
* @param patientId - ID of the patient
|
|
312
|
+
* @param eventId - ID of the event
|
|
313
|
+
* @param updateData - Data to update
|
|
314
|
+
* @returns Updated calendar event
|
|
315
|
+
*/
|
|
316
|
+
async updatePatientCalendarEvent(
|
|
317
|
+
patientId: string,
|
|
318
|
+
eventId: string,
|
|
319
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
320
|
+
): Promise<CalendarEvent> {
|
|
321
|
+
return updatePatientCalendarEventUtil(
|
|
322
|
+
this.db,
|
|
323
|
+
patientId,
|
|
324
|
+
eventId,
|
|
325
|
+
updateData
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Updates a calendar event for a clinic
|
|
331
|
+
* @param clinicId - ID of the clinic
|
|
332
|
+
* @param eventId - ID of the event
|
|
333
|
+
* @param updateData - Data to update
|
|
334
|
+
* @returns Updated calendar event
|
|
335
|
+
*/
|
|
336
|
+
async updateClinicCalendarEvent(
|
|
337
|
+
clinicId: string,
|
|
338
|
+
eventId: string,
|
|
339
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">
|
|
340
|
+
): Promise<CalendarEvent> {
|
|
341
|
+
return updateClinicCalendarEventUtil(
|
|
342
|
+
this.db,
|
|
343
|
+
clinicId,
|
|
344
|
+
eventId,
|
|
345
|
+
updateData
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Updates an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
351
|
+
* @param clinicId - ID of the clinic
|
|
352
|
+
* @param practitionerId - ID of the practitioner
|
|
353
|
+
* @param patientId - ID of the patient
|
|
354
|
+
* @param eventId - ID of the event
|
|
355
|
+
* @param updateData - Data to update
|
|
356
|
+
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
357
|
+
* @returns Updated calendar event
|
|
358
|
+
*/
|
|
359
|
+
async updateAppointment(
|
|
360
|
+
clinicId: string,
|
|
361
|
+
practitionerId: string,
|
|
362
|
+
patientId: string,
|
|
363
|
+
eventId: string,
|
|
364
|
+
updateData: Omit<UpdateCalendarEventData, "updatedAt">,
|
|
365
|
+
syncWithExternalCalendars: boolean = false
|
|
366
|
+
): Promise<CalendarEvent> {
|
|
367
|
+
// Update the event in all three calendars
|
|
368
|
+
const clinicPromise = this.updateClinicCalendarEvent(
|
|
369
|
+
clinicId,
|
|
370
|
+
eventId,
|
|
371
|
+
updateData
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const practitionerPromise = this.updatePractitionerCalendarEvent(
|
|
375
|
+
practitionerId,
|
|
376
|
+
eventId,
|
|
377
|
+
updateData
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const patientPromise = this.updatePatientCalendarEvent(
|
|
381
|
+
patientId,
|
|
382
|
+
eventId,
|
|
383
|
+
updateData
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
// Wait for all operations to complete
|
|
387
|
+
const [clinicEvent] = await Promise.all([
|
|
388
|
+
clinicPromise,
|
|
389
|
+
practitionerPromise,
|
|
390
|
+
patientPromise,
|
|
391
|
+
]);
|
|
392
|
+
|
|
393
|
+
// Sync with external calendars if requested
|
|
394
|
+
if (syncWithExternalCalendars) {
|
|
395
|
+
try {
|
|
396
|
+
await this.syncAppointmentWithExternalCalendars(
|
|
397
|
+
clinicId,
|
|
398
|
+
practitionerId,
|
|
399
|
+
patientId,
|
|
400
|
+
eventId
|
|
401
|
+
);
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error(
|
|
404
|
+
"Error syncing appointment with external calendars:",
|
|
405
|
+
error
|
|
406
|
+
);
|
|
407
|
+
// Continue even if sync fails
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Return the event from the clinic calendar
|
|
412
|
+
return clinicEvent;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Deletes a calendar event for a practitioner
|
|
417
|
+
* @param practitionerId - ID of the practitioner
|
|
418
|
+
* @param eventId - ID of the event
|
|
419
|
+
*/
|
|
420
|
+
async deletePractitionerCalendarEvent(
|
|
421
|
+
practitionerId: string,
|
|
422
|
+
eventId: string
|
|
423
|
+
): Promise<void> {
|
|
424
|
+
return deletePractitionerCalendarEventUtil(
|
|
425
|
+
this.db,
|
|
426
|
+
practitionerId,
|
|
427
|
+
eventId
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Deletes a calendar event for a patient
|
|
433
|
+
* @param patientId - ID of the patient
|
|
434
|
+
* @param eventId - ID of the event
|
|
435
|
+
*/
|
|
436
|
+
async deletePatientCalendarEvent(
|
|
437
|
+
patientId: string,
|
|
438
|
+
eventId: string
|
|
439
|
+
): Promise<void> {
|
|
440
|
+
return deletePatientCalendarEventUtil(this.db, patientId, eventId);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Deletes a calendar event for a clinic
|
|
445
|
+
* @param clinicId - ID of the clinic
|
|
446
|
+
* @param eventId - ID of the event
|
|
447
|
+
*/
|
|
448
|
+
async deleteClinicCalendarEvent(
|
|
449
|
+
clinicId: string,
|
|
450
|
+
eventId: string
|
|
451
|
+
): Promise<void> {
|
|
452
|
+
return deleteClinicCalendarEventUtil(this.db, clinicId, eventId);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Deletes an appointment across all relevant calendars (practitioner, patient, clinic)
|
|
457
|
+
* @param clinicId - ID of the clinic
|
|
458
|
+
* @param practitionerId - ID of the practitioner
|
|
459
|
+
* @param patientId - ID of the patient
|
|
460
|
+
* @param eventId - ID of the event
|
|
461
|
+
*/
|
|
462
|
+
async deleteAppointment(
|
|
463
|
+
clinicId: string,
|
|
464
|
+
practitionerId: string,
|
|
465
|
+
patientId: string,
|
|
466
|
+
eventId: string
|
|
467
|
+
): Promise<void> {
|
|
468
|
+
// First get the appointment to check if it's synced with external calendars
|
|
469
|
+
const appointment = await this.getClinicCalendarEvent(clinicId, eventId);
|
|
470
|
+
|
|
471
|
+
if (appointment && appointment.syncStatus === CalendarSyncStatus.EXTERNAL) {
|
|
472
|
+
// Delete from external calendars
|
|
473
|
+
await this.deleteEventFromExternalCalendars(
|
|
474
|
+
appointment,
|
|
475
|
+
"clinic",
|
|
476
|
+
clinicId
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
// Also try to delete from practitioner and patient external calendars
|
|
480
|
+
const practitionerEvent = await this.getPractitionerCalendarEvent(
|
|
481
|
+
practitionerId,
|
|
482
|
+
eventId
|
|
483
|
+
);
|
|
484
|
+
if (practitionerEvent) {
|
|
485
|
+
await this.deleteEventFromExternalCalendars(
|
|
486
|
+
practitionerEvent,
|
|
487
|
+
"practitioner",
|
|
488
|
+
practitionerId
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const patientEvent = await this.getPatientCalendarEvent(
|
|
493
|
+
patientId,
|
|
494
|
+
eventId
|
|
495
|
+
);
|
|
496
|
+
if (patientEvent) {
|
|
497
|
+
await this.deleteEventFromExternalCalendars(
|
|
498
|
+
patientEvent,
|
|
499
|
+
"patient",
|
|
500
|
+
patientId
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Delete the event from all three calendars
|
|
506
|
+
const clinicPromise = this.deleteClinicCalendarEvent(clinicId, eventId);
|
|
507
|
+
|
|
508
|
+
const practitionerPromise = this.deletePractitionerCalendarEvent(
|
|
509
|
+
practitionerId,
|
|
510
|
+
eventId
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
const patientPromise = this.deletePatientCalendarEvent(patientId, eventId);
|
|
514
|
+
|
|
515
|
+
// Wait for all operations to complete
|
|
516
|
+
await Promise.all([clinicPromise, practitionerPromise, patientPromise]);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Gets pending appointments for a clinic
|
|
521
|
+
* @param clinicId - ID of the clinic
|
|
522
|
+
* @returns Array of pending calendar events
|
|
523
|
+
*/
|
|
524
|
+
async getPendingClinicAppointments(
|
|
525
|
+
clinicId: string
|
|
526
|
+
): Promise<CalendarEvent[]> {
|
|
527
|
+
const events = await getClinicCalendarEventsUtil(
|
|
528
|
+
this.db,
|
|
529
|
+
clinicId,
|
|
530
|
+
new Date(0), // From the beginning of time
|
|
531
|
+
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // To one year from now
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
return events.filter(
|
|
535
|
+
(event: CalendarEvent) =>
|
|
536
|
+
event.status === CalendarEventStatus.PENDING &&
|
|
537
|
+
event.eventType === CalendarEventType.APPOINTMENT
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Gets confirmed appointments for a practitioner
|
|
543
|
+
* @param practitionerId - ID of the practitioner
|
|
544
|
+
* @returns Array of confirmed calendar events
|
|
545
|
+
*/
|
|
546
|
+
async getConfirmedPractitionerAppointments(
|
|
547
|
+
practitionerId: string
|
|
548
|
+
): Promise<CalendarEvent[]> {
|
|
549
|
+
const events = await getPractitionerCalendarEventsUtil(
|
|
550
|
+
this.db,
|
|
551
|
+
practitionerId,
|
|
552
|
+
new Date(0), // From the beginning of time
|
|
553
|
+
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // To one year from now
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
return events.filter(
|
|
557
|
+
(event: CalendarEvent) =>
|
|
558
|
+
event.status === CalendarEventStatus.CONFIRMED &&
|
|
559
|
+
event.eventType === CalendarEventType.APPOINTMENT
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Gets upcoming appointments for a patient
|
|
565
|
+
* @param patientId - ID of the patient
|
|
566
|
+
* @returns Array of upcoming calendar events
|
|
567
|
+
*/
|
|
568
|
+
async getUpcomingPatientAppointments(
|
|
569
|
+
patientId: string
|
|
570
|
+
): Promise<CalendarEvent[]> {
|
|
571
|
+
const now = new Date();
|
|
572
|
+
const events = await getPatientCalendarEventsUtil(
|
|
573
|
+
this.db,
|
|
574
|
+
patientId,
|
|
575
|
+
now,
|
|
576
|
+
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // To one year from now
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
return events.filter(
|
|
580
|
+
(event: CalendarEvent) =>
|
|
581
|
+
event.eventType === CalendarEventType.APPOINTMENT
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Confirms an appointment
|
|
587
|
+
* @param clinicId - ID of the clinic
|
|
588
|
+
* @param practitionerId - ID of the practitioner
|
|
589
|
+
* @param patientId - ID of the patient
|
|
590
|
+
* @param eventId - ID of the event
|
|
591
|
+
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
592
|
+
* @returns Confirmed calendar event
|
|
593
|
+
*/
|
|
594
|
+
async confirmAppointment(
|
|
595
|
+
clinicId: string,
|
|
596
|
+
practitionerId: string,
|
|
597
|
+
patientId: string,
|
|
598
|
+
eventId: string,
|
|
599
|
+
syncWithExternalCalendars: boolean = true
|
|
600
|
+
): Promise<CalendarEvent> {
|
|
601
|
+
return this.updateAppointment(
|
|
602
|
+
clinicId,
|
|
603
|
+
practitionerId,
|
|
604
|
+
patientId,
|
|
605
|
+
eventId,
|
|
606
|
+
{ status: CalendarEventStatus.CONFIRMED },
|
|
607
|
+
syncWithExternalCalendars
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Rejects an appointment
|
|
613
|
+
* @param clinicId - ID of the clinic
|
|
614
|
+
* @param practitionerId - ID of the practitioner
|
|
615
|
+
* @param patientId - ID of the patient
|
|
616
|
+
* @param eventId - ID of the event
|
|
617
|
+
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
618
|
+
* @returns Rejected calendar event
|
|
619
|
+
*/
|
|
620
|
+
async rejectAppointment(
|
|
621
|
+
clinicId: string,
|
|
622
|
+
practitionerId: string,
|
|
623
|
+
patientId: string,
|
|
624
|
+
eventId: string,
|
|
625
|
+
syncWithExternalCalendars: boolean = true
|
|
626
|
+
): Promise<CalendarEvent> {
|
|
627
|
+
return this.updateAppointment(
|
|
628
|
+
clinicId,
|
|
629
|
+
practitionerId,
|
|
630
|
+
patientId,
|
|
631
|
+
eventId,
|
|
632
|
+
{ status: CalendarEventStatus.REJECTED },
|
|
633
|
+
syncWithExternalCalendars
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Cancels an appointment
|
|
639
|
+
* @param clinicId - ID of the clinic
|
|
640
|
+
* @param practitionerId - ID of the practitioner
|
|
641
|
+
* @param patientId - ID of the patient
|
|
642
|
+
* @param eventId - ID of the event
|
|
643
|
+
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
644
|
+
* @returns Canceled calendar event
|
|
645
|
+
*/
|
|
646
|
+
async cancelAppointment(
|
|
647
|
+
clinicId: string,
|
|
648
|
+
practitionerId: string,
|
|
649
|
+
patientId: string,
|
|
650
|
+
eventId: string,
|
|
651
|
+
syncWithExternalCalendars: boolean = true
|
|
652
|
+
): Promise<CalendarEvent> {
|
|
653
|
+
return this.updateAppointment(
|
|
654
|
+
clinicId,
|
|
655
|
+
practitionerId,
|
|
656
|
+
patientId,
|
|
657
|
+
eventId,
|
|
658
|
+
{ status: CalendarEventStatus.CANCELED },
|
|
659
|
+
syncWithExternalCalendars
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Reschedules an appointment
|
|
665
|
+
* @param clinicId - ID of the clinic
|
|
666
|
+
* @param practitionerId - ID of the practitioner
|
|
667
|
+
* @param patientId - ID of the patient
|
|
668
|
+
* @param eventId - ID of the event
|
|
669
|
+
* @param newEventTime - New event time
|
|
670
|
+
* @param syncWithExternalCalendars - Whether to sync with external calendars
|
|
671
|
+
* @returns Rescheduled calendar event
|
|
672
|
+
*/
|
|
673
|
+
async rescheduleAppointment(
|
|
674
|
+
clinicId: string,
|
|
675
|
+
practitionerId: string,
|
|
676
|
+
patientId: string,
|
|
677
|
+
eventId: string,
|
|
678
|
+
newEventTime: CalendarEventTime,
|
|
679
|
+
syncWithExternalCalendars: boolean = true
|
|
680
|
+
): Promise<CalendarEvent> {
|
|
681
|
+
// First get the current event to preserve any other fields
|
|
682
|
+
const currentEvent = await this.getClinicCalendarEvent(clinicId, eventId);
|
|
683
|
+
|
|
684
|
+
if (!currentEvent) {
|
|
685
|
+
throw new Error(`Appointment with ID ${eventId} not found`);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Update the appointment with new time and status
|
|
689
|
+
return this.updateAppointment(
|
|
690
|
+
clinicId,
|
|
691
|
+
practitionerId,
|
|
692
|
+
patientId,
|
|
693
|
+
eventId,
|
|
694
|
+
{
|
|
695
|
+
status: CalendarEventStatus.RESCHEDULED,
|
|
696
|
+
eventTime: newEventTime,
|
|
697
|
+
},
|
|
698
|
+
syncWithExternalCalendars
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Gets the SyncedCalendarsService for managing synced calendars
|
|
704
|
+
* @returns SyncedCalendarsService instance
|
|
705
|
+
*/
|
|
706
|
+
getSyncedCalendarsService(): SyncedCalendarsService {
|
|
707
|
+
return this.syncedCalendarsService;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Syncs an appointment with external calendars
|
|
712
|
+
* @param clinicId - ID of the clinic
|
|
713
|
+
* @param practitionerId - ID of the practitioner
|
|
714
|
+
* @param patientId - ID of the patient
|
|
715
|
+
* @param eventId - ID of the event
|
|
716
|
+
* @returns Updated calendar event with sync information
|
|
717
|
+
*/
|
|
718
|
+
async syncAppointmentWithExternalCalendars(
|
|
719
|
+
clinicId: string,
|
|
720
|
+
practitionerId: string,
|
|
721
|
+
patientId: string,
|
|
722
|
+
eventId: string
|
|
723
|
+
): Promise<CalendarEvent> {
|
|
724
|
+
// Get the appointment
|
|
725
|
+
const appointment = await this.getClinicCalendarEvent(clinicId, eventId);
|
|
726
|
+
|
|
727
|
+
if (!appointment) {
|
|
728
|
+
throw new Error(`Appointment with ID ${eventId} not found`);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Get synced calendars for all entities
|
|
732
|
+
const clinicCalendars =
|
|
733
|
+
await this.syncedCalendarsService.getClinicSyncedCalendars(clinicId);
|
|
734
|
+
const practitionerCalendars =
|
|
735
|
+
await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
736
|
+
practitionerId
|
|
737
|
+
);
|
|
738
|
+
const patientCalendars =
|
|
739
|
+
await this.syncedCalendarsService.getPatientSyncedCalendars(patientId);
|
|
740
|
+
|
|
741
|
+
// Combine all active calendars
|
|
742
|
+
const activeCalendars = [
|
|
743
|
+
...clinicCalendars.filter((cal) => cal.isActive),
|
|
744
|
+
...practitionerCalendars.filter((cal) => cal.isActive),
|
|
745
|
+
...patientCalendars.filter((cal) => cal.isActive),
|
|
746
|
+
];
|
|
747
|
+
|
|
748
|
+
// If no active calendars, return the appointment as is
|
|
749
|
+
if (activeCalendars.length === 0) {
|
|
750
|
+
return appointment;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Array to store synced calendar event IDs
|
|
754
|
+
const syncedCalendarEvents: SyncedCalendarEvent[] = [];
|
|
755
|
+
const currentTime = Timestamp.now();
|
|
756
|
+
|
|
757
|
+
// Sync with each calendar
|
|
758
|
+
for (const calendar of activeCalendars) {
|
|
759
|
+
try {
|
|
760
|
+
// Sync based on provider
|
|
761
|
+
if (calendar.provider === SyncedCalendarProvider.GOOGLE) {
|
|
762
|
+
let syncResult;
|
|
763
|
+
|
|
764
|
+
// Determine which entity owns this calendar and sync accordingly
|
|
765
|
+
if (clinicCalendars.some((c) => c.id === calendar.id)) {
|
|
766
|
+
syncResult =
|
|
767
|
+
await this.syncedCalendarsService.syncClinicEventsToGoogleCalendar(
|
|
768
|
+
clinicId,
|
|
769
|
+
calendar.id,
|
|
770
|
+
[appointment]
|
|
771
|
+
);
|
|
772
|
+
} else if (practitionerCalendars.some((c) => c.id === calendar.id)) {
|
|
773
|
+
syncResult =
|
|
774
|
+
await this.syncedCalendarsService.syncPractitionerEventsToGoogleCalendar(
|
|
775
|
+
practitionerId,
|
|
776
|
+
calendar.id,
|
|
777
|
+
[appointment]
|
|
778
|
+
);
|
|
779
|
+
} else if (patientCalendars.some((c) => c.id === calendar.id)) {
|
|
780
|
+
syncResult =
|
|
781
|
+
await this.syncedCalendarsService.syncPatientEventsToGoogleCalendar(
|
|
782
|
+
patientId,
|
|
783
|
+
calendar.id,
|
|
784
|
+
[appointment]
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// If sync was successful and we have event IDs, add them to our list
|
|
789
|
+
if (syncResult && syncResult.success && syncResult.syncedEvents > 0) {
|
|
790
|
+
// Get the external event ID - this might need to be adjusted based on the actual return structure
|
|
791
|
+
const externalEventId =
|
|
792
|
+
(syncResult as any).eventIds?.[0] || appointment.id;
|
|
793
|
+
|
|
794
|
+
syncedCalendarEvents.push({
|
|
795
|
+
eventId: externalEventId,
|
|
796
|
+
syncedCalendarProvider: SyncedCalendarProvider.GOOGLE,
|
|
797
|
+
syncedAt: currentTime,
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// Add other providers as needed (Outlook, Apple, etc.)
|
|
802
|
+
} catch (error) {
|
|
803
|
+
console.error(`Error syncing with calendar ${calendar.id}:`, error);
|
|
804
|
+
// Continue with other calendars even if one fails
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// Update the appointment with synced calendar information
|
|
809
|
+
if (syncedCalendarEvents.length > 0) {
|
|
810
|
+
return this.updateAppointment(
|
|
811
|
+
clinicId,
|
|
812
|
+
practitionerId,
|
|
813
|
+
patientId,
|
|
814
|
+
eventId,
|
|
815
|
+
{
|
|
816
|
+
syncedCalendarEventId: syncedCalendarEvents,
|
|
817
|
+
}
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return appointment;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Imports events from external calendars
|
|
826
|
+
* @param entityType - Type of entity (practitioner, patient, clinic)
|
|
827
|
+
* @param entityId - ID of the entity
|
|
828
|
+
* @param startDate - Start date for fetching events
|
|
829
|
+
* @param endDate - End date for fetching events
|
|
830
|
+
* @returns Number of events imported
|
|
831
|
+
*/
|
|
832
|
+
async importEventsFromExternalCalendars(
|
|
833
|
+
entityType: "practitioner" | "patient" | "clinic",
|
|
834
|
+
entityId: string,
|
|
835
|
+
startDate: Date,
|
|
836
|
+
endDate: Date
|
|
837
|
+
): Promise<number> {
|
|
838
|
+
let syncedCalendars: any[] = [];
|
|
839
|
+
|
|
840
|
+
// Get synced calendars for the entity
|
|
841
|
+
switch (entityType) {
|
|
842
|
+
case "practitioner":
|
|
843
|
+
syncedCalendars =
|
|
844
|
+
await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
845
|
+
entityId
|
|
846
|
+
);
|
|
847
|
+
break;
|
|
848
|
+
case "patient":
|
|
849
|
+
syncedCalendars =
|
|
850
|
+
await this.syncedCalendarsService.getPatientSyncedCalendars(entityId);
|
|
851
|
+
break;
|
|
852
|
+
case "clinic":
|
|
853
|
+
syncedCalendars =
|
|
854
|
+
await this.syncedCalendarsService.getClinicSyncedCalendars(entityId);
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Filter active calendars
|
|
859
|
+
const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
|
|
860
|
+
|
|
861
|
+
if (activeCalendars.length === 0) {
|
|
862
|
+
return 0;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
let importedEventsCount = 0;
|
|
866
|
+
const currentTime = Timestamp.now();
|
|
867
|
+
|
|
868
|
+
// Import from each calendar
|
|
869
|
+
for (const calendar of activeCalendars) {
|
|
870
|
+
try {
|
|
871
|
+
let externalEvents: any[] = [];
|
|
872
|
+
|
|
873
|
+
// Fetch events based on provider and entity type
|
|
874
|
+
if (calendar.provider === SyncedCalendarProvider.GOOGLE) {
|
|
875
|
+
switch (entityType) {
|
|
876
|
+
case "practitioner":
|
|
877
|
+
externalEvents =
|
|
878
|
+
await this.syncedCalendarsService.fetchEventsFromPractitionerGoogleCalendar(
|
|
879
|
+
entityId,
|
|
880
|
+
calendar.id,
|
|
881
|
+
startDate,
|
|
882
|
+
endDate
|
|
883
|
+
);
|
|
884
|
+
break;
|
|
885
|
+
case "patient":
|
|
886
|
+
externalEvents =
|
|
887
|
+
await this.syncedCalendarsService.fetchEventsFromPatientGoogleCalendar(
|
|
888
|
+
entityId,
|
|
889
|
+
calendar.id,
|
|
890
|
+
startDate,
|
|
891
|
+
endDate
|
|
892
|
+
);
|
|
893
|
+
break;
|
|
894
|
+
case "clinic":
|
|
895
|
+
externalEvents =
|
|
896
|
+
await this.syncedCalendarsService.fetchEventsFromClinicGoogleCalendar(
|
|
897
|
+
entityId,
|
|
898
|
+
calendar.id,
|
|
899
|
+
startDate,
|
|
900
|
+
endDate
|
|
901
|
+
);
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
// Add other providers as needed
|
|
906
|
+
|
|
907
|
+
// Process and import each event
|
|
908
|
+
for (const externalEvent of externalEvents) {
|
|
909
|
+
try {
|
|
910
|
+
// Create event data from external event
|
|
911
|
+
const eventData: Omit<
|
|
912
|
+
CreateCalendarEventData,
|
|
913
|
+
"id" | "createdAt" | "updatedAt"
|
|
914
|
+
> = {
|
|
915
|
+
eventName: externalEvent.summary || "Imported Event",
|
|
916
|
+
eventTime: {
|
|
917
|
+
start: Timestamp.fromDate(
|
|
918
|
+
new Date(
|
|
919
|
+
externalEvent.start.dateTime || externalEvent.start.date
|
|
920
|
+
)
|
|
921
|
+
),
|
|
922
|
+
end: Timestamp.fromDate(
|
|
923
|
+
new Date(externalEvent.end.dateTime || externalEvent.end.date)
|
|
924
|
+
),
|
|
925
|
+
},
|
|
926
|
+
description: externalEvent.description || "",
|
|
927
|
+
status: CalendarEventStatus.CONFIRMED,
|
|
928
|
+
// Set sync status to EXTERNAL as this is imported from an external calendar
|
|
929
|
+
syncStatus: CalendarSyncStatus.EXTERNAL,
|
|
930
|
+
eventType: CalendarEventType.BLOCKING, // Use BLOCKING type for external events
|
|
931
|
+
syncedCalendarEventId: [
|
|
932
|
+
{
|
|
933
|
+
eventId: externalEvent.id,
|
|
934
|
+
syncedCalendarProvider: calendar.provider,
|
|
935
|
+
syncedAt: currentTime,
|
|
936
|
+
},
|
|
937
|
+
],
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
// Create the event in our system
|
|
941
|
+
switch (entityType) {
|
|
942
|
+
case "practitioner":
|
|
943
|
+
await this.createPractitionerCalendarEvent(entityId, eventData);
|
|
944
|
+
break;
|
|
945
|
+
case "patient":
|
|
946
|
+
await this.createPatientCalendarEvent(entityId, eventData);
|
|
947
|
+
break;
|
|
948
|
+
case "clinic":
|
|
949
|
+
await this.createClinicCalendarEvent(entityId, eventData);
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
importedEventsCount++;
|
|
954
|
+
} catch (eventError) {
|
|
955
|
+
console.error("Error importing event:", eventError);
|
|
956
|
+
// Continue with other events even if one fails
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
} catch (calendarError) {
|
|
960
|
+
console.error(
|
|
961
|
+
`Error fetching events from calendar ${calendar.id}:`,
|
|
962
|
+
calendarError
|
|
963
|
+
);
|
|
964
|
+
// Continue with other calendars even if one fails
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
return importedEventsCount;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Deletes an event from external calendars
|
|
973
|
+
* @param event - Calendar event to delete
|
|
974
|
+
* @param entityType - Type of entity (practitioner, patient, clinic)
|
|
975
|
+
* @param entityId - ID of the entity
|
|
976
|
+
* @returns Success status
|
|
977
|
+
*/
|
|
978
|
+
async deleteEventFromExternalCalendars(
|
|
979
|
+
event: CalendarEvent,
|
|
980
|
+
entityType: "practitioner" | "patient" | "clinic",
|
|
981
|
+
entityId: string
|
|
982
|
+
): Promise<boolean> {
|
|
983
|
+
// If the event doesn't have synced calendar event IDs, return success
|
|
984
|
+
if (
|
|
985
|
+
!event.syncedCalendarEventId ||
|
|
986
|
+
event.syncedCalendarEventId.length === 0
|
|
987
|
+
) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Get synced calendars for the entity
|
|
992
|
+
let syncedCalendars: any[] = [];
|
|
993
|
+
switch (entityType) {
|
|
994
|
+
case "practitioner":
|
|
995
|
+
syncedCalendars =
|
|
996
|
+
await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
997
|
+
entityId
|
|
998
|
+
);
|
|
999
|
+
break;
|
|
1000
|
+
case "patient":
|
|
1001
|
+
syncedCalendars =
|
|
1002
|
+
await this.syncedCalendarsService.getPatientSyncedCalendars(entityId);
|
|
1003
|
+
break;
|
|
1004
|
+
case "clinic":
|
|
1005
|
+
syncedCalendars =
|
|
1006
|
+
await this.syncedCalendarsService.getClinicSyncedCalendars(entityId);
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Filter active calendars
|
|
1011
|
+
const activeCalendars = syncedCalendars.filter((cal) => cal.isActive);
|
|
1012
|
+
|
|
1013
|
+
if (activeCalendars.length === 0) {
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
let allSuccessful = true;
|
|
1018
|
+
|
|
1019
|
+
// Delete from each external calendar
|
|
1020
|
+
for (const syncedEvent of event.syncedCalendarEventId) {
|
|
1021
|
+
try {
|
|
1022
|
+
// Find the calendar for this provider
|
|
1023
|
+
const calendar = activeCalendars.find(
|
|
1024
|
+
(cal) => cal.provider === syncedEvent.syncedCalendarProvider
|
|
1025
|
+
);
|
|
1026
|
+
|
|
1027
|
+
if (!calendar) {
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Delete based on provider and entity type
|
|
1032
|
+
if (
|
|
1033
|
+
syncedEvent.syncedCalendarProvider === SyncedCalendarProvider.GOOGLE
|
|
1034
|
+
) {
|
|
1035
|
+
let success = false;
|
|
1036
|
+
|
|
1037
|
+
switch (entityType) {
|
|
1038
|
+
case "practitioner":
|
|
1039
|
+
success =
|
|
1040
|
+
await this.syncedCalendarsService.deletePractitionerGoogleCalendarEvent(
|
|
1041
|
+
entityId,
|
|
1042
|
+
calendar.id,
|
|
1043
|
+
syncedEvent.eventId
|
|
1044
|
+
);
|
|
1045
|
+
break;
|
|
1046
|
+
case "patient":
|
|
1047
|
+
success =
|
|
1048
|
+
await this.syncedCalendarsService.deletePatientGoogleCalendarEvent(
|
|
1049
|
+
entityId,
|
|
1050
|
+
calendar.id,
|
|
1051
|
+
syncedEvent.eventId
|
|
1052
|
+
);
|
|
1053
|
+
break;
|
|
1054
|
+
case "clinic":
|
|
1055
|
+
success =
|
|
1056
|
+
await this.syncedCalendarsService.deleteClinicGoogleCalendarEvent(
|
|
1057
|
+
entityId,
|
|
1058
|
+
calendar.id,
|
|
1059
|
+
syncedEvent.eventId
|
|
1060
|
+
);
|
|
1061
|
+
break;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (!success) {
|
|
1065
|
+
allSuccessful = false;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
// Add other providers as needed
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
console.error(`Error deleting event from external calendar:`, error);
|
|
1071
|
+
allSuccessful = false;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
return allSuccessful;
|
|
1076
|
+
}
|
|
1077
|
+
}
|