@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,587 +1,587 @@
|
|
|
1
|
-
import {
|
|
2
|
-
collection,
|
|
3
|
-
doc,
|
|
4
|
-
getDoc,
|
|
5
|
-
getDocs,
|
|
6
|
-
setDoc,
|
|
7
|
-
updateDoc,
|
|
8
|
-
query,
|
|
9
|
-
where,
|
|
10
|
-
orderBy,
|
|
11
|
-
limit,
|
|
12
|
-
startAfter,
|
|
13
|
-
QueryDocumentSnapshot,
|
|
14
|
-
} from "firebase/firestore";
|
|
15
|
-
import { BaseService } from "../base.service";
|
|
16
|
-
import {
|
|
17
|
-
// DocumentTemplate, // Not directly used in this file if relying on templateService
|
|
18
|
-
// DOCUMENTATION_TEMPLATES_COLLECTION, // No longer needed directly here if using templateService
|
|
19
|
-
// FILLED_DOCUMENTS_COLLECTION, // This will be replaced by subcollection paths
|
|
20
|
-
FilledDocument,
|
|
21
|
-
FilledDocumentStatus,
|
|
22
|
-
FilledDocumentFileValue,
|
|
23
|
-
USER_FORMS_SUBCOLLECTION,
|
|
24
|
-
DOCTOR_FORMS_SUBCOLLECTION,
|
|
25
|
-
} from "../../types"; // General types
|
|
26
|
-
import { APPOINTMENTS_COLLECTION } from "../../types/appointment"; // Specific import for the constant
|
|
27
|
-
import { DocumentationTemplateService } from "./documentation-template.service";
|
|
28
|
-
import {
|
|
29
|
-
MediaService,
|
|
30
|
-
MediaAccessLevel,
|
|
31
|
-
MediaMetadata,
|
|
32
|
-
} from "../media/media.service";
|
|
33
|
-
// Import the new validation schemas if you plan to use them for input validation here
|
|
34
|
-
// import { createFilledDocumentDataSchema, updateFilledDocumentDataSchema } from '../../validations/documentation-templates.schema';
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Service for managing filled documents within appointment subcollections
|
|
38
|
-
*/
|
|
39
|
-
export class FilledDocumentService extends BaseService {
|
|
40
|
-
// No single collectionRef anymore, paths will be dynamic
|
|
41
|
-
private readonly templateService: DocumentationTemplateService;
|
|
42
|
-
private readonly mediaService: MediaService;
|
|
43
|
-
|
|
44
|
-
constructor(...args: ConstructorParameters<typeof BaseService>) {
|
|
45
|
-
super(...args);
|
|
46
|
-
this.templateService = new DocumentationTemplateService(...args); // Pass db and other args
|
|
47
|
-
this.mediaService = new MediaService(...args); // Initialize media service with the same args
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private getFormSubcollectionPath(
|
|
51
|
-
isUserForm: boolean
|
|
52
|
-
): typeof USER_FORMS_SUBCOLLECTION | typeof DOCTOR_FORMS_SUBCOLLECTION {
|
|
53
|
-
return isUserForm ? USER_FORMS_SUBCOLLECTION : DOCTOR_FORMS_SUBCOLLECTION;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Create a new filled document within an appointment's subcollection.
|
|
58
|
-
* @param templateId - ID of the template to use.
|
|
59
|
-
* @param templateVersion - Version of the template to use.
|
|
60
|
-
* @param appointmentId - ID of the appointment this form belongs to.
|
|
61
|
-
* @param procedureId - ID of the procedure associated with this form.
|
|
62
|
-
* @param patientId - ID of the patient.
|
|
63
|
-
* @param practitionerId - ID of the practitioner (can be system/generic if patient is filling).
|
|
64
|
-
* @param clinicId - ID of the clinic.
|
|
65
|
-
* @param initialValues - Optional initial values for the form elements.
|
|
66
|
-
* @param initialStatus - Optional initial status for the form.
|
|
67
|
-
* @returns The created filled document.
|
|
68
|
-
*/
|
|
69
|
-
async createFilledDocumentForAppointment(
|
|
70
|
-
templateId: string,
|
|
71
|
-
templateVersion: number,
|
|
72
|
-
appointmentId: string,
|
|
73
|
-
procedureId: string,
|
|
74
|
-
patientId: string,
|
|
75
|
-
practitionerId: string, // Consider if this is always available or if a placeholder is needed
|
|
76
|
-
clinicId: string, // Same consideration as practitionerId
|
|
77
|
-
initialValues: { [elementId: string]: any } = {},
|
|
78
|
-
initialStatus: FilledDocumentStatus = FilledDocumentStatus.DRAFT
|
|
79
|
-
): Promise<FilledDocument> {
|
|
80
|
-
const template = await this.templateService.getTemplateById(
|
|
81
|
-
templateId,
|
|
82
|
-
templateVersion
|
|
83
|
-
);
|
|
84
|
-
if (!template) {
|
|
85
|
-
throw new Error(`Template with ID ${templateId} not found`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const documentId = this.generateId();
|
|
89
|
-
const now = Date.now();
|
|
90
|
-
const isUserForm = template.isUserForm || false;
|
|
91
|
-
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
92
|
-
|
|
93
|
-
const filledDocument: FilledDocument = {
|
|
94
|
-
id: documentId,
|
|
95
|
-
templateId,
|
|
96
|
-
templateVersion: template.version,
|
|
97
|
-
isUserForm: isUserForm, // Set based on template
|
|
98
|
-
isRequired: template.isRequired || false, // Inherit isRequired from the template
|
|
99
|
-
appointmentId: appointmentId, // NEW
|
|
100
|
-
procedureId: procedureId, // NEW
|
|
101
|
-
patientId,
|
|
102
|
-
practitionerId,
|
|
103
|
-
clinicId,
|
|
104
|
-
createdAt: now,
|
|
105
|
-
updatedAt: now,
|
|
106
|
-
values: initialValues,
|
|
107
|
-
status: initialStatus,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const docRef = doc(
|
|
111
|
-
this.db,
|
|
112
|
-
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
113
|
-
appointmentId,
|
|
114
|
-
formSubcollection,
|
|
115
|
-
documentId
|
|
116
|
-
);
|
|
117
|
-
await setDoc(docRef, filledDocument);
|
|
118
|
-
|
|
119
|
-
return filledDocument;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get a specific filled document from an appointment's subcollection.
|
|
124
|
-
* @param appointmentId - ID of the appointment.
|
|
125
|
-
* @param formId - ID of the filled document.
|
|
126
|
-
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
127
|
-
* @returns The filled document or null if not found.
|
|
128
|
-
*/
|
|
129
|
-
async getFilledDocumentFromAppointmentById(
|
|
130
|
-
appointmentId: string,
|
|
131
|
-
formId: string,
|
|
132
|
-
isUserForm: boolean
|
|
133
|
-
): Promise<FilledDocument | null> {
|
|
134
|
-
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
135
|
-
const docRef = doc(
|
|
136
|
-
this.db,
|
|
137
|
-
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
138
|
-
appointmentId,
|
|
139
|
-
formSubcollection,
|
|
140
|
-
formId
|
|
141
|
-
);
|
|
142
|
-
const docSnap = await getDoc(docRef);
|
|
143
|
-
|
|
144
|
-
if (!docSnap.exists()) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
return docSnap.data() as FilledDocument;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Update values or status in a filled document within an appointment's subcollection.
|
|
152
|
-
* @param appointmentId - ID of the appointment.
|
|
153
|
-
* @param formId - ID of the filled document to update.
|
|
154
|
-
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
155
|
-
* @param values - Updated values for elements.
|
|
156
|
-
* @param status - Optional new status for the document.
|
|
157
|
-
* @returns The updated filled document.
|
|
158
|
-
*/
|
|
159
|
-
async updateFilledDocumentInAppointment(
|
|
160
|
-
appointmentId: string,
|
|
161
|
-
formId: string,
|
|
162
|
-
isUserForm: boolean,
|
|
163
|
-
values?: { [elementId: string]: any },
|
|
164
|
-
status?: FilledDocumentStatus
|
|
165
|
-
): Promise<FilledDocument> {
|
|
166
|
-
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
167
|
-
const docRef = doc(
|
|
168
|
-
this.db,
|
|
169
|
-
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
170
|
-
appointmentId,
|
|
171
|
-
formSubcollection,
|
|
172
|
-
formId
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
const existingDoc = await this.getFilledDocumentFromAppointmentById(
|
|
176
|
-
appointmentId,
|
|
177
|
-
formId,
|
|
178
|
-
isUserForm
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
if (!existingDoc) {
|
|
182
|
-
throw new Error(
|
|
183
|
-
`Filled document with ID ${formId} not found in appointment ${appointmentId} ${formSubcollection}`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const updatePayload: Partial<FilledDocument> = {
|
|
188
|
-
updatedAt: Date.now(),
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
if (values) {
|
|
192
|
-
updatePayload.values = {
|
|
193
|
-
...existingDoc.values,
|
|
194
|
-
...values,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
if (status) {
|
|
198
|
-
updatePayload.status = status;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
Object.keys(updatePayload).length === 1 &&
|
|
203
|
-
"updatedAt" in updatePayload
|
|
204
|
-
) {
|
|
205
|
-
// Only updatedAt, no actual data change, so we can skip the update
|
|
206
|
-
// or just update timestamp if that is the desired behavior.
|
|
207
|
-
// For now, we proceed to update the timestamp at least.
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
await updateDoc(docRef, updatePayload);
|
|
211
|
-
|
|
212
|
-
return { ...existingDoc, ...updatePayload } as FilledDocument;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Get all filled user forms for a specific appointment.
|
|
217
|
-
* @param appointmentId ID of the appointment.
|
|
218
|
-
* @param pageSize Number of documents to retrieve.
|
|
219
|
-
* @param lastDoc Last document from previous page for pagination.
|
|
220
|
-
*/
|
|
221
|
-
async getFilledUserFormsForAppointment(
|
|
222
|
-
appointmentId: string,
|
|
223
|
-
pageSize = 20,
|
|
224
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
225
|
-
): Promise<{
|
|
226
|
-
documents: FilledDocument[];
|
|
227
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
228
|
-
}> {
|
|
229
|
-
const subcollectionRef = collection(
|
|
230
|
-
this.db,
|
|
231
|
-
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
232
|
-
appointmentId,
|
|
233
|
-
USER_FORMS_SUBCOLLECTION
|
|
234
|
-
);
|
|
235
|
-
let q = query(
|
|
236
|
-
subcollectionRef,
|
|
237
|
-
orderBy("updatedAt", "desc"),
|
|
238
|
-
limit(pageSize)
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
if (lastDoc) {
|
|
242
|
-
q = query(q, startAfter(lastDoc));
|
|
243
|
-
}
|
|
244
|
-
return this.executeQuery(q);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Get all filled doctor forms for a specific appointment.
|
|
249
|
-
* @param appointmentId ID of the appointment.
|
|
250
|
-
* @param pageSize Number of documents to retrieve.
|
|
251
|
-
* @param lastDoc Last document from previous page for pagination.
|
|
252
|
-
*/
|
|
253
|
-
async getFilledDoctorFormsForAppointment(
|
|
254
|
-
appointmentId: string,
|
|
255
|
-
pageSize = 20,
|
|
256
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
257
|
-
): Promise<{
|
|
258
|
-
documents: FilledDocument[];
|
|
259
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
260
|
-
}> {
|
|
261
|
-
const subcollectionRef = collection(
|
|
262
|
-
this.db,
|
|
263
|
-
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
264
|
-
appointmentId,
|
|
265
|
-
DOCTOR_FORMS_SUBCOLLECTION
|
|
266
|
-
);
|
|
267
|
-
let q = query(
|
|
268
|
-
subcollectionRef,
|
|
269
|
-
orderBy("updatedAt", "desc"),
|
|
270
|
-
limit(pageSize)
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
if (lastDoc) {
|
|
274
|
-
q = query(q, startAfter(lastDoc));
|
|
275
|
-
}
|
|
276
|
-
return this.executeQuery(q);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Helper to execute query and return documents + lastDoc
|
|
280
|
-
private async executeQuery(
|
|
281
|
-
q: any // Firestore Query type
|
|
282
|
-
): Promise<{
|
|
283
|
-
documents: FilledDocument[];
|
|
284
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
285
|
-
}> {
|
|
286
|
-
const querySnapshot = await getDocs(q);
|
|
287
|
-
const documents: FilledDocument[] = [];
|
|
288
|
-
let lastVisible: QueryDocumentSnapshot<FilledDocument> | null = null;
|
|
289
|
-
|
|
290
|
-
querySnapshot.forEach((doc) => {
|
|
291
|
-
documents.push(doc.data() as FilledDocument);
|
|
292
|
-
lastVisible = doc as QueryDocumentSnapshot<FilledDocument>;
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
return {
|
|
296
|
-
documents,
|
|
297
|
-
lastDoc: lastVisible,
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// IMPORTANT: The following methods that query across all patients/practitioners/clinics
|
|
302
|
-
// (e.g., getFilledDocumentsByPatient, getFilledDocumentsByPractitioner)
|
|
303
|
-
// will NOT work correctly if FILLED_DOCUMENTS_COLLECTION is no longer a top-level collection
|
|
304
|
-
// and data is only in appointment subcollections. You would need to use
|
|
305
|
-
// Firestore Collection Group Queries for that, which require an index and a different query approach.
|
|
306
|
-
// These methods are left here for now but would need significant rework or removal.
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Get filled documents for a patient (NEEDS REWORK for subcollections or Collection Group Query)
|
|
310
|
-
* @param patientId - ID of the patient
|
|
311
|
-
* @param pageSize - Number of documents to retrieve
|
|
312
|
-
* @param lastDoc - Last document from previous page for pagination
|
|
313
|
-
* @returns Array of filled documents and the last document for pagination
|
|
314
|
-
*/
|
|
315
|
-
async getFilledDocumentsByPatient(
|
|
316
|
-
patientId: string,
|
|
317
|
-
pageSize = 20,
|
|
318
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
319
|
-
): Promise<{
|
|
320
|
-
documents: FilledDocument[];
|
|
321
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
322
|
-
}> {
|
|
323
|
-
console.warn(
|
|
324
|
-
"getFilledDocumentsByPatient needs rework for subcollection model or Collection Group Queries."
|
|
325
|
-
);
|
|
326
|
-
// Original logic commented out as this.collectionRef is no longer valid for this model
|
|
327
|
-
/*
|
|
328
|
-
let q = query(
|
|
329
|
-
this.collectionRef, // LINTER ERROR: this.collectionRef no longer exists
|
|
330
|
-
where("patientId", "==", patientId),
|
|
331
|
-
orderBy("updatedAt", "desc"),
|
|
332
|
-
limit(pageSize)
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
if (lastDoc) {
|
|
336
|
-
q = query(q, startAfter(lastDoc));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const querySnapshot = await getDocs(q);
|
|
340
|
-
const documents: FilledDocument[] = [];
|
|
341
|
-
let lastVisible: QueryDocumentSnapshot<FilledDocument> | null = null;
|
|
342
|
-
|
|
343
|
-
querySnapshot.forEach((doc) => {
|
|
344
|
-
documents.push(doc.data() as FilledDocument);
|
|
345
|
-
lastVisible = doc as QueryDocumentSnapshot<FilledDocument>;
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
return {
|
|
349
|
-
documents,
|
|
350
|
-
lastDoc: lastVisible,
|
|
351
|
-
};
|
|
352
|
-
*/
|
|
353
|
-
return { documents: [], lastDoc: null }; // Placeholder return
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Get filled documents for a practitioner (NEEDS REWORK for subcollections or Collection Group Query)
|
|
358
|
-
* @param practitionerId - ID of the practitioner
|
|
359
|
-
* @param pageSize - Number of documents to retrieve
|
|
360
|
-
* @param lastDoc - Last document from previous page for pagination
|
|
361
|
-
* @returns Array of filled documents and the last document for pagination
|
|
362
|
-
*/
|
|
363
|
-
async getFilledDocumentsByPractitioner(
|
|
364
|
-
practitionerId: string,
|
|
365
|
-
pageSize = 20,
|
|
366
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
367
|
-
): Promise<{
|
|
368
|
-
documents: FilledDocument[];
|
|
369
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
370
|
-
}> {
|
|
371
|
-
console.warn(
|
|
372
|
-
"getFilledDocumentsByPractitioner needs rework for subcollection model or Collection Group Queries."
|
|
373
|
-
);
|
|
374
|
-
// Original logic commented out
|
|
375
|
-
/*
|
|
376
|
-
let q = query(
|
|
377
|
-
this.collectionRef, // LINTER ERROR
|
|
378
|
-
where("practitionerId", "==", practitionerId),
|
|
379
|
-
orderBy("updatedAt", "desc"),
|
|
380
|
-
limit(pageSize)
|
|
381
|
-
);
|
|
382
|
-
// ... rest of original logic ...
|
|
383
|
-
*/
|
|
384
|
-
return { documents: [], lastDoc: null }; // Placeholder return
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Get filled documents for a clinic (NEEDS REWORK for subcollections or Collection Group Query)
|
|
389
|
-
* @param clinicId - ID of the clinic
|
|
390
|
-
* @param pageSize - Number of documents to retrieve
|
|
391
|
-
* @param lastDoc - Last document from previous page for pagination
|
|
392
|
-
* @returns Array of filled documents and the last document for pagination
|
|
393
|
-
*/
|
|
394
|
-
async getFilledDocumentsByClinic(
|
|
395
|
-
clinicId: string,
|
|
396
|
-
pageSize = 20,
|
|
397
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
398
|
-
): Promise<{
|
|
399
|
-
documents: FilledDocument[];
|
|
400
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
401
|
-
}> {
|
|
402
|
-
console.warn(
|
|
403
|
-
"getFilledDocumentsByClinic needs rework for subcollection model or Collection Group Queries."
|
|
404
|
-
);
|
|
405
|
-
// Original logic commented out
|
|
406
|
-
/*
|
|
407
|
-
let q = query(
|
|
408
|
-
this.collectionRef, // LINTER ERROR
|
|
409
|
-
where("clinicId", "==", clinicId),
|
|
410
|
-
orderBy("updatedAt", "desc"),
|
|
411
|
-
limit(pageSize)
|
|
412
|
-
);
|
|
413
|
-
// ... rest of original logic ...
|
|
414
|
-
*/
|
|
415
|
-
return { documents: [], lastDoc: null }; // Placeholder return
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Get filled documents by template (NEEDS REWORK for subcollections or Collection Group Query)
|
|
420
|
-
* @param templateId - ID of the template
|
|
421
|
-
* @param pageSize - Number of documents to retrieve
|
|
422
|
-
* @param lastDoc - Last document from previous page for pagination
|
|
423
|
-
* @returns Array of filled documents and the last document for pagination
|
|
424
|
-
*/
|
|
425
|
-
async getFilledDocumentsByTemplate(
|
|
426
|
-
templateId: string,
|
|
427
|
-
pageSize = 20,
|
|
428
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
429
|
-
): Promise<{
|
|
430
|
-
documents: FilledDocument[];
|
|
431
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
432
|
-
}> {
|
|
433
|
-
console.warn(
|
|
434
|
-
"getFilledDocumentsByTemplate needs rework for subcollection model or Collection Group Queries."
|
|
435
|
-
);
|
|
436
|
-
// Original logic commented out
|
|
437
|
-
/*
|
|
438
|
-
let q = query(
|
|
439
|
-
this.collectionRef, // LINTER ERROR
|
|
440
|
-
where("templateId", "==", templateId),
|
|
441
|
-
orderBy("updatedAt", "desc"),
|
|
442
|
-
limit(pageSize)
|
|
443
|
-
);
|
|
444
|
-
// ... rest of original logic ...
|
|
445
|
-
*/
|
|
446
|
-
return { documents: [], lastDoc: null }; // Placeholder return
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Get filled documents by status (NEEDS REWORK for subcollections or Collection Group Query)
|
|
451
|
-
* @param status - Status to filter by
|
|
452
|
-
* @param pageSize - Number of documents to retrieve
|
|
453
|
-
* @param lastDoc - Last document from previous page for pagination
|
|
454
|
-
* @returns Array of filled documents and the last document for pagination
|
|
455
|
-
*/
|
|
456
|
-
async getFilledDocumentsByStatus(
|
|
457
|
-
status: FilledDocumentStatus,
|
|
458
|
-
pageSize = 20,
|
|
459
|
-
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
460
|
-
): Promise<{
|
|
461
|
-
documents: FilledDocument[];
|
|
462
|
-
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
463
|
-
}> {
|
|
464
|
-
console.warn(
|
|
465
|
-
"getFilledDocumentsByStatus needs rework for subcollection model or Collection Group Queries."
|
|
466
|
-
);
|
|
467
|
-
// Original logic commented out
|
|
468
|
-
/*
|
|
469
|
-
let q = query(
|
|
470
|
-
this.collectionRef, // LINTER ERROR
|
|
471
|
-
where("status", "==", status),
|
|
472
|
-
orderBy("updatedAt", "desc"),
|
|
473
|
-
limit(pageSize)
|
|
474
|
-
);
|
|
475
|
-
// ... rest of original logic ...
|
|
476
|
-
*/
|
|
477
|
-
return { documents: [], lastDoc: null }; // Placeholder return
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Upload a file for a filled document field without updating the document.
|
|
482
|
-
* This method only handles the upload and returns the file value to be used by the UI.
|
|
483
|
-
*
|
|
484
|
-
* @param appointmentId - ID of the appointment
|
|
485
|
-
* @param formId - ID of the filled document
|
|
486
|
-
* @param isUserForm - Boolean indicating if it's a user form or doctor form
|
|
487
|
-
* @param file - The file to upload
|
|
488
|
-
* @returns The file value object to be stored in the document
|
|
489
|
-
*/
|
|
490
|
-
async uploadFileForFilledDocument(
|
|
491
|
-
appointmentId: string,
|
|
492
|
-
formId: string,
|
|
493
|
-
isUserForm: boolean,
|
|
494
|
-
file: File | Blob
|
|
495
|
-
): Promise<FilledDocumentFileValue> {
|
|
496
|
-
console.log(
|
|
497
|
-
`[FilledDocumentService] Uploading file for form ${formId} in appointment ${appointmentId}`
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
// Generate a unique file ID
|
|
501
|
-
const fileId = this.generateId();
|
|
502
|
-
|
|
503
|
-
// Set the path according to the specified structure
|
|
504
|
-
const formType = isUserForm ? "user-form" : "doctor-form";
|
|
505
|
-
const collectionName = `${formType}/${formId}`;
|
|
506
|
-
|
|
507
|
-
// Always use CONFIDENTIAL access level
|
|
508
|
-
const accessLevel = MediaAccessLevel.CONFIDENTIAL;
|
|
509
|
-
|
|
510
|
-
// Upload the file using MediaService
|
|
511
|
-
const mediaMetadata = await this.mediaService.uploadMedia(
|
|
512
|
-
file,
|
|
513
|
-
appointmentId, // Using appointmentId as ownerId
|
|
514
|
-
accessLevel,
|
|
515
|
-
collectionName,
|
|
516
|
-
file instanceof File ? file.name : `file_${fileId}`
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
// Create and return a file value object
|
|
520
|
-
const fileValue: FilledDocumentFileValue = {
|
|
521
|
-
mediaId: mediaMetadata.id,
|
|
522
|
-
url: mediaMetadata.url,
|
|
523
|
-
name: mediaMetadata.name,
|
|
524
|
-
contentType: mediaMetadata.contentType,
|
|
525
|
-
size: mediaMetadata.size,
|
|
526
|
-
uploadedAt: Date.now(),
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
return fileValue;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Upload a signature image for a filled document.
|
|
534
|
-
* This is a specialized version of uploadFileForFilledDocument specifically for signatures.
|
|
535
|
-
*
|
|
536
|
-
* @param appointmentId - ID of the appointment
|
|
537
|
-
* @param formId - ID of the filled document
|
|
538
|
-
* @param isUserForm - Boolean indicating if it's a user form or doctor form
|
|
539
|
-
* @param signatureBlob - The signature image as a Blob
|
|
540
|
-
* @returns The file value object to be stored in the document
|
|
541
|
-
*/
|
|
542
|
-
async uploadSignatureForFilledDocument(
|
|
543
|
-
appointmentId: string,
|
|
544
|
-
formId: string,
|
|
545
|
-
isUserForm: boolean,
|
|
546
|
-
signatureBlob: Blob
|
|
547
|
-
): Promise<FilledDocumentFileValue> {
|
|
548
|
-
console.log(
|
|
549
|
-
`[FilledDocumentService] Uploading signature for form ${formId}`
|
|
550
|
-
);
|
|
551
|
-
|
|
552
|
-
// Generate a filename for the signature
|
|
553
|
-
const signatureId = this.generateId();
|
|
554
|
-
const signatureFile = new File(
|
|
555
|
-
[signatureBlob],
|
|
556
|
-
`signature_${signatureId}.png`,
|
|
557
|
-
{ type: "image/png" }
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
// Use the general file upload method
|
|
561
|
-
return this.uploadFileForFilledDocument(
|
|
562
|
-
appointmentId,
|
|
563
|
-
formId,
|
|
564
|
-
isUserForm,
|
|
565
|
-
signatureFile
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Delete a file using its mediaId.
|
|
571
|
-
*
|
|
572
|
-
* @param mediaId - ID of the media to delete
|
|
573
|
-
* @returns Promise resolving when the deletion is complete
|
|
574
|
-
*/
|
|
575
|
-
async deleteFile(mediaId: string): Promise<void> {
|
|
576
|
-
console.log(
|
|
577
|
-
`[FilledDocumentService] Deleting file with mediaId ${mediaId}`
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
if (!mediaId) {
|
|
581
|
-
throw new Error("MediaId is required to delete a file");
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// Delete the file using MediaService
|
|
585
|
-
await this.mediaService.deleteMedia(mediaId);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
collection,
|
|
3
|
+
doc,
|
|
4
|
+
getDoc,
|
|
5
|
+
getDocs,
|
|
6
|
+
setDoc,
|
|
7
|
+
updateDoc,
|
|
8
|
+
query,
|
|
9
|
+
where,
|
|
10
|
+
orderBy,
|
|
11
|
+
limit,
|
|
12
|
+
startAfter,
|
|
13
|
+
QueryDocumentSnapshot,
|
|
14
|
+
} from "firebase/firestore";
|
|
15
|
+
import { BaseService } from "../base.service";
|
|
16
|
+
import {
|
|
17
|
+
// DocumentTemplate, // Not directly used in this file if relying on templateService
|
|
18
|
+
// DOCUMENTATION_TEMPLATES_COLLECTION, // No longer needed directly here if using templateService
|
|
19
|
+
// FILLED_DOCUMENTS_COLLECTION, // This will be replaced by subcollection paths
|
|
20
|
+
FilledDocument,
|
|
21
|
+
FilledDocumentStatus,
|
|
22
|
+
FilledDocumentFileValue,
|
|
23
|
+
USER_FORMS_SUBCOLLECTION,
|
|
24
|
+
DOCTOR_FORMS_SUBCOLLECTION,
|
|
25
|
+
} from "../../types"; // General types
|
|
26
|
+
import { APPOINTMENTS_COLLECTION } from "../../types/appointment"; // Specific import for the constant
|
|
27
|
+
import { DocumentationTemplateService } from "./documentation-template.service";
|
|
28
|
+
import {
|
|
29
|
+
MediaService,
|
|
30
|
+
MediaAccessLevel,
|
|
31
|
+
MediaMetadata,
|
|
32
|
+
} from "../media/media.service";
|
|
33
|
+
// Import the new validation schemas if you plan to use them for input validation here
|
|
34
|
+
// import { createFilledDocumentDataSchema, updateFilledDocumentDataSchema } from '../../validations/documentation-templates.schema';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Service for managing filled documents within appointment subcollections
|
|
38
|
+
*/
|
|
39
|
+
export class FilledDocumentService extends BaseService {
|
|
40
|
+
// No single collectionRef anymore, paths will be dynamic
|
|
41
|
+
private readonly templateService: DocumentationTemplateService;
|
|
42
|
+
private readonly mediaService: MediaService;
|
|
43
|
+
|
|
44
|
+
constructor(...args: ConstructorParameters<typeof BaseService>) {
|
|
45
|
+
super(...args);
|
|
46
|
+
this.templateService = new DocumentationTemplateService(...args); // Pass db and other args
|
|
47
|
+
this.mediaService = new MediaService(...args); // Initialize media service with the same args
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private getFormSubcollectionPath(
|
|
51
|
+
isUserForm: boolean
|
|
52
|
+
): typeof USER_FORMS_SUBCOLLECTION | typeof DOCTOR_FORMS_SUBCOLLECTION {
|
|
53
|
+
return isUserForm ? USER_FORMS_SUBCOLLECTION : DOCTOR_FORMS_SUBCOLLECTION;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a new filled document within an appointment's subcollection.
|
|
58
|
+
* @param templateId - ID of the template to use.
|
|
59
|
+
* @param templateVersion - Version of the template to use.
|
|
60
|
+
* @param appointmentId - ID of the appointment this form belongs to.
|
|
61
|
+
* @param procedureId - ID of the procedure associated with this form.
|
|
62
|
+
* @param patientId - ID of the patient.
|
|
63
|
+
* @param practitionerId - ID of the practitioner (can be system/generic if patient is filling).
|
|
64
|
+
* @param clinicId - ID of the clinic.
|
|
65
|
+
* @param initialValues - Optional initial values for the form elements.
|
|
66
|
+
* @param initialStatus - Optional initial status for the form.
|
|
67
|
+
* @returns The created filled document.
|
|
68
|
+
*/
|
|
69
|
+
async createFilledDocumentForAppointment(
|
|
70
|
+
templateId: string,
|
|
71
|
+
templateVersion: number,
|
|
72
|
+
appointmentId: string,
|
|
73
|
+
procedureId: string,
|
|
74
|
+
patientId: string,
|
|
75
|
+
practitionerId: string, // Consider if this is always available or if a placeholder is needed
|
|
76
|
+
clinicId: string, // Same consideration as practitionerId
|
|
77
|
+
initialValues: { [elementId: string]: any } = {},
|
|
78
|
+
initialStatus: FilledDocumentStatus = FilledDocumentStatus.DRAFT
|
|
79
|
+
): Promise<FilledDocument> {
|
|
80
|
+
const template = await this.templateService.getTemplateById(
|
|
81
|
+
templateId,
|
|
82
|
+
templateVersion
|
|
83
|
+
);
|
|
84
|
+
if (!template) {
|
|
85
|
+
throw new Error(`Template with ID ${templateId} not found`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const documentId = this.generateId();
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
const isUserForm = template.isUserForm || false;
|
|
91
|
+
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
92
|
+
|
|
93
|
+
const filledDocument: FilledDocument = {
|
|
94
|
+
id: documentId,
|
|
95
|
+
templateId,
|
|
96
|
+
templateVersion: template.version,
|
|
97
|
+
isUserForm: isUserForm, // Set based on template
|
|
98
|
+
isRequired: template.isRequired || false, // Inherit isRequired from the template
|
|
99
|
+
appointmentId: appointmentId, // NEW
|
|
100
|
+
procedureId: procedureId, // NEW
|
|
101
|
+
patientId,
|
|
102
|
+
practitionerId,
|
|
103
|
+
clinicId,
|
|
104
|
+
createdAt: now,
|
|
105
|
+
updatedAt: now,
|
|
106
|
+
values: initialValues,
|
|
107
|
+
status: initialStatus,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const docRef = doc(
|
|
111
|
+
this.db,
|
|
112
|
+
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
113
|
+
appointmentId,
|
|
114
|
+
formSubcollection,
|
|
115
|
+
documentId
|
|
116
|
+
);
|
|
117
|
+
await setDoc(docRef, filledDocument);
|
|
118
|
+
|
|
119
|
+
return filledDocument;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get a specific filled document from an appointment's subcollection.
|
|
124
|
+
* @param appointmentId - ID of the appointment.
|
|
125
|
+
* @param formId - ID of the filled document.
|
|
126
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
127
|
+
* @returns The filled document or null if not found.
|
|
128
|
+
*/
|
|
129
|
+
async getFilledDocumentFromAppointmentById(
|
|
130
|
+
appointmentId: string,
|
|
131
|
+
formId: string,
|
|
132
|
+
isUserForm: boolean
|
|
133
|
+
): Promise<FilledDocument | null> {
|
|
134
|
+
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
135
|
+
const docRef = doc(
|
|
136
|
+
this.db,
|
|
137
|
+
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
138
|
+
appointmentId,
|
|
139
|
+
formSubcollection,
|
|
140
|
+
formId
|
|
141
|
+
);
|
|
142
|
+
const docSnap = await getDoc(docRef);
|
|
143
|
+
|
|
144
|
+
if (!docSnap.exists()) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return docSnap.data() as FilledDocument;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Update values or status in a filled document within an appointment's subcollection.
|
|
152
|
+
* @param appointmentId - ID of the appointment.
|
|
153
|
+
* @param formId - ID of the filled document to update.
|
|
154
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
155
|
+
* @param values - Updated values for elements.
|
|
156
|
+
* @param status - Optional new status for the document.
|
|
157
|
+
* @returns The updated filled document.
|
|
158
|
+
*/
|
|
159
|
+
async updateFilledDocumentInAppointment(
|
|
160
|
+
appointmentId: string,
|
|
161
|
+
formId: string,
|
|
162
|
+
isUserForm: boolean,
|
|
163
|
+
values?: { [elementId: string]: any },
|
|
164
|
+
status?: FilledDocumentStatus
|
|
165
|
+
): Promise<FilledDocument> {
|
|
166
|
+
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
167
|
+
const docRef = doc(
|
|
168
|
+
this.db,
|
|
169
|
+
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
170
|
+
appointmentId,
|
|
171
|
+
formSubcollection,
|
|
172
|
+
formId
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const existingDoc = await this.getFilledDocumentFromAppointmentById(
|
|
176
|
+
appointmentId,
|
|
177
|
+
formId,
|
|
178
|
+
isUserForm
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (!existingDoc) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Filled document with ID ${formId} not found in appointment ${appointmentId} ${formSubcollection}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const updatePayload: Partial<FilledDocument> = {
|
|
188
|
+
updatedAt: Date.now(),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
if (values) {
|
|
192
|
+
updatePayload.values = {
|
|
193
|
+
...existingDoc.values,
|
|
194
|
+
...values,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (status) {
|
|
198
|
+
updatePayload.status = status;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (
|
|
202
|
+
Object.keys(updatePayload).length === 1 &&
|
|
203
|
+
"updatedAt" in updatePayload
|
|
204
|
+
) {
|
|
205
|
+
// Only updatedAt, no actual data change, so we can skip the update
|
|
206
|
+
// or just update timestamp if that is the desired behavior.
|
|
207
|
+
// For now, we proceed to update the timestamp at least.
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await updateDoc(docRef, updatePayload);
|
|
211
|
+
|
|
212
|
+
return { ...existingDoc, ...updatePayload } as FilledDocument;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get all filled user forms for a specific appointment.
|
|
217
|
+
* @param appointmentId ID of the appointment.
|
|
218
|
+
* @param pageSize Number of documents to retrieve.
|
|
219
|
+
* @param lastDoc Last document from previous page for pagination.
|
|
220
|
+
*/
|
|
221
|
+
async getFilledUserFormsForAppointment(
|
|
222
|
+
appointmentId: string,
|
|
223
|
+
pageSize = 20,
|
|
224
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
225
|
+
): Promise<{
|
|
226
|
+
documents: FilledDocument[];
|
|
227
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
228
|
+
}> {
|
|
229
|
+
const subcollectionRef = collection(
|
|
230
|
+
this.db,
|
|
231
|
+
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
232
|
+
appointmentId,
|
|
233
|
+
USER_FORMS_SUBCOLLECTION
|
|
234
|
+
);
|
|
235
|
+
let q = query(
|
|
236
|
+
subcollectionRef,
|
|
237
|
+
orderBy("updatedAt", "desc"),
|
|
238
|
+
limit(pageSize)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (lastDoc) {
|
|
242
|
+
q = query(q, startAfter(lastDoc));
|
|
243
|
+
}
|
|
244
|
+
return this.executeQuery(q);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get all filled doctor forms for a specific appointment.
|
|
249
|
+
* @param appointmentId ID of the appointment.
|
|
250
|
+
* @param pageSize Number of documents to retrieve.
|
|
251
|
+
* @param lastDoc Last document from previous page for pagination.
|
|
252
|
+
*/
|
|
253
|
+
async getFilledDoctorFormsForAppointment(
|
|
254
|
+
appointmentId: string,
|
|
255
|
+
pageSize = 20,
|
|
256
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
257
|
+
): Promise<{
|
|
258
|
+
documents: FilledDocument[];
|
|
259
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
260
|
+
}> {
|
|
261
|
+
const subcollectionRef = collection(
|
|
262
|
+
this.db,
|
|
263
|
+
APPOINTMENTS_COLLECTION, // Replaced "appointments"
|
|
264
|
+
appointmentId,
|
|
265
|
+
DOCTOR_FORMS_SUBCOLLECTION
|
|
266
|
+
);
|
|
267
|
+
let q = query(
|
|
268
|
+
subcollectionRef,
|
|
269
|
+
orderBy("updatedAt", "desc"),
|
|
270
|
+
limit(pageSize)
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
if (lastDoc) {
|
|
274
|
+
q = query(q, startAfter(lastDoc));
|
|
275
|
+
}
|
|
276
|
+
return this.executeQuery(q);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Helper to execute query and return documents + lastDoc
|
|
280
|
+
private async executeQuery(
|
|
281
|
+
q: any // Firestore Query type
|
|
282
|
+
): Promise<{
|
|
283
|
+
documents: FilledDocument[];
|
|
284
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
285
|
+
}> {
|
|
286
|
+
const querySnapshot = await getDocs(q);
|
|
287
|
+
const documents: FilledDocument[] = [];
|
|
288
|
+
let lastVisible: QueryDocumentSnapshot<FilledDocument> | null = null;
|
|
289
|
+
|
|
290
|
+
querySnapshot.forEach((doc) => {
|
|
291
|
+
documents.push(doc.data() as FilledDocument);
|
|
292
|
+
lastVisible = doc as QueryDocumentSnapshot<FilledDocument>;
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
documents,
|
|
297
|
+
lastDoc: lastVisible,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// IMPORTANT: The following methods that query across all patients/practitioners/clinics
|
|
302
|
+
// (e.g., getFilledDocumentsByPatient, getFilledDocumentsByPractitioner)
|
|
303
|
+
// will NOT work correctly if FILLED_DOCUMENTS_COLLECTION is no longer a top-level collection
|
|
304
|
+
// and data is only in appointment subcollections. You would need to use
|
|
305
|
+
// Firestore Collection Group Queries for that, which require an index and a different query approach.
|
|
306
|
+
// These methods are left here for now but would need significant rework or removal.
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get filled documents for a patient (NEEDS REWORK for subcollections or Collection Group Query)
|
|
310
|
+
* @param patientId - ID of the patient
|
|
311
|
+
* @param pageSize - Number of documents to retrieve
|
|
312
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
313
|
+
* @returns Array of filled documents and the last document for pagination
|
|
314
|
+
*/
|
|
315
|
+
async getFilledDocumentsByPatient(
|
|
316
|
+
patientId: string,
|
|
317
|
+
pageSize = 20,
|
|
318
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
319
|
+
): Promise<{
|
|
320
|
+
documents: FilledDocument[];
|
|
321
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
322
|
+
}> {
|
|
323
|
+
console.warn(
|
|
324
|
+
"getFilledDocumentsByPatient needs rework for subcollection model or Collection Group Queries."
|
|
325
|
+
);
|
|
326
|
+
// Original logic commented out as this.collectionRef is no longer valid for this model
|
|
327
|
+
/*
|
|
328
|
+
let q = query(
|
|
329
|
+
this.collectionRef, // LINTER ERROR: this.collectionRef no longer exists
|
|
330
|
+
where("patientId", "==", patientId),
|
|
331
|
+
orderBy("updatedAt", "desc"),
|
|
332
|
+
limit(pageSize)
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (lastDoc) {
|
|
336
|
+
q = query(q, startAfter(lastDoc));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const querySnapshot = await getDocs(q);
|
|
340
|
+
const documents: FilledDocument[] = [];
|
|
341
|
+
let lastVisible: QueryDocumentSnapshot<FilledDocument> | null = null;
|
|
342
|
+
|
|
343
|
+
querySnapshot.forEach((doc) => {
|
|
344
|
+
documents.push(doc.data() as FilledDocument);
|
|
345
|
+
lastVisible = doc as QueryDocumentSnapshot<FilledDocument>;
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
documents,
|
|
350
|
+
lastDoc: lastVisible,
|
|
351
|
+
};
|
|
352
|
+
*/
|
|
353
|
+
return { documents: [], lastDoc: null }; // Placeholder return
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get filled documents for a practitioner (NEEDS REWORK for subcollections or Collection Group Query)
|
|
358
|
+
* @param practitionerId - ID of the practitioner
|
|
359
|
+
* @param pageSize - Number of documents to retrieve
|
|
360
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
361
|
+
* @returns Array of filled documents and the last document for pagination
|
|
362
|
+
*/
|
|
363
|
+
async getFilledDocumentsByPractitioner(
|
|
364
|
+
practitionerId: string,
|
|
365
|
+
pageSize = 20,
|
|
366
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
367
|
+
): Promise<{
|
|
368
|
+
documents: FilledDocument[];
|
|
369
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
370
|
+
}> {
|
|
371
|
+
console.warn(
|
|
372
|
+
"getFilledDocumentsByPractitioner needs rework for subcollection model or Collection Group Queries."
|
|
373
|
+
);
|
|
374
|
+
// Original logic commented out
|
|
375
|
+
/*
|
|
376
|
+
let q = query(
|
|
377
|
+
this.collectionRef, // LINTER ERROR
|
|
378
|
+
where("practitionerId", "==", practitionerId),
|
|
379
|
+
orderBy("updatedAt", "desc"),
|
|
380
|
+
limit(pageSize)
|
|
381
|
+
);
|
|
382
|
+
// ... rest of original logic ...
|
|
383
|
+
*/
|
|
384
|
+
return { documents: [], lastDoc: null }; // Placeholder return
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get filled documents for a clinic (NEEDS REWORK for subcollections or Collection Group Query)
|
|
389
|
+
* @param clinicId - ID of the clinic
|
|
390
|
+
* @param pageSize - Number of documents to retrieve
|
|
391
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
392
|
+
* @returns Array of filled documents and the last document for pagination
|
|
393
|
+
*/
|
|
394
|
+
async getFilledDocumentsByClinic(
|
|
395
|
+
clinicId: string,
|
|
396
|
+
pageSize = 20,
|
|
397
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
398
|
+
): Promise<{
|
|
399
|
+
documents: FilledDocument[];
|
|
400
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
401
|
+
}> {
|
|
402
|
+
console.warn(
|
|
403
|
+
"getFilledDocumentsByClinic needs rework for subcollection model or Collection Group Queries."
|
|
404
|
+
);
|
|
405
|
+
// Original logic commented out
|
|
406
|
+
/*
|
|
407
|
+
let q = query(
|
|
408
|
+
this.collectionRef, // LINTER ERROR
|
|
409
|
+
where("clinicId", "==", clinicId),
|
|
410
|
+
orderBy("updatedAt", "desc"),
|
|
411
|
+
limit(pageSize)
|
|
412
|
+
);
|
|
413
|
+
// ... rest of original logic ...
|
|
414
|
+
*/
|
|
415
|
+
return { documents: [], lastDoc: null }; // Placeholder return
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Get filled documents by template (NEEDS REWORK for subcollections or Collection Group Query)
|
|
420
|
+
* @param templateId - ID of the template
|
|
421
|
+
* @param pageSize - Number of documents to retrieve
|
|
422
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
423
|
+
* @returns Array of filled documents and the last document for pagination
|
|
424
|
+
*/
|
|
425
|
+
async getFilledDocumentsByTemplate(
|
|
426
|
+
templateId: string,
|
|
427
|
+
pageSize = 20,
|
|
428
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
429
|
+
): Promise<{
|
|
430
|
+
documents: FilledDocument[];
|
|
431
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
432
|
+
}> {
|
|
433
|
+
console.warn(
|
|
434
|
+
"getFilledDocumentsByTemplate needs rework for subcollection model or Collection Group Queries."
|
|
435
|
+
);
|
|
436
|
+
// Original logic commented out
|
|
437
|
+
/*
|
|
438
|
+
let q = query(
|
|
439
|
+
this.collectionRef, // LINTER ERROR
|
|
440
|
+
where("templateId", "==", templateId),
|
|
441
|
+
orderBy("updatedAt", "desc"),
|
|
442
|
+
limit(pageSize)
|
|
443
|
+
);
|
|
444
|
+
// ... rest of original logic ...
|
|
445
|
+
*/
|
|
446
|
+
return { documents: [], lastDoc: null }; // Placeholder return
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Get filled documents by status (NEEDS REWORK for subcollections or Collection Group Query)
|
|
451
|
+
* @param status - Status to filter by
|
|
452
|
+
* @param pageSize - Number of documents to retrieve
|
|
453
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
454
|
+
* @returns Array of filled documents and the last document for pagination
|
|
455
|
+
*/
|
|
456
|
+
async getFilledDocumentsByStatus(
|
|
457
|
+
status: FilledDocumentStatus,
|
|
458
|
+
pageSize = 20,
|
|
459
|
+
lastDoc?: QueryDocumentSnapshot<FilledDocument>
|
|
460
|
+
): Promise<{
|
|
461
|
+
documents: FilledDocument[];
|
|
462
|
+
lastDoc: QueryDocumentSnapshot<FilledDocument> | null;
|
|
463
|
+
}> {
|
|
464
|
+
console.warn(
|
|
465
|
+
"getFilledDocumentsByStatus needs rework for subcollection model or Collection Group Queries."
|
|
466
|
+
);
|
|
467
|
+
// Original logic commented out
|
|
468
|
+
/*
|
|
469
|
+
let q = query(
|
|
470
|
+
this.collectionRef, // LINTER ERROR
|
|
471
|
+
where("status", "==", status),
|
|
472
|
+
orderBy("updatedAt", "desc"),
|
|
473
|
+
limit(pageSize)
|
|
474
|
+
);
|
|
475
|
+
// ... rest of original logic ...
|
|
476
|
+
*/
|
|
477
|
+
return { documents: [], lastDoc: null }; // Placeholder return
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Upload a file for a filled document field without updating the document.
|
|
482
|
+
* This method only handles the upload and returns the file value to be used by the UI.
|
|
483
|
+
*
|
|
484
|
+
* @param appointmentId - ID of the appointment
|
|
485
|
+
* @param formId - ID of the filled document
|
|
486
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form
|
|
487
|
+
* @param file - The file to upload
|
|
488
|
+
* @returns The file value object to be stored in the document
|
|
489
|
+
*/
|
|
490
|
+
async uploadFileForFilledDocument(
|
|
491
|
+
appointmentId: string,
|
|
492
|
+
formId: string,
|
|
493
|
+
isUserForm: boolean,
|
|
494
|
+
file: File | Blob
|
|
495
|
+
): Promise<FilledDocumentFileValue> {
|
|
496
|
+
console.log(
|
|
497
|
+
`[FilledDocumentService] Uploading file for form ${formId} in appointment ${appointmentId}`
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// Generate a unique file ID
|
|
501
|
+
const fileId = this.generateId();
|
|
502
|
+
|
|
503
|
+
// Set the path according to the specified structure
|
|
504
|
+
const formType = isUserForm ? "user-form" : "doctor-form";
|
|
505
|
+
const collectionName = `${formType}/${formId}`;
|
|
506
|
+
|
|
507
|
+
// Always use CONFIDENTIAL access level
|
|
508
|
+
const accessLevel = MediaAccessLevel.CONFIDENTIAL;
|
|
509
|
+
|
|
510
|
+
// Upload the file using MediaService
|
|
511
|
+
const mediaMetadata = await this.mediaService.uploadMedia(
|
|
512
|
+
file,
|
|
513
|
+
appointmentId, // Using appointmentId as ownerId
|
|
514
|
+
accessLevel,
|
|
515
|
+
collectionName,
|
|
516
|
+
file instanceof File ? file.name : `file_${fileId}`
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
// Create and return a file value object
|
|
520
|
+
const fileValue: FilledDocumentFileValue = {
|
|
521
|
+
mediaId: mediaMetadata.id,
|
|
522
|
+
url: mediaMetadata.url,
|
|
523
|
+
name: mediaMetadata.name,
|
|
524
|
+
contentType: mediaMetadata.contentType,
|
|
525
|
+
size: mediaMetadata.size,
|
|
526
|
+
uploadedAt: Date.now(),
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
return fileValue;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Upload a signature image for a filled document.
|
|
534
|
+
* This is a specialized version of uploadFileForFilledDocument specifically for signatures.
|
|
535
|
+
*
|
|
536
|
+
* @param appointmentId - ID of the appointment
|
|
537
|
+
* @param formId - ID of the filled document
|
|
538
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form
|
|
539
|
+
* @param signatureBlob - The signature image as a Blob
|
|
540
|
+
* @returns The file value object to be stored in the document
|
|
541
|
+
*/
|
|
542
|
+
async uploadSignatureForFilledDocument(
|
|
543
|
+
appointmentId: string,
|
|
544
|
+
formId: string,
|
|
545
|
+
isUserForm: boolean,
|
|
546
|
+
signatureBlob: Blob
|
|
547
|
+
): Promise<FilledDocumentFileValue> {
|
|
548
|
+
console.log(
|
|
549
|
+
`[FilledDocumentService] Uploading signature for form ${formId}`
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
// Generate a filename for the signature
|
|
553
|
+
const signatureId = this.generateId();
|
|
554
|
+
const signatureFile = new File(
|
|
555
|
+
[signatureBlob],
|
|
556
|
+
`signature_${signatureId}.png`,
|
|
557
|
+
{ type: "image/png" }
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
// Use the general file upload method
|
|
561
|
+
return this.uploadFileForFilledDocument(
|
|
562
|
+
appointmentId,
|
|
563
|
+
formId,
|
|
564
|
+
isUserForm,
|
|
565
|
+
signatureFile
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Delete a file using its mediaId.
|
|
571
|
+
*
|
|
572
|
+
* @param mediaId - ID of the media to delete
|
|
573
|
+
* @returns Promise resolving when the deletion is complete
|
|
574
|
+
*/
|
|
575
|
+
async deleteFile(mediaId: string): Promise<void> {
|
|
576
|
+
console.log(
|
|
577
|
+
`[FilledDocumentService] Deleting file with mediaId ${mediaId}`
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
if (!mediaId) {
|
|
581
|
+
throw new Error("MediaId is required to delete a file");
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Delete the file using MediaService
|
|
585
|
+
await this.mediaService.deleteMedia(mediaId);
|
|
586
|
+
}
|
|
587
|
+
}
|