@blackcode_sa/metaestetics-api 1.6.2 → 1.6.4
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 +228 -25
- package/dist/admin/index.d.ts +228 -25
- package/dist/admin/index.js +35867 -2493
- package/dist/admin/index.mjs +35856 -2464
- package/dist/backoffice/index.d.mts +252 -1
- package/dist/backoffice/index.d.ts +252 -1
- package/dist/backoffice/index.js +86 -12
- package/dist/backoffice/index.mjs +86 -13
- package/dist/index.d.mts +1417 -554
- package/dist/index.d.ts +1417 -554
- package/dist/index.js +1393 -687
- package/dist/index.mjs +1423 -711
- package/package.json +1 -1
- package/src/admin/index.ts +15 -1
- package/src/admin/notifications/notifications.admin.ts +1 -1
- package/src/admin/requirements/README.md +128 -0
- package/src/admin/requirements/patient-requirements.admin.service.ts +482 -0
- package/src/index.ts +16 -1
- package/src/services/appointment/appointment.service.ts +315 -86
- package/src/services/clinic/clinic-admin.service.ts +3 -0
- package/src/services/clinic/clinic-group.service.ts +8 -0
- package/src/services/documentation-templates/documentation-template.service.ts +24 -16
- package/src/services/documentation-templates/filled-document.service.ts +253 -136
- package/src/services/patient/patient.service.ts +31 -1
- package/src/services/patient/patientRequirements.service.ts +285 -0
- package/src/services/patient/utils/practitioner.utils.ts +79 -1
- package/src/types/appointment/index.ts +134 -10
- package/src/types/documentation-templates/index.ts +34 -2
- package/src/types/notifications/README.md +77 -0
- package/src/types/notifications/index.ts +154 -27
- package/src/types/patient/index.ts +6 -0
- package/src/types/patient/patient-requirements.ts +81 -0
- package/src/validations/appointment.schema.ts +300 -62
- package/src/validations/documentation-templates/template.schema.ts +55 -0
- package/src/validations/documentation-templates.schema.ts +9 -14
- package/src/validations/notification.schema.ts +3 -3
- package/src/validations/patient/patient-requirements.schema.ts +75 -0
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
// src/validations/appointment.schema.ts
|
|
2
|
-
import { z } from "zod";
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
3
|
|
|
4
4
|
// src/types/appointment/index.ts
|
|
5
5
|
var AppointmentStatus = /* @__PURE__ */ ((AppointmentStatus2) => {
|
|
6
|
-
AppointmentStatus2["
|
|
6
|
+
AppointmentStatus2["PENDING"] = "pending";
|
|
7
7
|
AppointmentStatus2["CONFIRMED"] = "confirmed";
|
|
8
8
|
AppointmentStatus2["CHECKED_IN"] = "checked_in";
|
|
9
9
|
AppointmentStatus2["IN_PROGRESS"] = "in_progress";
|
|
10
10
|
AppointmentStatus2["COMPLETED"] = "completed";
|
|
11
11
|
AppointmentStatus2["CANCELED_PATIENT"] = "canceled_patient";
|
|
12
|
+
AppointmentStatus2["CANCELED_PATIENT_RESCHEDULED"] = "canceled_patient_rescheduled";
|
|
12
13
|
AppointmentStatus2["CANCELED_CLINIC"] = "canceled_clinic";
|
|
13
14
|
AppointmentStatus2["NO_SHOW"] = "no_show";
|
|
14
|
-
AppointmentStatus2["
|
|
15
|
+
AppointmentStatus2["RESCHEDULED_BY_CLINIC"] = "rescheduled_by_clinic";
|
|
15
16
|
return AppointmentStatus2;
|
|
16
17
|
})(AppointmentStatus || {});
|
|
17
18
|
var PaymentStatus = /* @__PURE__ */ ((PaymentStatus3) => {
|
|
@@ -22,82 +23,444 @@ var PaymentStatus = /* @__PURE__ */ ((PaymentStatus3) => {
|
|
|
22
23
|
PaymentStatus3["NOT_APPLICABLE"] = "not_applicable";
|
|
23
24
|
return PaymentStatus3;
|
|
24
25
|
})(PaymentStatus || {});
|
|
26
|
+
var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
27
|
+
MediaType2["BEFORE_PHOTO"] = "before_photo";
|
|
28
|
+
MediaType2["AFTER_PHOTO"] = "after_photo";
|
|
29
|
+
MediaType2["CONSENT_SCAN"] = "consent_scan";
|
|
30
|
+
MediaType2["OTHER_DOCUMENT"] = "other_document";
|
|
31
|
+
return MediaType2;
|
|
32
|
+
})(MediaType || {});
|
|
25
33
|
var APPOINTMENTS_COLLECTION = "appointments";
|
|
26
34
|
|
|
35
|
+
// src/validations/documentation-templates/template.schema.ts
|
|
36
|
+
import { z } from "zod";
|
|
37
|
+
|
|
38
|
+
// src/types/documentation-templates/index.ts
|
|
39
|
+
var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
|
|
40
|
+
var FILLED_DOCUMENTS_COLLECTION = "filled-documents";
|
|
41
|
+
var USER_FORMS_SUBCOLLECTION = "user-forms";
|
|
42
|
+
var DOCTOR_FORMS_SUBCOLLECTION = "doctor-forms";
|
|
43
|
+
var DocumentElementType = /* @__PURE__ */ ((DocumentElementType2) => {
|
|
44
|
+
DocumentElementType2["HEADING"] = "heading";
|
|
45
|
+
DocumentElementType2["PARAGRAPH"] = "paragraph";
|
|
46
|
+
DocumentElementType2["LIST"] = "list";
|
|
47
|
+
DocumentElementType2["DYNAMIC_TEXT"] = "dynamic_text";
|
|
48
|
+
DocumentElementType2["BINARY_CHOICE"] = "binary_choice";
|
|
49
|
+
DocumentElementType2["MULTIPLE_CHOICE"] = "multiple_choice";
|
|
50
|
+
DocumentElementType2["SINGLE_CHOICE"] = "single_choice";
|
|
51
|
+
DocumentElementType2["RATING_SCALE"] = "rating_scale";
|
|
52
|
+
DocumentElementType2["TEXT_INPUT"] = "text_input";
|
|
53
|
+
DocumentElementType2["DATE_PICKER"] = "date_picker";
|
|
54
|
+
DocumentElementType2["SIGNATURE"] = "signature";
|
|
55
|
+
DocumentElementType2["DITIGAL_SIGNATURE"] = "digital_signature";
|
|
56
|
+
DocumentElementType2["FILE_UPLOAD"] = "file_upload";
|
|
57
|
+
return DocumentElementType2;
|
|
58
|
+
})(DocumentElementType || {});
|
|
59
|
+
var ListType = /* @__PURE__ */ ((ListType2) => {
|
|
60
|
+
ListType2["ORDERED"] = "ordered";
|
|
61
|
+
ListType2["UNORDERED"] = "unordered";
|
|
62
|
+
return ListType2;
|
|
63
|
+
})(ListType || {});
|
|
64
|
+
var HeadingLevel = /* @__PURE__ */ ((HeadingLevel2) => {
|
|
65
|
+
HeadingLevel2["H1"] = "h1";
|
|
66
|
+
HeadingLevel2["H2"] = "h2";
|
|
67
|
+
HeadingLevel2["H3"] = "h3";
|
|
68
|
+
HeadingLevel2["H4"] = "h4";
|
|
69
|
+
HeadingLevel2["H5"] = "h5";
|
|
70
|
+
HeadingLevel2["H6"] = "h6";
|
|
71
|
+
return HeadingLevel2;
|
|
72
|
+
})(HeadingLevel || {});
|
|
73
|
+
var DynamicVariable = /* @__PURE__ */ ((DynamicVariable2) => {
|
|
74
|
+
DynamicVariable2["PATIENT_NAME"] = "[PATIENT_NAME]";
|
|
75
|
+
DynamicVariable2["DOCTOR_NAME"] = "[DOCTOR_NAME]";
|
|
76
|
+
DynamicVariable2["CLINIC_NAME"] = "[CLINIC_NAME]";
|
|
77
|
+
DynamicVariable2["PATIENT_BIRTHDAY"] = "[PATIENT_BIRTHDAY]";
|
|
78
|
+
DynamicVariable2["APPOINTMENT_DATE"] = "[APPOINTMENT_DATE]";
|
|
79
|
+
DynamicVariable2["CURRENT_DATE"] = "[CURRENT_DATE]";
|
|
80
|
+
DynamicVariable2["PROCEDURE_NAME"] = "[PROCEDURE_NAME]";
|
|
81
|
+
DynamicVariable2["PROCEDURE_DESCRIPTION"] = "[PROCEDURE_DESCRIPTION]";
|
|
82
|
+
DynamicVariable2["PROCEDURE_COST"] = "[PROCEDURE_COST]";
|
|
83
|
+
DynamicVariable2["PROCEDURE_DURATION"] = "[PROCEDURE_DURATION]";
|
|
84
|
+
DynamicVariable2["PROCEDURE_RISK"] = "[PROCEDURE_RISK]";
|
|
85
|
+
return DynamicVariable2;
|
|
86
|
+
})(DynamicVariable || {});
|
|
87
|
+
var FilledDocumentStatus = /* @__PURE__ */ ((FilledDocumentStatus2) => {
|
|
88
|
+
FilledDocumentStatus2["DRAFT"] = "draft";
|
|
89
|
+
FilledDocumentStatus2["SKIPPED"] = "skipped";
|
|
90
|
+
FilledDocumentStatus2["PENDING"] = "pending";
|
|
91
|
+
FilledDocumentStatus2["COMPLETED"] = "completed";
|
|
92
|
+
FilledDocumentStatus2["SIGNED"] = "signed";
|
|
93
|
+
FilledDocumentStatus2["REJECTED"] = "rejected";
|
|
94
|
+
return FilledDocumentStatus2;
|
|
95
|
+
})(FilledDocumentStatus || {});
|
|
96
|
+
|
|
97
|
+
// src/validations/documentation-templates/template.schema.ts
|
|
98
|
+
var baseElementSchema = z.object({
|
|
99
|
+
id: z.string().optional(),
|
|
100
|
+
// Make id optional so we can omit it later
|
|
101
|
+
type: z.nativeEnum(DocumentElementType),
|
|
102
|
+
required: z.boolean().optional()
|
|
103
|
+
});
|
|
104
|
+
var headingElementSchema = baseElementSchema.extend({
|
|
105
|
+
type: z.literal("heading" /* HEADING */),
|
|
106
|
+
text: z.string().min(1).max(200),
|
|
107
|
+
level: z.nativeEnum(HeadingLevel)
|
|
108
|
+
});
|
|
109
|
+
var paragraphElementSchema = baseElementSchema.extend({
|
|
110
|
+
type: z.literal("paragraph" /* PARAGRAPH */),
|
|
111
|
+
text: z.string().min(1).max(5e3)
|
|
112
|
+
});
|
|
113
|
+
var listElementSchema = baseElementSchema.extend({
|
|
114
|
+
type: z.literal("list" /* LIST */),
|
|
115
|
+
items: z.array(z.string().min(1).max(500)).min(1).max(100),
|
|
116
|
+
listType: z.nativeEnum(ListType)
|
|
117
|
+
});
|
|
118
|
+
var dynamicTextElementSchema = baseElementSchema.extend({
|
|
119
|
+
type: z.literal("dynamic_text" /* DYNAMIC_TEXT */),
|
|
120
|
+
text: z.string().min(1).max(5e3)
|
|
121
|
+
});
|
|
122
|
+
var binaryChoiceElementSchema = baseElementSchema.extend({
|
|
123
|
+
type: z.literal("binary_choice" /* BINARY_CHOICE */),
|
|
124
|
+
question: z.string().min(1).max(500),
|
|
125
|
+
defaultValue: z.boolean().optional()
|
|
126
|
+
});
|
|
127
|
+
var multipleChoiceElementSchema = baseElementSchema.extend({
|
|
128
|
+
type: z.literal("multiple_choice" /* MULTIPLE_CHOICE */),
|
|
129
|
+
question: z.string().min(1).max(500),
|
|
130
|
+
options: z.array(z.string().min(1).max(200)).min(2).max(50),
|
|
131
|
+
defaultValues: z.array(z.string()).optional()
|
|
132
|
+
});
|
|
133
|
+
var singleChoiceElementSchema = baseElementSchema.extend({
|
|
134
|
+
type: z.literal("single_choice" /* SINGLE_CHOICE */),
|
|
135
|
+
question: z.string().min(1).max(500),
|
|
136
|
+
options: z.array(z.string().min(1).max(200)).min(2).max(50),
|
|
137
|
+
defaultValue: z.string().optional()
|
|
138
|
+
});
|
|
139
|
+
var ratingScaleElementSchema = baseElementSchema.extend({
|
|
140
|
+
type: z.literal("rating_scale" /* RATING_SCALE */),
|
|
141
|
+
question: z.string().min(1).max(500),
|
|
142
|
+
min: z.number().int().min(0).max(10),
|
|
143
|
+
max: z.number().int().min(1).max(10),
|
|
144
|
+
labels: z.record(z.string()).optional(),
|
|
145
|
+
defaultValue: z.number().int().optional()
|
|
146
|
+
});
|
|
147
|
+
var textInputElementSchema = baseElementSchema.extend({
|
|
148
|
+
type: z.literal("text_input" /* TEXT_INPUT */),
|
|
149
|
+
label: z.string().min(1).max(200),
|
|
150
|
+
placeholder: z.string().max(200).optional(),
|
|
151
|
+
multiline: z.boolean().optional(),
|
|
152
|
+
defaultValue: z.string().optional()
|
|
153
|
+
});
|
|
154
|
+
var datePickerElementSchema = baseElementSchema.extend({
|
|
155
|
+
type: z.literal("date_picker" /* DATE_PICKER */),
|
|
156
|
+
label: z.string().min(1).max(200),
|
|
157
|
+
defaultValue: z.string().optional()
|
|
158
|
+
// ISO date string
|
|
159
|
+
});
|
|
160
|
+
var signatureElementSchema = baseElementSchema.extend({
|
|
161
|
+
type: z.literal("signature" /* SIGNATURE */),
|
|
162
|
+
label: z.string().min(1).max(200)
|
|
163
|
+
});
|
|
164
|
+
var digitalSignatureElementSchema = baseElementSchema.extend({
|
|
165
|
+
type: z.literal("digital_signature" /* DITIGAL_SIGNATURE */),
|
|
166
|
+
// Matching your type enum
|
|
167
|
+
label: z.string().min(1).max(200)
|
|
168
|
+
});
|
|
169
|
+
var fileUploadElementSchema = baseElementSchema.extend({
|
|
170
|
+
type: z.literal("file_upload" /* FILE_UPLOAD */),
|
|
171
|
+
label: z.string().min(1).max(200),
|
|
172
|
+
allowedFileTypes: z.array(z.string()).optional(),
|
|
173
|
+
maxFileSizeMB: z.number().positive().optional()
|
|
174
|
+
});
|
|
175
|
+
var documentElementSchema = z.discriminatedUnion("type", [
|
|
176
|
+
headingElementSchema,
|
|
177
|
+
paragraphElementSchema,
|
|
178
|
+
listElementSchema,
|
|
179
|
+
dynamicTextElementSchema,
|
|
180
|
+
binaryChoiceElementSchema,
|
|
181
|
+
multipleChoiceElementSchema,
|
|
182
|
+
singleChoiceElementSchema,
|
|
183
|
+
ratingScaleElementSchema,
|
|
184
|
+
textInputElementSchema,
|
|
185
|
+
datePickerElementSchema,
|
|
186
|
+
signatureElementSchema,
|
|
187
|
+
digitalSignatureElementSchema,
|
|
188
|
+
fileUploadElementSchema
|
|
189
|
+
]);
|
|
190
|
+
var documentElementWithoutIdSchema = z.discriminatedUnion("type", [
|
|
191
|
+
headingElementSchema.omit({ id: true }),
|
|
192
|
+
paragraphElementSchema.omit({ id: true }),
|
|
193
|
+
listElementSchema.omit({ id: true }),
|
|
194
|
+
dynamicTextElementSchema.omit({ id: true }),
|
|
195
|
+
binaryChoiceElementSchema.omit({ id: true }),
|
|
196
|
+
multipleChoiceElementSchema.omit({ id: true }),
|
|
197
|
+
singleChoiceElementSchema.omit({ id: true }),
|
|
198
|
+
ratingScaleElementSchema.omit({ id: true }),
|
|
199
|
+
textInputElementSchema.omit({ id: true }),
|
|
200
|
+
datePickerElementSchema.omit({ id: true }),
|
|
201
|
+
signatureElementSchema.omit({ id: true }),
|
|
202
|
+
digitalSignatureElementSchema.omit({ id: true }),
|
|
203
|
+
fileUploadElementSchema.omit({ id: true })
|
|
204
|
+
]);
|
|
205
|
+
var createDocumentTemplateSchema = z.object({
|
|
206
|
+
title: z.string().min(1).max(200),
|
|
207
|
+
description: z.string().max(1e3).optional(),
|
|
208
|
+
elements: z.array(documentElementWithoutIdSchema).min(1).max(100),
|
|
209
|
+
tags: z.array(z.string().min(1).max(50)).max(20).optional(),
|
|
210
|
+
isUserForm: z.boolean().optional().default(false),
|
|
211
|
+
isRequired: z.boolean().optional().default(false),
|
|
212
|
+
sortingOrder: z.number().int().optional().default(0)
|
|
213
|
+
});
|
|
214
|
+
var updateDocumentTemplateSchema = z.object({
|
|
215
|
+
title: z.string().min(1).max(200).optional(),
|
|
216
|
+
description: z.string().max(1e3).optional(),
|
|
217
|
+
elements: z.array(documentElementWithoutIdSchema).min(1).max(100).optional(),
|
|
218
|
+
tags: z.array(z.string().min(1).max(50)).max(20).optional(),
|
|
219
|
+
isActive: z.boolean().optional(),
|
|
220
|
+
isUserForm: z.boolean().optional(),
|
|
221
|
+
isRequired: z.boolean().optional(),
|
|
222
|
+
sortingOrder: z.number().int().optional()
|
|
223
|
+
});
|
|
224
|
+
var documentTemplateSchema = z.object({
|
|
225
|
+
id: z.string(),
|
|
226
|
+
title: z.string().min(1).max(200),
|
|
227
|
+
description: z.string().max(1e3).optional(),
|
|
228
|
+
createdAt: z.number(),
|
|
229
|
+
updatedAt: z.number(),
|
|
230
|
+
createdBy: z.string(),
|
|
231
|
+
elements: z.array(documentElementSchema),
|
|
232
|
+
tags: z.array(z.string().min(1).max(50)).max(20).optional(),
|
|
233
|
+
version: z.number().int().positive(),
|
|
234
|
+
isActive: z.boolean(),
|
|
235
|
+
isUserForm: z.boolean().optional().default(false),
|
|
236
|
+
isRequired: z.boolean().optional().default(false),
|
|
237
|
+
sortingOrder: z.number().int().optional().default(0)
|
|
238
|
+
});
|
|
239
|
+
var filledDocumentStatusSchema = z.nativeEnum(FilledDocumentStatus);
|
|
240
|
+
var filledDocumentSchema = z.object({
|
|
241
|
+
id: z.string(),
|
|
242
|
+
templateId: z.string(),
|
|
243
|
+
templateVersion: z.number(),
|
|
244
|
+
isUserForm: z.boolean(),
|
|
245
|
+
procedureId: z.string(),
|
|
246
|
+
appointmentId: z.string(),
|
|
247
|
+
patientId: z.string(),
|
|
248
|
+
practitionerId: z.string(),
|
|
249
|
+
clinicId: z.string(),
|
|
250
|
+
createdAt: z.number(),
|
|
251
|
+
updatedAt: z.number(),
|
|
252
|
+
values: z.record(z.string(), z.any()),
|
|
253
|
+
status: filledDocumentStatusSchema
|
|
254
|
+
});
|
|
255
|
+
var createFilledDocumentDataSchema = z.object({
|
|
256
|
+
templateId: z.string(),
|
|
257
|
+
procedureId: z.string(),
|
|
258
|
+
appointmentId: z.string(),
|
|
259
|
+
patientId: z.string(),
|
|
260
|
+
practitionerId: z.string(),
|
|
261
|
+
clinicId: z.string(),
|
|
262
|
+
values: z.record(z.string(), z.any()).optional().default({}),
|
|
263
|
+
status: filledDocumentStatusSchema.optional().default("draft" /* DRAFT */)
|
|
264
|
+
});
|
|
265
|
+
var updateFilledDocumentDataSchema = z.object({
|
|
266
|
+
values: z.record(z.string(), z.any()).optional(),
|
|
267
|
+
status: filledDocumentStatusSchema.optional()
|
|
268
|
+
});
|
|
269
|
+
|
|
27
270
|
// src/validations/appointment.schema.ts
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
271
|
+
var MIN_STRING_LENGTH = 1;
|
|
272
|
+
var MAX_STRING_LENGTH = 1024;
|
|
273
|
+
var MAX_STRING_LENGTH_LONG = 4096;
|
|
274
|
+
var MAX_ARRAY_LENGTH = 100;
|
|
275
|
+
var appointmentStatusSchema = z2.nativeEnum(AppointmentStatus);
|
|
276
|
+
var paymentStatusSchema = z2.nativeEnum(PaymentStatus);
|
|
277
|
+
var mediaTypeSchema = z2.nativeEnum(MediaType);
|
|
278
|
+
var appointmentMediaItemSchema = z2.object({
|
|
279
|
+
id: z2.string().min(MIN_STRING_LENGTH, "Media item ID is required"),
|
|
280
|
+
type: mediaTypeSchema,
|
|
281
|
+
url: z2.string().url("Media URL must be a valid URL"),
|
|
282
|
+
fileName: z2.string().optional(),
|
|
283
|
+
uploadedAt: z2.any().refine(
|
|
284
|
+
(val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
285
|
+
"uploadedAt must be a valid timestamp or Date object"
|
|
286
|
+
),
|
|
287
|
+
uploadedBy: z2.string().min(MIN_STRING_LENGTH, "Uploaded by user ID is required"),
|
|
288
|
+
description: z2.string().max(MAX_STRING_LENGTH, "Description too long").optional()
|
|
289
|
+
});
|
|
290
|
+
var procedureExtendedInfoSchema = z2.object({
|
|
291
|
+
id: z2.string().min(MIN_STRING_LENGTH),
|
|
292
|
+
name: z2.string().min(MIN_STRING_LENGTH),
|
|
293
|
+
description: z2.string(),
|
|
294
|
+
cost: z2.number().min(0),
|
|
295
|
+
duration: z2.number().min(0),
|
|
296
|
+
procedureFamily: z2.any(),
|
|
297
|
+
procedureCategoryId: z2.string(),
|
|
298
|
+
procedureCategoryName: z2.string(),
|
|
299
|
+
procedureSubCategoryId: z2.string(),
|
|
300
|
+
procedureSubCategoryName: z2.string(),
|
|
301
|
+
procedureTechnologyId: z2.string(),
|
|
302
|
+
procedureTechnologyName: z2.string(),
|
|
303
|
+
procedureProductBrandId: z2.string(),
|
|
304
|
+
procedureProductBrandName: z2.string(),
|
|
305
|
+
procedureProductId: z2.string(),
|
|
306
|
+
procedureProductName: z2.string()
|
|
307
|
+
});
|
|
308
|
+
var linkedFormInfoSchema = z2.object({
|
|
309
|
+
formId: z2.string().min(MIN_STRING_LENGTH, "Form ID is required"),
|
|
310
|
+
templateId: z2.string().min(MIN_STRING_LENGTH, "Template ID is required"),
|
|
311
|
+
templateVersion: z2.number().int().positive("Template version must be a positive integer"),
|
|
312
|
+
title: z2.string().min(MIN_STRING_LENGTH, "Form title is required"),
|
|
313
|
+
isUserForm: z2.boolean(),
|
|
314
|
+
status: filledDocumentStatusSchema,
|
|
315
|
+
path: z2.string().min(MIN_STRING_LENGTH, "Form path is required"),
|
|
316
|
+
submittedAt: z2.any().refine(
|
|
317
|
+
(val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
318
|
+
"submittedAt must be a valid timestamp or Date object"
|
|
319
|
+
).optional(),
|
|
320
|
+
completedAt: z2.any().refine(
|
|
321
|
+
(val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
322
|
+
"completedAt must be a valid timestamp or Date object"
|
|
323
|
+
).optional()
|
|
324
|
+
});
|
|
325
|
+
var patientReviewInfoSchema = z2.object({
|
|
326
|
+
reviewId: z2.string().min(MIN_STRING_LENGTH, "Review ID is required"),
|
|
327
|
+
rating: z2.number().min(1).max(5, "Rating must be between 1 and 5"),
|
|
328
|
+
comment: z2.string().max(MAX_STRING_LENGTH_LONG, "Comment too long").optional(),
|
|
329
|
+
reviewedAt: z2.any().refine(
|
|
330
|
+
(val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
331
|
+
"reviewedAt must be a valid timestamp or Date object"
|
|
332
|
+
)
|
|
333
|
+
});
|
|
334
|
+
var finalizedDetailsSchema = z2.object({
|
|
335
|
+
by: z2.string().min(MIN_STRING_LENGTH, "Finalized by user ID is required"),
|
|
336
|
+
at: z2.any().refine(
|
|
337
|
+
(val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
338
|
+
"Finalized at must be a valid timestamp or Date object"
|
|
339
|
+
),
|
|
340
|
+
notes: z2.string().max(MAX_STRING_LENGTH_LONG, "Finalization notes too long").optional()
|
|
341
|
+
});
|
|
342
|
+
var createAppointmentSchema = z2.object({
|
|
343
|
+
calendarEventId: z2.string().min(MIN_STRING_LENGTH, "Calendar event ID is required"),
|
|
344
|
+
clinicBranchId: z2.string().min(MIN_STRING_LENGTH, "Clinic branch ID is required"),
|
|
345
|
+
practitionerId: z2.string().min(MIN_STRING_LENGTH, "Practitioner ID is required"),
|
|
346
|
+
patientId: z2.string().min(MIN_STRING_LENGTH, "Patient ID is required"),
|
|
347
|
+
procedureId: z2.string().min(MIN_STRING_LENGTH, "Procedure ID is required"),
|
|
348
|
+
appointmentStartTime: z2.any().refine(
|
|
349
|
+
(val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
350
|
+
"Appointment start time must be a valid timestamp or Date object"
|
|
37
351
|
),
|
|
38
|
-
appointmentEndTime:
|
|
39
|
-
(val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0,
|
|
40
|
-
"Appointment end time must be a valid timestamp"
|
|
352
|
+
appointmentEndTime: z2.any().refine(
|
|
353
|
+
(val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
354
|
+
"Appointment end time must be a valid timestamp or Date object"
|
|
41
355
|
),
|
|
42
|
-
cost:
|
|
43
|
-
currency:
|
|
44
|
-
patientNotes:
|
|
45
|
-
initialStatus:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}).optional().default("unpaid" /* UNPAID */)
|
|
356
|
+
cost: z2.number().min(0, "Cost must be a non-negative number"),
|
|
357
|
+
currency: z2.string().min(1, "Currency is required"),
|
|
358
|
+
patientNotes: z2.string().max(MAX_STRING_LENGTH, "Patient notes too long").nullable().optional(),
|
|
359
|
+
initialStatus: appointmentStatusSchema,
|
|
360
|
+
initialPaymentStatus: paymentStatusSchema.optional().default("unpaid" /* UNPAID */)
|
|
361
|
+
}).refine((data) => data.appointmentEndTime > data.appointmentStartTime, {
|
|
362
|
+
message: "Appointment end time must be after start time",
|
|
363
|
+
path: ["appointmentEndTime"]
|
|
51
364
|
});
|
|
52
|
-
var updateAppointmentSchema =
|
|
53
|
-
status:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
365
|
+
var updateAppointmentSchema = z2.object({
|
|
366
|
+
status: appointmentStatusSchema.optional(),
|
|
367
|
+
confirmationTime: z2.any().optional().nullable(),
|
|
368
|
+
cancellationTime: z2.any().optional().nullable(),
|
|
369
|
+
rescheduleTime: z2.any().optional().nullable(),
|
|
370
|
+
procedureActualStartTime: z2.any().optional().nullable(),
|
|
371
|
+
actualDurationMinutes: z2.number().int().positive("Duration must be a positive integer").optional(),
|
|
372
|
+
cancellationReason: z2.string().max(MAX_STRING_LENGTH, "Cancellation reason too long").nullable().optional(),
|
|
373
|
+
canceledBy: z2.enum(["patient", "clinic", "practitioner", "system"]).optional(),
|
|
374
|
+
internalNotes: z2.string().max(MAX_STRING_LENGTH_LONG, "Internal notes too long").nullable().optional(),
|
|
375
|
+
patientNotes: z2.any().optional().nullable(),
|
|
376
|
+
paymentStatus: paymentStatusSchema.optional(),
|
|
377
|
+
paymentTransactionId: z2.any().optional().nullable(),
|
|
378
|
+
completedPreRequirements: z2.union([z2.array(z2.string()), z2.any()]).optional(),
|
|
379
|
+
completedPostRequirements: z2.union([z2.array(z2.string()), z2.any()]).optional(),
|
|
380
|
+
pendingUserFormsIds: z2.union([z2.array(z2.string()), z2.any()]).optional(),
|
|
381
|
+
appointmentStartTime: z2.any().refine(
|
|
382
|
+
(val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
383
|
+
"Appointment start time must be a valid timestamp or Date object"
|
|
59
384
|
).optional(),
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
385
|
+
appointmentEndTime: z2.any().refine(
|
|
386
|
+
(val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
387
|
+
"Appointment end time must be a valid timestamp or Date object"
|
|
388
|
+
).optional(),
|
|
389
|
+
calendarEventId: z2.string().min(MIN_STRING_LENGTH).optional(),
|
|
390
|
+
cost: z2.number().min(0).optional(),
|
|
391
|
+
clinicBranchId: z2.string().min(MIN_STRING_LENGTH).optional(),
|
|
392
|
+
practitionerId: z2.string().min(MIN_STRING_LENGTH).optional(),
|
|
393
|
+
linkedForms: z2.union([z2.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z2.any()]).optional(),
|
|
394
|
+
media: z2.union([
|
|
395
|
+
z2.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
|
|
396
|
+
z2.any()
|
|
397
|
+
]).optional(),
|
|
398
|
+
reviewInfo: z2.union([patientReviewInfoSchema.nullable(), z2.any()]).optional(),
|
|
399
|
+
finalizedDetails: z2.union([finalizedDetailsSchema.nullable(), z2.any()]).optional(),
|
|
400
|
+
isArchived: z2.boolean().optional(),
|
|
401
|
+
updatedAt: z2.any().optional()
|
|
70
402
|
}).refine(
|
|
71
403
|
(data) => {
|
|
72
|
-
if (data.status === "canceled_clinic" /* CANCELED_CLINIC */ || data.status === "canceled_patient" /* CANCELED_PATIENT */) {
|
|
404
|
+
if (data.status === "canceled_clinic" /* CANCELED_CLINIC */ || data.status === "canceled_patient" /* CANCELED_PATIENT */ || data.status === "canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */) {
|
|
73
405
|
return !!data.cancellationReason && !!data.canceledBy;
|
|
74
406
|
}
|
|
75
407
|
return true;
|
|
76
408
|
},
|
|
77
409
|
{
|
|
78
|
-
message: "Cancellation reason and canceled by must be provided when canceling an appointment",
|
|
410
|
+
message: "Cancellation reason and canceled by must be provided when canceling an appointment with patient or clinic origin.",
|
|
79
411
|
path: ["status"]
|
|
80
412
|
}
|
|
413
|
+
).refine(
|
|
414
|
+
(data) => {
|
|
415
|
+
if (data.appointmentStartTime && data.appointmentEndTime) {
|
|
416
|
+
return data.appointmentEndTime > data.appointmentStartTime;
|
|
417
|
+
}
|
|
418
|
+
return true;
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
message: "Appointment end time must be after start time if both are provided",
|
|
422
|
+
path: ["appointmentEndTime"]
|
|
423
|
+
}
|
|
81
424
|
);
|
|
82
|
-
var searchAppointmentsSchema =
|
|
83
|
-
patientId:
|
|
84
|
-
practitionerId:
|
|
85
|
-
clinicBranchId:
|
|
86
|
-
startDate:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
425
|
+
var searchAppointmentsSchema = z2.object({
|
|
426
|
+
patientId: z2.string().optional(),
|
|
427
|
+
practitionerId: z2.string().optional(),
|
|
428
|
+
clinicBranchId: z2.string().optional(),
|
|
429
|
+
startDate: z2.any().refine(
|
|
430
|
+
(val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
431
|
+
"Start date must be a valid timestamp or Date object"
|
|
432
|
+
).optional(),
|
|
433
|
+
endDate: z2.any().refine(
|
|
434
|
+
(val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || typeof val === "number",
|
|
435
|
+
"End date must be a valid timestamp or Date object"
|
|
436
|
+
).optional(),
|
|
437
|
+
status: z2.union([
|
|
438
|
+
appointmentStatusSchema,
|
|
439
|
+
z2.array(appointmentStatusSchema).nonempty()
|
|
91
440
|
]).optional(),
|
|
92
|
-
limit:
|
|
93
|
-
startAfter:
|
|
441
|
+
limit: z2.number().positive().int().optional().default(20),
|
|
442
|
+
startAfter: z2.any().optional()
|
|
94
443
|
}).refine(
|
|
95
444
|
(data) => {
|
|
96
|
-
|
|
445
|
+
if (!data.startDate && !data.endDate && !data.status) {
|
|
446
|
+
return !!(data.patientId || data.practitionerId || data.clinicBranchId);
|
|
447
|
+
}
|
|
448
|
+
return true;
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
message: "At least one of patientId, practitionerId, or clinicBranchId must be provided if no date or status filters are set.",
|
|
452
|
+
path: ["patientId"]
|
|
453
|
+
}
|
|
454
|
+
).refine(
|
|
455
|
+
(data) => {
|
|
456
|
+
if (data.startDate && data.endDate) {
|
|
457
|
+
return data.endDate >= data.startDate;
|
|
458
|
+
}
|
|
459
|
+
return true;
|
|
97
460
|
},
|
|
98
461
|
{
|
|
99
|
-
message: "
|
|
100
|
-
path: ["
|
|
462
|
+
message: "End date must be after or the same as start date",
|
|
463
|
+
path: ["endDate"]
|
|
101
464
|
}
|
|
102
465
|
);
|
|
103
466
|
|
|
@@ -165,54 +528,6 @@ import {
|
|
|
165
528
|
getDocs as getDocs13
|
|
166
529
|
} from "firebase/firestore";
|
|
167
530
|
|
|
168
|
-
// src/types/documentation-templates/index.ts
|
|
169
|
-
var DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
|
|
170
|
-
var FILLED_DOCUMENTS_COLLECTION = "filled-documents";
|
|
171
|
-
var DocumentElementType = /* @__PURE__ */ ((DocumentElementType2) => {
|
|
172
|
-
DocumentElementType2["HEADING"] = "heading";
|
|
173
|
-
DocumentElementType2["PARAGRAPH"] = "paragraph";
|
|
174
|
-
DocumentElementType2["LIST"] = "list";
|
|
175
|
-
DocumentElementType2["DYNAMIC_TEXT"] = "dynamic_text";
|
|
176
|
-
DocumentElementType2["BINARY_CHOICE"] = "binary_choice";
|
|
177
|
-
DocumentElementType2["MULTIPLE_CHOICE"] = "multiple_choice";
|
|
178
|
-
DocumentElementType2["SINGLE_CHOICE"] = "single_choice";
|
|
179
|
-
DocumentElementType2["RATING_SCALE"] = "rating_scale";
|
|
180
|
-
DocumentElementType2["TEXT_INPUT"] = "text_input";
|
|
181
|
-
DocumentElementType2["DATE_PICKER"] = "date_picker";
|
|
182
|
-
DocumentElementType2["SIGNATURE"] = "signature";
|
|
183
|
-
DocumentElementType2["FILE_UPLOAD"] = "file_upload";
|
|
184
|
-
return DocumentElementType2;
|
|
185
|
-
})(DocumentElementType || {});
|
|
186
|
-
var ListType = /* @__PURE__ */ ((ListType2) => {
|
|
187
|
-
ListType2["ORDERED"] = "ordered";
|
|
188
|
-
ListType2["UNORDERED"] = "unordered";
|
|
189
|
-
return ListType2;
|
|
190
|
-
})(ListType || {});
|
|
191
|
-
var HeadingLevel = /* @__PURE__ */ ((HeadingLevel2) => {
|
|
192
|
-
HeadingLevel2["H1"] = "h1";
|
|
193
|
-
HeadingLevel2["H2"] = "h2";
|
|
194
|
-
HeadingLevel2["H3"] = "h3";
|
|
195
|
-
HeadingLevel2["H4"] = "h4";
|
|
196
|
-
HeadingLevel2["H5"] = "h5";
|
|
197
|
-
HeadingLevel2["H6"] = "h6";
|
|
198
|
-
return HeadingLevel2;
|
|
199
|
-
})(HeadingLevel || {});
|
|
200
|
-
var DynamicVariable = /* @__PURE__ */ ((DynamicVariable2) => {
|
|
201
|
-
DynamicVariable2["PATIENT_NAME"] = "[PATIENT_NAME]";
|
|
202
|
-
DynamicVariable2["DOCTOR_NAME"] = "[DOCTOR_NAME]";
|
|
203
|
-
DynamicVariable2["CLINIC_NAME"] = "[CLINIC_NAME]";
|
|
204
|
-
DynamicVariable2["PATIENT_BIRTHDAY"] = "[PATIENT_BIRTHDAY]";
|
|
205
|
-
DynamicVariable2["APPOINTMENT_DATE"] = "[APPOINTMENT_DATE]";
|
|
206
|
-
DynamicVariable2["CURRENT_DATE"] = "[CURRENT_DATE]";
|
|
207
|
-
return DynamicVariable2;
|
|
208
|
-
})(DynamicVariable || {});
|
|
209
|
-
var FilledDocumentStatus = /* @__PURE__ */ ((FilledDocumentStatus2) => {
|
|
210
|
-
FilledDocumentStatus2["DRAFT"] = "draft";
|
|
211
|
-
FilledDocumentStatus2["COMPLETED"] = "completed";
|
|
212
|
-
FilledDocumentStatus2["SIGNED"] = "signed";
|
|
213
|
-
return FilledDocumentStatus2;
|
|
214
|
-
})(FilledDocumentStatus || {});
|
|
215
|
-
|
|
216
531
|
// src/types/calendar/index.ts
|
|
217
532
|
var CalendarEventStatus = /* @__PURE__ */ ((CalendarEventStatus4) => {
|
|
218
533
|
CalendarEventStatus4["PENDING"] = "pending";
|
|
@@ -259,136 +574,6 @@ import { z as z19 } from "zod";
|
|
|
259
574
|
|
|
260
575
|
// src/validations/schemas.ts
|
|
261
576
|
import { z as z3 } from "zod";
|
|
262
|
-
|
|
263
|
-
// src/validations/documentation-templates/template.schema.ts
|
|
264
|
-
import { z as z2 } from "zod";
|
|
265
|
-
var baseElementSchema = z2.object({
|
|
266
|
-
id: z2.string().optional(),
|
|
267
|
-
// Make id optional so we can omit it later
|
|
268
|
-
type: z2.nativeEnum(DocumentElementType),
|
|
269
|
-
required: z2.boolean().optional()
|
|
270
|
-
});
|
|
271
|
-
var headingElementSchema = baseElementSchema.extend({
|
|
272
|
-
type: z2.literal("heading" /* HEADING */),
|
|
273
|
-
text: z2.string().min(1).max(200),
|
|
274
|
-
level: z2.nativeEnum(HeadingLevel)
|
|
275
|
-
});
|
|
276
|
-
var paragraphElementSchema = baseElementSchema.extend({
|
|
277
|
-
type: z2.literal("paragraph" /* PARAGRAPH */),
|
|
278
|
-
text: z2.string().min(1).max(5e3)
|
|
279
|
-
});
|
|
280
|
-
var listElementSchema = baseElementSchema.extend({
|
|
281
|
-
type: z2.literal("list" /* LIST */),
|
|
282
|
-
items: z2.array(z2.string().min(1).max(500)).min(1).max(100),
|
|
283
|
-
listType: z2.nativeEnum(ListType)
|
|
284
|
-
});
|
|
285
|
-
var dynamicTextElementSchema = baseElementSchema.extend({
|
|
286
|
-
type: z2.literal("dynamic_text" /* DYNAMIC_TEXT */),
|
|
287
|
-
text: z2.string().min(1).max(5e3)
|
|
288
|
-
});
|
|
289
|
-
var binaryChoiceElementSchema = baseElementSchema.extend({
|
|
290
|
-
type: z2.literal("binary_choice" /* BINARY_CHOICE */),
|
|
291
|
-
question: z2.string().min(1).max(500),
|
|
292
|
-
defaultValue: z2.boolean().optional()
|
|
293
|
-
});
|
|
294
|
-
var multipleChoiceElementSchema = baseElementSchema.extend({
|
|
295
|
-
type: z2.literal("multiple_choice" /* MULTIPLE_CHOICE */),
|
|
296
|
-
question: z2.string().min(1).max(500),
|
|
297
|
-
options: z2.array(z2.string().min(1).max(200)).min(2).max(50),
|
|
298
|
-
defaultValues: z2.array(z2.string()).optional()
|
|
299
|
-
});
|
|
300
|
-
var singleChoiceElementSchema = baseElementSchema.extend({
|
|
301
|
-
type: z2.literal("single_choice" /* SINGLE_CHOICE */),
|
|
302
|
-
question: z2.string().min(1).max(500),
|
|
303
|
-
options: z2.array(z2.string().min(1).max(200)).min(2).max(50),
|
|
304
|
-
defaultValue: z2.string().optional()
|
|
305
|
-
});
|
|
306
|
-
var ratingScaleElementSchema = baseElementSchema.extend({
|
|
307
|
-
type: z2.literal("rating_scale" /* RATING_SCALE */),
|
|
308
|
-
question: z2.string().min(1).max(500),
|
|
309
|
-
min: z2.number().int().min(0).max(10),
|
|
310
|
-
max: z2.number().int().min(1).max(10),
|
|
311
|
-
labels: z2.record(z2.string()).optional(),
|
|
312
|
-
defaultValue: z2.number().int().optional()
|
|
313
|
-
});
|
|
314
|
-
var textInputElementSchema = baseElementSchema.extend({
|
|
315
|
-
type: z2.literal("text_input" /* TEXT_INPUT */),
|
|
316
|
-
label: z2.string().min(1).max(200),
|
|
317
|
-
placeholder: z2.string().max(200).optional(),
|
|
318
|
-
multiline: z2.boolean().optional(),
|
|
319
|
-
defaultValue: z2.string().optional()
|
|
320
|
-
});
|
|
321
|
-
var datePickerElementSchema = baseElementSchema.extend({
|
|
322
|
-
type: z2.literal("date_picker" /* DATE_PICKER */),
|
|
323
|
-
label: z2.string().min(1).max(200),
|
|
324
|
-
defaultValue: z2.string().optional()
|
|
325
|
-
// ISO date string
|
|
326
|
-
});
|
|
327
|
-
var signatureElementSchema = baseElementSchema.extend({
|
|
328
|
-
type: z2.literal("signature" /* SIGNATURE */),
|
|
329
|
-
label: z2.string().min(1).max(200)
|
|
330
|
-
});
|
|
331
|
-
var fileUploadElementSchema = baseElementSchema.extend({
|
|
332
|
-
type: z2.literal("file_upload" /* FILE_UPLOAD */),
|
|
333
|
-
label: z2.string().min(1).max(200),
|
|
334
|
-
allowedFileTypes: z2.array(z2.string()).optional(),
|
|
335
|
-
maxFileSizeMB: z2.number().positive().optional()
|
|
336
|
-
});
|
|
337
|
-
var documentElementSchema = z2.discriminatedUnion("type", [
|
|
338
|
-
headingElementSchema,
|
|
339
|
-
paragraphElementSchema,
|
|
340
|
-
listElementSchema,
|
|
341
|
-
dynamicTextElementSchema,
|
|
342
|
-
binaryChoiceElementSchema,
|
|
343
|
-
multipleChoiceElementSchema,
|
|
344
|
-
singleChoiceElementSchema,
|
|
345
|
-
ratingScaleElementSchema,
|
|
346
|
-
textInputElementSchema,
|
|
347
|
-
datePickerElementSchema,
|
|
348
|
-
signatureElementSchema,
|
|
349
|
-
fileUploadElementSchema
|
|
350
|
-
]);
|
|
351
|
-
var documentElementWithoutIdSchema = z2.discriminatedUnion("type", [
|
|
352
|
-
headingElementSchema.omit({ id: true }),
|
|
353
|
-
paragraphElementSchema.omit({ id: true }),
|
|
354
|
-
listElementSchema.omit({ id: true }),
|
|
355
|
-
dynamicTextElementSchema.omit({ id: true }),
|
|
356
|
-
binaryChoiceElementSchema.omit({ id: true }),
|
|
357
|
-
multipleChoiceElementSchema.omit({ id: true }),
|
|
358
|
-
singleChoiceElementSchema.omit({ id: true }),
|
|
359
|
-
ratingScaleElementSchema.omit({ id: true }),
|
|
360
|
-
textInputElementSchema.omit({ id: true }),
|
|
361
|
-
datePickerElementSchema.omit({ id: true }),
|
|
362
|
-
signatureElementSchema.omit({ id: true }),
|
|
363
|
-
fileUploadElementSchema.omit({ id: true })
|
|
364
|
-
]);
|
|
365
|
-
var createDocumentTemplateSchema = z2.object({
|
|
366
|
-
title: z2.string().min(1).max(200),
|
|
367
|
-
description: z2.string().max(1e3).optional(),
|
|
368
|
-
elements: z2.array(documentElementWithoutIdSchema).min(1).max(100),
|
|
369
|
-
tags: z2.array(z2.string().min(1).max(50)).max(20).optional()
|
|
370
|
-
});
|
|
371
|
-
var updateDocumentTemplateSchema = z2.object({
|
|
372
|
-
title: z2.string().min(1).max(200).optional(),
|
|
373
|
-
description: z2.string().max(1e3).optional(),
|
|
374
|
-
elements: z2.array(documentElementWithoutIdSchema).min(1).max(100).optional(),
|
|
375
|
-
tags: z2.array(z2.string().min(1).max(50)).max(20).optional(),
|
|
376
|
-
isActive: z2.boolean().optional()
|
|
377
|
-
});
|
|
378
|
-
var documentTemplateSchema = z2.object({
|
|
379
|
-
id: z2.string(),
|
|
380
|
-
title: z2.string().min(1).max(200),
|
|
381
|
-
description: z2.string().max(1e3).optional(),
|
|
382
|
-
createdAt: z2.number(),
|
|
383
|
-
updatedAt: z2.number(),
|
|
384
|
-
createdBy: z2.string(),
|
|
385
|
-
elements: z2.array(documentElementSchema),
|
|
386
|
-
tags: z2.array(z2.string().min(1).max(50)).max(20).optional(),
|
|
387
|
-
version: z2.number().int().positive(),
|
|
388
|
-
isActive: z2.boolean()
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// src/validations/schemas.ts
|
|
392
577
|
var emailSchema = z3.string().email("Invalid email format").min(5, "Email must be at least 5 characters").max(255, "Email must be less than 255 characters");
|
|
393
578
|
var passwordSchema = z3.string().min(8, "Password must be at least 8 characters").max(100, "Password must be less than 100 characters").regex(
|
|
394
579
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d\w\W]{8,}$/,
|
|
@@ -1452,9 +1637,9 @@ var addAllergyUtil = async (db, patientId, data, userRef) => {
|
|
|
1452
1637
|
var updateAllergyUtil = async (db, patientId, data, userRef) => {
|
|
1453
1638
|
const validatedData = updateAllergySchema.parse(data);
|
|
1454
1639
|
const { allergyIndex, ...updateData } = validatedData;
|
|
1455
|
-
const
|
|
1456
|
-
if (!
|
|
1457
|
-
const medicalInfo =
|
|
1640
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1641
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1642
|
+
const medicalInfo = doc33.data();
|
|
1458
1643
|
if (allergyIndex >= medicalInfo.allergies.length) {
|
|
1459
1644
|
throw new Error("Invalid allergy index");
|
|
1460
1645
|
}
|
|
@@ -1470,9 +1655,9 @@ var updateAllergyUtil = async (db, patientId, data, userRef) => {
|
|
|
1470
1655
|
});
|
|
1471
1656
|
};
|
|
1472
1657
|
var removeAllergyUtil = async (db, patientId, allergyIndex, userRef) => {
|
|
1473
|
-
const
|
|
1474
|
-
if (!
|
|
1475
|
-
const medicalInfo =
|
|
1658
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1659
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1660
|
+
const medicalInfo = doc33.data();
|
|
1476
1661
|
if (allergyIndex >= medicalInfo.allergies.length) {
|
|
1477
1662
|
throw new Error("Invalid allergy index");
|
|
1478
1663
|
}
|
|
@@ -1497,9 +1682,9 @@ var addBlockingConditionUtil = async (db, patientId, data, userRef) => {
|
|
|
1497
1682
|
var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
|
|
1498
1683
|
const validatedData = updateBlockingConditionSchema.parse(data);
|
|
1499
1684
|
const { conditionIndex, ...updateData } = validatedData;
|
|
1500
|
-
const
|
|
1501
|
-
if (!
|
|
1502
|
-
const medicalInfo =
|
|
1685
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1686
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1687
|
+
const medicalInfo = doc33.data();
|
|
1503
1688
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
1504
1689
|
throw new Error("Invalid blocking condition index");
|
|
1505
1690
|
}
|
|
@@ -1515,9 +1700,9 @@ var updateBlockingConditionUtil = async (db, patientId, data, userRef) => {
|
|
|
1515
1700
|
});
|
|
1516
1701
|
};
|
|
1517
1702
|
var removeBlockingConditionUtil = async (db, patientId, conditionIndex, userRef) => {
|
|
1518
|
-
const
|
|
1519
|
-
if (!
|
|
1520
|
-
const medicalInfo =
|
|
1703
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1704
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1705
|
+
const medicalInfo = doc33.data();
|
|
1521
1706
|
if (conditionIndex >= medicalInfo.blockingConditions.length) {
|
|
1522
1707
|
throw new Error("Invalid blocking condition index");
|
|
1523
1708
|
}
|
|
@@ -1542,9 +1727,9 @@ var addContraindicationUtil = async (db, patientId, data, userRef) => {
|
|
|
1542
1727
|
var updateContraindicationUtil = async (db, patientId, data, userRef) => {
|
|
1543
1728
|
const validatedData = updateContraindicationSchema.parse(data);
|
|
1544
1729
|
const { contraindicationIndex, ...updateData } = validatedData;
|
|
1545
|
-
const
|
|
1546
|
-
if (!
|
|
1547
|
-
const medicalInfo =
|
|
1730
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1731
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1732
|
+
const medicalInfo = doc33.data();
|
|
1548
1733
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
1549
1734
|
throw new Error("Invalid contraindication index");
|
|
1550
1735
|
}
|
|
@@ -1560,9 +1745,9 @@ var updateContraindicationUtil = async (db, patientId, data, userRef) => {
|
|
|
1560
1745
|
});
|
|
1561
1746
|
};
|
|
1562
1747
|
var removeContraindicationUtil = async (db, patientId, contraindicationIndex, userRef) => {
|
|
1563
|
-
const
|
|
1564
|
-
if (!
|
|
1565
|
-
const medicalInfo =
|
|
1748
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1749
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1750
|
+
const medicalInfo = doc33.data();
|
|
1566
1751
|
if (contraindicationIndex >= medicalInfo.contraindications.length) {
|
|
1567
1752
|
throw new Error("Invalid contraindication index");
|
|
1568
1753
|
}
|
|
@@ -1587,9 +1772,9 @@ var addMedicationUtil = async (db, patientId, data, userRef) => {
|
|
|
1587
1772
|
var updateMedicationUtil = async (db, patientId, data, userRef) => {
|
|
1588
1773
|
const validatedData = updateMedicationSchema.parse(data);
|
|
1589
1774
|
const { medicationIndex, ...updateData } = validatedData;
|
|
1590
|
-
const
|
|
1591
|
-
if (!
|
|
1592
|
-
const medicalInfo =
|
|
1775
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1776
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1777
|
+
const medicalInfo = doc33.data();
|
|
1593
1778
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
1594
1779
|
throw new Error("Invalid medication index");
|
|
1595
1780
|
}
|
|
@@ -1605,9 +1790,9 @@ var updateMedicationUtil = async (db, patientId, data, userRef) => {
|
|
|
1605
1790
|
});
|
|
1606
1791
|
};
|
|
1607
1792
|
var removeMedicationUtil = async (db, patientId, medicationIndex, userRef) => {
|
|
1608
|
-
const
|
|
1609
|
-
if (!
|
|
1610
|
-
const medicalInfo =
|
|
1793
|
+
const doc33 = await getDoc3(getMedicalInfoDocRef(db, patientId));
|
|
1794
|
+
if (!doc33.exists()) throw new Error("Medical info not found");
|
|
1795
|
+
const medicalInfo = doc33.data();
|
|
1611
1796
|
if (medicationIndex >= medicalInfo.currentMedications.length) {
|
|
1612
1797
|
throw new Error("Invalid medication index");
|
|
1613
1798
|
}
|
|
@@ -1885,7 +2070,7 @@ var searchPatientsUtil = async (db, params, requester) => {
|
|
|
1885
2070
|
try {
|
|
1886
2071
|
const finalQuery = query2(patientsCollectionRef, ...constraints);
|
|
1887
2072
|
const querySnapshot = await getDocs2(finalQuery);
|
|
1888
|
-
const patients = querySnapshot.docs.map((
|
|
2073
|
+
const patients = querySnapshot.docs.map((doc33) => doc33.data());
|
|
1889
2074
|
console.log(`[searchPatientsUtil] Found ${patients.length} patients matching criteria.`);
|
|
1890
2075
|
return patients;
|
|
1891
2076
|
} catch (error) {
|
|
@@ -1909,8 +2094,8 @@ var getAllPatientsUtil = async (db, options) => {
|
|
|
1909
2094
|
}
|
|
1910
2095
|
const patientsSnapshot = await getDocs2(q);
|
|
1911
2096
|
const patients = [];
|
|
1912
|
-
patientsSnapshot.forEach((
|
|
1913
|
-
patients.push(
|
|
2097
|
+
patientsSnapshot.forEach((doc33) => {
|
|
2098
|
+
patients.push(doc33.data());
|
|
1914
2099
|
});
|
|
1915
2100
|
console.log(`[getAllPatientsUtil] Found ${patients.length} patients`);
|
|
1916
2101
|
return patients;
|
|
@@ -2143,8 +2328,8 @@ var getPatientsByPractitionerUtil = async (db, practitionerId, options) => {
|
|
|
2143
2328
|
}
|
|
2144
2329
|
const patientsSnapshot = await getDocs3(q);
|
|
2145
2330
|
const patients = [];
|
|
2146
|
-
patientsSnapshot.forEach((
|
|
2147
|
-
patients.push(
|
|
2331
|
+
patientsSnapshot.forEach((doc33) => {
|
|
2332
|
+
patients.push(doc33.data());
|
|
2148
2333
|
});
|
|
2149
2334
|
console.log(
|
|
2150
2335
|
`[getPatientsByPractitionerUtil] Found ${patients.length} patients for practitioner ID: ${practitionerId}`
|
|
@@ -2160,7 +2345,52 @@ var getPatientsByPractitionerUtil = async (db, practitionerId, options) => {
|
|
|
2160
2345
|
);
|
|
2161
2346
|
}
|
|
2162
2347
|
};
|
|
2163
|
-
|
|
2348
|
+
var getPatientsByPractitionerWithDetailsUtil = async (db, practitionerId, options) => {
|
|
2349
|
+
try {
|
|
2350
|
+
console.log(
|
|
2351
|
+
`[getPatientsByPractitionerWithDetailsUtil] Fetching detailed patient profiles for practitioner ID: ${practitionerId} with options:`,
|
|
2352
|
+
options
|
|
2353
|
+
);
|
|
2354
|
+
const patientProfiles = await getPatientsByPractitionerUtil(
|
|
2355
|
+
db,
|
|
2356
|
+
practitionerId,
|
|
2357
|
+
options
|
|
2358
|
+
);
|
|
2359
|
+
const patientProfilesWithDetails = await Promise.all(
|
|
2360
|
+
patientProfiles.map(async (profile) => {
|
|
2361
|
+
try {
|
|
2362
|
+
const sensitiveInfoDoc = await getDoc7(
|
|
2363
|
+
getSensitiveInfoDocRef(db, profile.id)
|
|
2364
|
+
);
|
|
2365
|
+
const sensitiveInfo = sensitiveInfoDoc.exists() ? sensitiveInfoDoc.data() : void 0;
|
|
2366
|
+
return {
|
|
2367
|
+
patientProfile: profile,
|
|
2368
|
+
patientSensitiveInfo: sensitiveInfo
|
|
2369
|
+
};
|
|
2370
|
+
} catch (error) {
|
|
2371
|
+
console.error(
|
|
2372
|
+
`[getPatientsByPractitionerWithDetailsUtil] Error fetching sensitive info for patient ${profile.id}:`,
|
|
2373
|
+
error
|
|
2374
|
+
);
|
|
2375
|
+
return { patientProfile: profile };
|
|
2376
|
+
}
|
|
2377
|
+
})
|
|
2378
|
+
);
|
|
2379
|
+
console.log(
|
|
2380
|
+
`[getPatientsByPractitionerWithDetailsUtil] Found ${patientProfilesWithDetails.length} detailed patient profiles for practitioner ID: ${practitionerId}`
|
|
2381
|
+
);
|
|
2382
|
+
return patientProfilesWithDetails;
|
|
2383
|
+
} catch (error) {
|
|
2384
|
+
console.error(
|
|
2385
|
+
`[getPatientsByPractitionerWithDetailsUtil] Error fetching detailed patient profiles:`,
|
|
2386
|
+
error
|
|
2387
|
+
);
|
|
2388
|
+
throw new Error(
|
|
2389
|
+
`Failed to retrieve detailed patient profiles: ${error instanceof Error ? error.message : String(error)}`
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
|
|
2164
2394
|
// src/services/patient/utils/clinic.utils.ts
|
|
2165
2395
|
import {
|
|
2166
2396
|
collection as collection4,
|
|
@@ -2196,8 +2426,8 @@ var getPatientsByClinicUtil = async (db, clinicId, options) => {
|
|
|
2196
2426
|
}
|
|
2197
2427
|
const patientsSnapshot = await getDocs4(q);
|
|
2198
2428
|
const patients = [];
|
|
2199
|
-
patientsSnapshot.forEach((
|
|
2200
|
-
patients.push(
|
|
2429
|
+
patientsSnapshot.forEach((doc33) => {
|
|
2430
|
+
patients.push(doc33.data());
|
|
2201
2431
|
});
|
|
2202
2432
|
console.log(
|
|
2203
2433
|
`[getPatientsByClinicUtil] Found ${patients.length} patients for clinic ID: ${clinicId}`
|
|
@@ -2497,6 +2727,25 @@ var PatientService = class extends BaseService {
|
|
|
2497
2727
|
);
|
|
2498
2728
|
return getPatientsByPractitionerUtil(this.db, practitionerId, options);
|
|
2499
2729
|
}
|
|
2730
|
+
/**
|
|
2731
|
+
* Gets all patients associated with a specific practitioner with their sensitive information.
|
|
2732
|
+
*
|
|
2733
|
+
* @param {string} practitionerId - ID of the practitioner whose patients to retrieve
|
|
2734
|
+
* @param {Object} options - Optional parameters for pagination
|
|
2735
|
+
* @param {number} options.limit - Maximum number of profiles to return
|
|
2736
|
+
* @param {string} options.startAfter - The ID of the document to start after (for pagination)
|
|
2737
|
+
* @returns {Promise<PatientProfileForDoctor[]>} A promise resolving to an array of patient profiles with sensitive info
|
|
2738
|
+
*/
|
|
2739
|
+
async getPatientsByPractitionerWithDetails(practitionerId, options) {
|
|
2740
|
+
console.log(
|
|
2741
|
+
`[PatientService.getPatientsByPractitionerWithDetails] Fetching detailed patient profiles for practitioner: ${practitionerId}`
|
|
2742
|
+
);
|
|
2743
|
+
return getPatientsByPractitionerWithDetailsUtil(
|
|
2744
|
+
this.db,
|
|
2745
|
+
practitionerId,
|
|
2746
|
+
options
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2500
2749
|
/**
|
|
2501
2750
|
* Gets all patients associated with a specific clinic.
|
|
2502
2751
|
*
|
|
@@ -3303,7 +3552,7 @@ async function getClinicAdminsByGroup(db, clinicGroupId) {
|
|
|
3303
3552
|
where5("clinicGroupId", "==", clinicGroupId)
|
|
3304
3553
|
);
|
|
3305
3554
|
const querySnapshot = await getDocs5(q);
|
|
3306
|
-
return querySnapshot.docs.map((
|
|
3555
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
3307
3556
|
}
|
|
3308
3557
|
async function updateClinicAdmin(db, adminId, data) {
|
|
3309
3558
|
const admin = await getClinicAdmin(db, adminId);
|
|
@@ -3564,6 +3813,8 @@ var ClinicAdminService = class extends BaseService {
|
|
|
3564
3813
|
this.getClinicService()
|
|
3565
3814
|
);
|
|
3566
3815
|
}
|
|
3816
|
+
// TODO: Add more methods for clinic admins for managing permissions, editing profiles by the admin, or by the clinic group owner and so on
|
|
3817
|
+
// Generally refactor admin permissions and clinic group permissions and systems for admin management
|
|
3567
3818
|
};
|
|
3568
3819
|
|
|
3569
3820
|
// src/services/practitioner/practitioner.service.ts
|
|
@@ -4017,7 +4268,7 @@ var PractitionerService = class extends BaseService {
|
|
|
4017
4268
|
where6("expiresAt", ">", Timestamp8.now())
|
|
4018
4269
|
);
|
|
4019
4270
|
const querySnapshot = await getDocs6(q);
|
|
4020
|
-
return querySnapshot.docs.map((
|
|
4271
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
4021
4272
|
}
|
|
4022
4273
|
/**
|
|
4023
4274
|
* Gets a token by its string value and validates it
|
|
@@ -4100,7 +4351,7 @@ var PractitionerService = class extends BaseService {
|
|
|
4100
4351
|
where6("status", "==", "active" /* ACTIVE */)
|
|
4101
4352
|
);
|
|
4102
4353
|
const querySnapshot = await getDocs6(q);
|
|
4103
|
-
return querySnapshot.docs.map((
|
|
4354
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
4104
4355
|
}
|
|
4105
4356
|
/**
|
|
4106
4357
|
* Dohvata sve zdravstvene radnike za određenu kliniku
|
|
@@ -4112,7 +4363,7 @@ var PractitionerService = class extends BaseService {
|
|
|
4112
4363
|
where6("isActive", "==", true)
|
|
4113
4364
|
);
|
|
4114
4365
|
const querySnapshot = await getDocs6(q);
|
|
4115
|
-
return querySnapshot.docs.map((
|
|
4366
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
4116
4367
|
}
|
|
4117
4368
|
/**
|
|
4118
4369
|
* Dohvata sve draft zdravstvene radnike za određenu kliniku sa statusom DRAFT
|
|
@@ -4124,7 +4375,7 @@ var PractitionerService = class extends BaseService {
|
|
|
4124
4375
|
where6("status", "==", "draft" /* DRAFT */)
|
|
4125
4376
|
);
|
|
4126
4377
|
const querySnapshot = await getDocs6(q);
|
|
4127
|
-
return querySnapshot.docs.map((
|
|
4378
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
4128
4379
|
}
|
|
4129
4380
|
/**
|
|
4130
4381
|
* Updates a practitioner
|
|
@@ -4306,7 +4557,7 @@ var PractitionerService = class extends BaseService {
|
|
|
4306
4557
|
);
|
|
4307
4558
|
const querySnapshot = await getDocs6(q);
|
|
4308
4559
|
const practitioners = querySnapshot.docs.map(
|
|
4309
|
-
(
|
|
4560
|
+
(doc33) => doc33.data()
|
|
4310
4561
|
);
|
|
4311
4562
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
4312
4563
|
return {
|
|
@@ -4377,8 +4628,8 @@ var PractitionerService = class extends BaseService {
|
|
|
4377
4628
|
console.log(
|
|
4378
4629
|
`[PRACTITIONER_SERVICE] Found ${querySnapshot.docs.length} practitioners with base query`
|
|
4379
4630
|
);
|
|
4380
|
-
let practitioners = querySnapshot.docs.map((
|
|
4381
|
-
return { ...
|
|
4631
|
+
let practitioners = querySnapshot.docs.map((doc33) => {
|
|
4632
|
+
return { ...doc33.data(), id: doc33.id };
|
|
4382
4633
|
});
|
|
4383
4634
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
4384
4635
|
if (filters.nameSearch && filters.nameSearch.trim() !== "") {
|
|
@@ -4632,7 +4883,7 @@ var UserService = class extends BaseService {
|
|
|
4632
4883
|
];
|
|
4633
4884
|
const q = query7(collection7(this.db, USERS_COLLECTION), ...constraints);
|
|
4634
4885
|
const querySnapshot = await getDocs7(q);
|
|
4635
|
-
const users = querySnapshot.docs.map((
|
|
4886
|
+
const users = querySnapshot.docs.map((doc33) => doc33.data());
|
|
4636
4887
|
return Promise.all(users.map((userData) => userSchema.parse(userData)));
|
|
4637
4888
|
}
|
|
4638
4889
|
/**
|
|
@@ -5012,7 +5263,7 @@ async function getAllActiveGroups(db) {
|
|
|
5012
5263
|
where8("isActive", "==", true)
|
|
5013
5264
|
);
|
|
5014
5265
|
const querySnapshot = await getDocs8(q);
|
|
5015
|
-
return querySnapshot.docs.map((
|
|
5266
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
5016
5267
|
}
|
|
5017
5268
|
async function updateClinicGroup(db, groupId, data, app) {
|
|
5018
5269
|
console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
|
|
@@ -5384,6 +5635,11 @@ var ClinicGroupService = class extends BaseService {
|
|
|
5384
5635
|
this.app
|
|
5385
5636
|
);
|
|
5386
5637
|
}
|
|
5638
|
+
// TODO: Add a method to get all admin tokens for a clinic group (not just active ones)
|
|
5639
|
+
// TODO: Refactor admin token methods not to add tokens to the clinicGroup document,
|
|
5640
|
+
// but to add them to a subcollection called adminTokens that belongs to a specific clinicGroup document
|
|
5641
|
+
// TODO: Add granular control over admin permissions, e.g. only allow admins to manage certain clinics to tokens directly
|
|
5642
|
+
// TODO: Generally refactor admin tokens and invites, also create cloud function to send invites and send updates when sombody uses the token
|
|
5387
5643
|
};
|
|
5388
5644
|
|
|
5389
5645
|
// src/services/clinic/clinic.service.ts
|
|
@@ -5436,7 +5692,7 @@ async function getClinicsByGroup(db, groupId) {
|
|
|
5436
5692
|
where9("isActive", "==", true)
|
|
5437
5693
|
);
|
|
5438
5694
|
const querySnapshot = await getDocs9(q);
|
|
5439
|
-
return querySnapshot.docs.map((
|
|
5695
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
5440
5696
|
}
|
|
5441
5697
|
async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
|
|
5442
5698
|
console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
|
|
@@ -5630,7 +5886,7 @@ async function getClinicsByAdmin(db, adminId, options = {}, clinicAdminService,
|
|
|
5630
5886
|
}
|
|
5631
5887
|
const q = query9(collection9(db, CLINICS_COLLECTION), ...constraints);
|
|
5632
5888
|
const querySnapshot = await getDocs9(q);
|
|
5633
|
-
return querySnapshot.docs.map((
|
|
5889
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
5634
5890
|
}
|
|
5635
5891
|
async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGroupService) {
|
|
5636
5892
|
return getClinicsByAdmin(
|
|
@@ -5675,11 +5931,11 @@ async function getAllClinics(db, pagination, lastDoc) {
|
|
|
5675
5931
|
}
|
|
5676
5932
|
const clinicsSnapshot = await getDocs9(clinicsQuery);
|
|
5677
5933
|
const lastVisible = clinicsSnapshot.docs[clinicsSnapshot.docs.length - 1];
|
|
5678
|
-
const clinics = clinicsSnapshot.docs.map((
|
|
5679
|
-
const data =
|
|
5934
|
+
const clinics = clinicsSnapshot.docs.map((doc33) => {
|
|
5935
|
+
const data = doc33.data();
|
|
5680
5936
|
return {
|
|
5681
5937
|
...data,
|
|
5682
|
-
id:
|
|
5938
|
+
id: doc33.id
|
|
5683
5939
|
};
|
|
5684
5940
|
});
|
|
5685
5941
|
return {
|
|
@@ -5706,8 +5962,8 @@ async function getAllClinicsInRange(db, center, rangeInKm, pagination, lastDoc)
|
|
|
5706
5962
|
];
|
|
5707
5963
|
const q = query9(collection9(db, CLINICS_COLLECTION), ...constraints);
|
|
5708
5964
|
const querySnapshot = await getDocs9(q);
|
|
5709
|
-
for (const
|
|
5710
|
-
const clinic =
|
|
5965
|
+
for (const doc33 of querySnapshot.docs) {
|
|
5966
|
+
const clinic = doc33.data();
|
|
5711
5967
|
const distance = distanceBetween2(
|
|
5712
5968
|
[center.latitude, center.longitude],
|
|
5713
5969
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -5829,8 +6085,8 @@ async function findClinicsInRadius(db, center, radiusInKm, filters) {
|
|
|
5829
6085
|
}
|
|
5830
6086
|
const q = query10(collection10(db, CLINICS_COLLECTION), ...constraints);
|
|
5831
6087
|
const querySnapshot = await getDocs10(q);
|
|
5832
|
-
for (const
|
|
5833
|
-
const clinic =
|
|
6088
|
+
for (const doc33 of querySnapshot.docs) {
|
|
6089
|
+
const clinic = doc33.data();
|
|
5834
6090
|
const distance = distanceBetween3(
|
|
5835
6091
|
[center.latitude, center.longitude],
|
|
5836
6092
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -5926,8 +6182,8 @@ async function getClinicsByFilters(db, filters) {
|
|
|
5926
6182
|
console.log(
|
|
5927
6183
|
`[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics in geo bound`
|
|
5928
6184
|
);
|
|
5929
|
-
for (const
|
|
5930
|
-
const clinic = { ...
|
|
6185
|
+
for (const doc33 of querySnapshot.docs) {
|
|
6186
|
+
const clinic = { ...doc33.data(), id: doc33.id };
|
|
5931
6187
|
const distance = distanceBetween4(
|
|
5932
6188
|
[center.latitude, center.longitude],
|
|
5933
6189
|
[clinic.location.latitude, clinic.location.longitude]
|
|
@@ -5983,8 +6239,8 @@ async function getClinicsByFilters(db, filters) {
|
|
|
5983
6239
|
console.log(
|
|
5984
6240
|
`[FILTER_UTILS] Found ${querySnapshot.docs.length} clinics with regular query`
|
|
5985
6241
|
);
|
|
5986
|
-
const clinics = querySnapshot.docs.map((
|
|
5987
|
-
return { ...
|
|
6242
|
+
const clinics = querySnapshot.docs.map((doc33) => {
|
|
6243
|
+
return { ...doc33.data(), id: doc33.id };
|
|
5988
6244
|
});
|
|
5989
6245
|
let filteredClinics = clinics;
|
|
5990
6246
|
if (filters.center) {
|
|
@@ -7209,17 +7465,28 @@ import {
|
|
|
7209
7465
|
|
|
7210
7466
|
// src/types/notifications/index.ts
|
|
7211
7467
|
var NotificationType = /* @__PURE__ */ ((NotificationType3) => {
|
|
7212
|
-
NotificationType3["PRE_REQUIREMENT"] = "preRequirement";
|
|
7213
|
-
NotificationType3["POST_REQUIREMENT"] = "postRequirement";
|
|
7214
7468
|
NotificationType3["APPOINTMENT_REMINDER"] = "appointmentReminder";
|
|
7215
|
-
NotificationType3["
|
|
7469
|
+
NotificationType3["APPOINTMENT_STATUS_CHANGE"] = "appointmentStatusChange";
|
|
7470
|
+
NotificationType3["APPOINTMENT_RESCHEDULED_PROPOSAL"] = "appointmentRescheduledProposal";
|
|
7471
|
+
NotificationType3["APPOINTMENT_CANCELLED"] = "appointmentCancelled";
|
|
7472
|
+
NotificationType3["REQUIREMENT_INSTRUCTION_DUE"] = "requirementInstructionDue";
|
|
7473
|
+
NotificationType3["FORM_REMINDER"] = "formReminder";
|
|
7474
|
+
NotificationType3["FORM_SUBMISSION_CONFIRMATION"] = "formSubmissionConfirmation";
|
|
7475
|
+
NotificationType3["REVIEW_REQUEST"] = "reviewRequest";
|
|
7476
|
+
NotificationType3["PAYMENT_DUE"] = "paymentDue";
|
|
7477
|
+
NotificationType3["PAYMENT_CONFIRMATION"] = "paymentConfirmation";
|
|
7478
|
+
NotificationType3["PAYMENT_FAILED"] = "paymentFailed";
|
|
7479
|
+
NotificationType3["GENERAL_MESSAGE"] = "generalMessage";
|
|
7480
|
+
NotificationType3["ACCOUNT_NOTIFICATION"] = "accountNotification";
|
|
7216
7481
|
return NotificationType3;
|
|
7217
7482
|
})(NotificationType || {});
|
|
7218
7483
|
var NOTIFICATIONS_COLLECTION = "notifications";
|
|
7219
7484
|
var NotificationStatus = /* @__PURE__ */ ((NotificationStatus2) => {
|
|
7220
7485
|
NotificationStatus2["PENDING"] = "pending";
|
|
7486
|
+
NotificationStatus2["PROCESSING"] = "processing";
|
|
7221
7487
|
NotificationStatus2["SENT"] = "sent";
|
|
7222
7488
|
NotificationStatus2["FAILED"] = "failed";
|
|
7489
|
+
NotificationStatus2["DELIVERED"] = "delivered";
|
|
7223
7490
|
NotificationStatus2["CANCELLED"] = "cancelled";
|
|
7224
7491
|
NotificationStatus2["PARTIAL_SUCCESS"] = "partialSuccess";
|
|
7225
7492
|
return NotificationStatus2;
|
|
@@ -7275,9 +7542,9 @@ var NotificationService = class extends BaseService {
|
|
|
7275
7542
|
orderBy3("notificationTime", "desc")
|
|
7276
7543
|
);
|
|
7277
7544
|
const querySnapshot = await getDocs14(q);
|
|
7278
|
-
return querySnapshot.docs.map((
|
|
7279
|
-
id:
|
|
7280
|
-
...
|
|
7545
|
+
return querySnapshot.docs.map((doc33) => ({
|
|
7546
|
+
id: doc33.id,
|
|
7547
|
+
...doc33.data()
|
|
7281
7548
|
}));
|
|
7282
7549
|
}
|
|
7283
7550
|
/**
|
|
@@ -7291,9 +7558,9 @@ var NotificationService = class extends BaseService {
|
|
|
7291
7558
|
orderBy3("notificationTime", "desc")
|
|
7292
7559
|
);
|
|
7293
7560
|
const querySnapshot = await getDocs14(q);
|
|
7294
|
-
return querySnapshot.docs.map((
|
|
7295
|
-
id:
|
|
7296
|
-
...
|
|
7561
|
+
return querySnapshot.docs.map((doc33) => ({
|
|
7562
|
+
id: doc33.id,
|
|
7563
|
+
...doc33.data()
|
|
7297
7564
|
}));
|
|
7298
7565
|
}
|
|
7299
7566
|
/**
|
|
@@ -7365,9 +7632,9 @@ var NotificationService = class extends BaseService {
|
|
|
7365
7632
|
orderBy3("notificationTime", "desc")
|
|
7366
7633
|
);
|
|
7367
7634
|
const querySnapshot = await getDocs14(q);
|
|
7368
|
-
return querySnapshot.docs.map((
|
|
7369
|
-
id:
|
|
7370
|
-
...
|
|
7635
|
+
return querySnapshot.docs.map((doc33) => ({
|
|
7636
|
+
id: doc33.id,
|
|
7637
|
+
...doc33.data()
|
|
7371
7638
|
}));
|
|
7372
7639
|
}
|
|
7373
7640
|
/**
|
|
@@ -7380,9 +7647,9 @@ var NotificationService = class extends BaseService {
|
|
|
7380
7647
|
orderBy3("notificationTime", "desc")
|
|
7381
7648
|
);
|
|
7382
7649
|
const querySnapshot = await getDocs14(q);
|
|
7383
|
-
return querySnapshot.docs.map((
|
|
7384
|
-
id:
|
|
7385
|
-
...
|
|
7650
|
+
return querySnapshot.docs.map((doc33) => ({
|
|
7651
|
+
id: doc33.id,
|
|
7652
|
+
...doc33.data()
|
|
7386
7653
|
}));
|
|
7387
7654
|
}
|
|
7388
7655
|
};
|
|
@@ -7613,7 +7880,7 @@ var ProcedureService = class extends BaseService {
|
|
|
7613
7880
|
where15("isActive", "==", true)
|
|
7614
7881
|
);
|
|
7615
7882
|
const snapshot = await getDocs15(q);
|
|
7616
|
-
return snapshot.docs.map((
|
|
7883
|
+
return snapshot.docs.map((doc33) => doc33.data());
|
|
7617
7884
|
}
|
|
7618
7885
|
/**
|
|
7619
7886
|
* Gets all procedures for a practitioner
|
|
@@ -7627,7 +7894,7 @@ var ProcedureService = class extends BaseService {
|
|
|
7627
7894
|
where15("isActive", "==", true)
|
|
7628
7895
|
);
|
|
7629
7896
|
const snapshot = await getDocs15(q);
|
|
7630
|
-
return snapshot.docs.map((
|
|
7897
|
+
return snapshot.docs.map((doc33) => doc33.data());
|
|
7631
7898
|
}
|
|
7632
7899
|
/**
|
|
7633
7900
|
* Updates a procedure
|
|
@@ -7810,20 +8077,20 @@ var ProcedureService = class extends BaseService {
|
|
|
7810
8077
|
const proceduresCollection = collection15(this.db, PROCEDURES_COLLECTION);
|
|
7811
8078
|
let proceduresQuery = query15(proceduresCollection);
|
|
7812
8079
|
if (pagination && pagination > 0) {
|
|
7813
|
-
const { limit:
|
|
8080
|
+
const { limit: limit12, startAfter: startAfter12 } = await import("firebase/firestore");
|
|
7814
8081
|
if (lastDoc) {
|
|
7815
8082
|
proceduresQuery = query15(
|
|
7816
8083
|
proceduresCollection,
|
|
7817
8084
|
orderBy4("name"),
|
|
7818
8085
|
// Use imported orderBy
|
|
7819
|
-
|
|
7820
|
-
|
|
8086
|
+
startAfter12(lastDoc),
|
|
8087
|
+
limit12(pagination)
|
|
7821
8088
|
);
|
|
7822
8089
|
} else {
|
|
7823
8090
|
proceduresQuery = query15(
|
|
7824
8091
|
proceduresCollection,
|
|
7825
8092
|
orderBy4("name"),
|
|
7826
|
-
|
|
8093
|
+
limit12(pagination)
|
|
7827
8094
|
);
|
|
7828
8095
|
}
|
|
7829
8096
|
} else {
|
|
@@ -7831,11 +8098,11 @@ var ProcedureService = class extends BaseService {
|
|
|
7831
8098
|
}
|
|
7832
8099
|
const proceduresSnapshot = await getDocs15(proceduresQuery);
|
|
7833
8100
|
const lastVisible = proceduresSnapshot.docs[proceduresSnapshot.docs.length - 1];
|
|
7834
|
-
const procedures = proceduresSnapshot.docs.map((
|
|
7835
|
-
const data =
|
|
8101
|
+
const procedures = proceduresSnapshot.docs.map((doc33) => {
|
|
8102
|
+
const data = doc33.data();
|
|
7836
8103
|
return {
|
|
7837
8104
|
...data,
|
|
7838
|
-
id:
|
|
8105
|
+
id: doc33.id
|
|
7839
8106
|
// Ensure ID is present
|
|
7840
8107
|
};
|
|
7841
8108
|
});
|
|
@@ -7917,8 +8184,8 @@ var ProcedureService = class extends BaseService {
|
|
|
7917
8184
|
console.log(
|
|
7918
8185
|
`[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures in geo bound`
|
|
7919
8186
|
);
|
|
7920
|
-
for (const
|
|
7921
|
-
const procedure = { ...
|
|
8187
|
+
for (const doc33 of querySnapshot.docs) {
|
|
8188
|
+
const procedure = { ...doc33.data(), id: doc33.id };
|
|
7922
8189
|
const distance = distanceBetween6(
|
|
7923
8190
|
[center.latitude, center.longitude],
|
|
7924
8191
|
[
|
|
@@ -7969,8 +8236,8 @@ var ProcedureService = class extends BaseService {
|
|
|
7969
8236
|
console.log(
|
|
7970
8237
|
`[PROCEDURE_SERVICE] Found ${querySnapshot.docs.length} procedures with regular query`
|
|
7971
8238
|
);
|
|
7972
|
-
const procedures = querySnapshot.docs.map((
|
|
7973
|
-
return { ...
|
|
8239
|
+
const procedures = querySnapshot.docs.map((doc33) => {
|
|
8240
|
+
return { ...doc33.data(), id: doc33.id };
|
|
7974
8241
|
});
|
|
7975
8242
|
if (filters.location) {
|
|
7976
8243
|
const center = filters.location;
|
|
@@ -8123,7 +8390,10 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
8123
8390
|
createdBy: userId,
|
|
8124
8391
|
version: 1,
|
|
8125
8392
|
isActive: true,
|
|
8126
|
-
tags: validatedData.tags || []
|
|
8393
|
+
tags: validatedData.tags || [],
|
|
8394
|
+
isUserForm: validatedData.isUserForm || false,
|
|
8395
|
+
isRequired: validatedData.isRequired || false,
|
|
8396
|
+
sortingOrder: validatedData.sortingOrder || 0
|
|
8127
8397
|
};
|
|
8128
8398
|
const docRef = doc16(this.collectionRef, templateId);
|
|
8129
8399
|
await setDoc14(docRef, template);
|
|
@@ -8158,21 +8428,31 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
8158
8428
|
if (validatedData.elements) {
|
|
8159
8429
|
updatedElements = validatedData.elements.map((element) => ({
|
|
8160
8430
|
...element,
|
|
8161
|
-
id: this.generateId()
|
|
8431
|
+
id: element.id || this.generateId()
|
|
8162
8432
|
}));
|
|
8163
8433
|
}
|
|
8164
|
-
const
|
|
8165
|
-
...validatedData,
|
|
8434
|
+
const updatePayload = {
|
|
8166
8435
|
elements: updatedElements,
|
|
8167
8436
|
updatedAt: Date.now(),
|
|
8168
8437
|
version: template.version + 1
|
|
8169
8438
|
};
|
|
8439
|
+
if (validatedData.title !== void 0)
|
|
8440
|
+
updatePayload.title = validatedData.title;
|
|
8441
|
+
if (validatedData.description !== void 0)
|
|
8442
|
+
updatePayload.description = validatedData.description;
|
|
8443
|
+
if (validatedData.isActive !== void 0)
|
|
8444
|
+
updatePayload.isActive = validatedData.isActive;
|
|
8445
|
+
if (validatedData.tags !== void 0)
|
|
8446
|
+
updatePayload.tags = validatedData.tags;
|
|
8447
|
+
if (validatedData.isUserForm !== void 0)
|
|
8448
|
+
updatePayload.isUserForm = validatedData.isUserForm;
|
|
8449
|
+
if (validatedData.isRequired !== void 0)
|
|
8450
|
+
updatePayload.isRequired = validatedData.isRequired;
|
|
8451
|
+
if (validatedData.sortingOrder !== void 0)
|
|
8452
|
+
updatePayload.sortingOrder = validatedData.sortingOrder;
|
|
8170
8453
|
const docRef = doc16(this.collectionRef, templateId);
|
|
8171
|
-
await updateDoc16(docRef,
|
|
8172
|
-
return {
|
|
8173
|
-
...template,
|
|
8174
|
-
...updateData
|
|
8175
|
-
};
|
|
8454
|
+
await updateDoc16(docRef, updatePayload);
|
|
8455
|
+
return { ...template, ...updatePayload };
|
|
8176
8456
|
}
|
|
8177
8457
|
/**
|
|
8178
8458
|
* Delete a document template
|
|
@@ -8201,9 +8481,9 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
8201
8481
|
const querySnapshot = await getDocs16(q);
|
|
8202
8482
|
const templates = [];
|
|
8203
8483
|
let lastVisible = null;
|
|
8204
|
-
querySnapshot.forEach((
|
|
8205
|
-
templates.push(
|
|
8206
|
-
lastVisible =
|
|
8484
|
+
querySnapshot.forEach((doc33) => {
|
|
8485
|
+
templates.push(doc33.data());
|
|
8486
|
+
lastVisible = doc33;
|
|
8207
8487
|
});
|
|
8208
8488
|
return {
|
|
8209
8489
|
templates,
|
|
@@ -8231,9 +8511,9 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
8231
8511
|
const querySnapshot = await getDocs16(q);
|
|
8232
8512
|
const templates = [];
|
|
8233
8513
|
let lastVisible = null;
|
|
8234
|
-
querySnapshot.forEach((
|
|
8235
|
-
templates.push(
|
|
8236
|
-
lastVisible =
|
|
8514
|
+
querySnapshot.forEach((doc33) => {
|
|
8515
|
+
templates.push(doc33.data());
|
|
8516
|
+
lastVisible = doc33;
|
|
8237
8517
|
});
|
|
8238
8518
|
return {
|
|
8239
8519
|
templates,
|
|
@@ -8260,9 +8540,9 @@ var DocumentationTemplateService = class extends BaseService {
|
|
|
8260
8540
|
const querySnapshot = await getDocs16(q);
|
|
8261
8541
|
const templates = [];
|
|
8262
8542
|
let lastVisible = null;
|
|
8263
|
-
querySnapshot.forEach((
|
|
8264
|
-
templates.push(
|
|
8265
|
-
lastVisible =
|
|
8543
|
+
querySnapshot.forEach((doc33) => {
|
|
8544
|
+
templates.push(doc33.data());
|
|
8545
|
+
lastVisible = doc33;
|
|
8266
8546
|
});
|
|
8267
8547
|
return {
|
|
8268
8548
|
templates,
|
|
@@ -8280,7 +8560,6 @@ import {
|
|
|
8280
8560
|
setDoc as setDoc15,
|
|
8281
8561
|
updateDoc as updateDoc17,
|
|
8282
8562
|
query as query17,
|
|
8283
|
-
where as where17,
|
|
8284
8563
|
orderBy as orderBy6,
|
|
8285
8564
|
limit as limit9,
|
|
8286
8565
|
startAfter as startAfter9
|
|
@@ -8288,50 +8567,80 @@ import {
|
|
|
8288
8567
|
var FilledDocumentService = class extends BaseService {
|
|
8289
8568
|
constructor(...args) {
|
|
8290
8569
|
super(...args);
|
|
8291
|
-
this.collectionRef = collection17(
|
|
8292
|
-
this.db,
|
|
8293
|
-
FILLED_DOCUMENTS_COLLECTION
|
|
8294
|
-
);
|
|
8295
8570
|
this.templateService = new DocumentationTemplateService(...args);
|
|
8296
8571
|
}
|
|
8572
|
+
getFormSubcollectionPath(isUserForm) {
|
|
8573
|
+
return isUserForm ? USER_FORMS_SUBCOLLECTION : DOCTOR_FORMS_SUBCOLLECTION;
|
|
8574
|
+
}
|
|
8297
8575
|
/**
|
|
8298
|
-
* Create a new filled document
|
|
8299
|
-
* @param templateId - ID of the template to use
|
|
8300
|
-
* @param
|
|
8301
|
-
* @param
|
|
8302
|
-
* @param
|
|
8303
|
-
* @
|
|
8576
|
+
* Create a new filled document within an appointment's subcollection.
|
|
8577
|
+
* @param templateId - ID of the template to use.
|
|
8578
|
+
* @param appointmentId - ID of the appointment this form belongs to.
|
|
8579
|
+
* @param procedureId - ID of the procedure associated with this form.
|
|
8580
|
+
* @param patientId - ID of the patient.
|
|
8581
|
+
* @param practitionerId - ID of the practitioner (can be system/generic if patient is filling).
|
|
8582
|
+
* @param clinicId - ID of the clinic.
|
|
8583
|
+
* @param initialValues - Optional initial values for the form elements.
|
|
8584
|
+
* @param initialStatus - Optional initial status for the form.
|
|
8585
|
+
* @returns The created filled document.
|
|
8304
8586
|
*/
|
|
8305
|
-
async
|
|
8587
|
+
async createFilledDocumentForAppointment(templateId, appointmentId, procedureId, patientId, practitionerId, clinicId, initialValues = {}, initialStatus = "draft" /* DRAFT */) {
|
|
8306
8588
|
const template = await this.templateService.getTemplateById(templateId);
|
|
8307
8589
|
if (!template) {
|
|
8308
8590
|
throw new Error(`Template with ID ${templateId} not found`);
|
|
8309
8591
|
}
|
|
8310
8592
|
const documentId3 = this.generateId();
|
|
8311
8593
|
const now = Date.now();
|
|
8594
|
+
const isUserForm = template.isUserForm || false;
|
|
8595
|
+
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
8312
8596
|
const filledDocument = {
|
|
8313
8597
|
id: documentId3,
|
|
8314
8598
|
templateId,
|
|
8315
8599
|
templateVersion: template.version,
|
|
8600
|
+
isUserForm,
|
|
8601
|
+
// Set based on template
|
|
8602
|
+
isRequired: template.isRequired || false,
|
|
8603
|
+
// Inherit isRequired from the template
|
|
8604
|
+
appointmentId,
|
|
8605
|
+
// NEW
|
|
8606
|
+
procedureId,
|
|
8607
|
+
// NEW
|
|
8316
8608
|
patientId,
|
|
8317
8609
|
practitionerId,
|
|
8318
8610
|
clinicId,
|
|
8319
8611
|
createdAt: now,
|
|
8320
8612
|
updatedAt: now,
|
|
8321
|
-
values:
|
|
8322
|
-
status:
|
|
8613
|
+
values: initialValues,
|
|
8614
|
+
status: initialStatus
|
|
8323
8615
|
};
|
|
8324
|
-
const docRef = doc17(
|
|
8616
|
+
const docRef = doc17(
|
|
8617
|
+
this.db,
|
|
8618
|
+
APPOINTMENTS_COLLECTION,
|
|
8619
|
+
// Replaced "appointments"
|
|
8620
|
+
appointmentId,
|
|
8621
|
+
formSubcollection,
|
|
8622
|
+
documentId3
|
|
8623
|
+
);
|
|
8325
8624
|
await setDoc15(docRef, filledDocument);
|
|
8326
8625
|
return filledDocument;
|
|
8327
8626
|
}
|
|
8328
8627
|
/**
|
|
8329
|
-
* Get a filled document
|
|
8330
|
-
* @param
|
|
8331
|
-
* @
|
|
8628
|
+
* Get a specific filled document from an appointment's subcollection.
|
|
8629
|
+
* @param appointmentId - ID of the appointment.
|
|
8630
|
+
* @param formId - ID of the filled document.
|
|
8631
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
8632
|
+
* @returns The filled document or null if not found.
|
|
8332
8633
|
*/
|
|
8333
|
-
async
|
|
8334
|
-
const
|
|
8634
|
+
async getFilledDocumentFromAppointmentById(appointmentId, formId, isUserForm) {
|
|
8635
|
+
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
8636
|
+
const docRef = doc17(
|
|
8637
|
+
this.db,
|
|
8638
|
+
APPOINTMENTS_COLLECTION,
|
|
8639
|
+
// Replaced "appointments"
|
|
8640
|
+
appointmentId,
|
|
8641
|
+
formSubcollection,
|
|
8642
|
+
formId
|
|
8643
|
+
);
|
|
8335
8644
|
const docSnap = await getDoc20(docRef);
|
|
8336
8645
|
if (!docSnap.exists()) {
|
|
8337
8646
|
return null;
|
|
@@ -8339,178 +8648,183 @@ var FilledDocumentService = class extends BaseService {
|
|
|
8339
8648
|
return docSnap.data();
|
|
8340
8649
|
}
|
|
8341
8650
|
/**
|
|
8342
|
-
* Update values in a filled document
|
|
8343
|
-
* @param
|
|
8344
|
-
* @param
|
|
8345
|
-
* @param
|
|
8346
|
-
* @
|
|
8651
|
+
* Update values or status in a filled document within an appointment's subcollection.
|
|
8652
|
+
* @param appointmentId - ID of the appointment.
|
|
8653
|
+
* @param formId - ID of the filled document to update.
|
|
8654
|
+
* @param isUserForm - Boolean indicating if it's a user form or doctor form.
|
|
8655
|
+
* @param values - Updated values for elements.
|
|
8656
|
+
* @param status - Optional new status for the document.
|
|
8657
|
+
* @returns The updated filled document.
|
|
8347
8658
|
*/
|
|
8348
|
-
async
|
|
8349
|
-
const
|
|
8350
|
-
|
|
8351
|
-
|
|
8659
|
+
async updateFilledDocumentInAppointment(appointmentId, formId, isUserForm, values, status) {
|
|
8660
|
+
const formSubcollection = this.getFormSubcollectionPath(isUserForm);
|
|
8661
|
+
const docRef = doc17(
|
|
8662
|
+
this.db,
|
|
8663
|
+
APPOINTMENTS_COLLECTION,
|
|
8664
|
+
// Replaced "appointments"
|
|
8665
|
+
appointmentId,
|
|
8666
|
+
formSubcollection,
|
|
8667
|
+
formId
|
|
8668
|
+
);
|
|
8669
|
+
const existingDoc = await this.getFilledDocumentFromAppointmentById(
|
|
8670
|
+
appointmentId,
|
|
8671
|
+
formId,
|
|
8672
|
+
isUserForm
|
|
8673
|
+
);
|
|
8674
|
+
if (!existingDoc) {
|
|
8675
|
+
throw new Error(
|
|
8676
|
+
`Filled document with ID ${formId} not found in appointment ${appointmentId} ${formSubcollection}`
|
|
8677
|
+
);
|
|
8352
8678
|
}
|
|
8353
|
-
const
|
|
8354
|
-
values: {
|
|
8355
|
-
...filledDocument.values,
|
|
8356
|
-
...values
|
|
8357
|
-
},
|
|
8679
|
+
const updatePayload = {
|
|
8358
8680
|
updatedAt: Date.now()
|
|
8359
8681
|
};
|
|
8682
|
+
if (values) {
|
|
8683
|
+
updatePayload.values = {
|
|
8684
|
+
...existingDoc.values,
|
|
8685
|
+
...values
|
|
8686
|
+
};
|
|
8687
|
+
}
|
|
8360
8688
|
if (status) {
|
|
8361
|
-
|
|
8689
|
+
updatePayload.status = status;
|
|
8362
8690
|
}
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
...updateData
|
|
8368
|
-
};
|
|
8691
|
+
if (Object.keys(updatePayload).length === 1 && "updatedAt" in updatePayload) {
|
|
8692
|
+
}
|
|
8693
|
+
await updateDoc17(docRef, updatePayload);
|
|
8694
|
+
return { ...existingDoc, ...updatePayload };
|
|
8369
8695
|
}
|
|
8370
8696
|
/**
|
|
8371
|
-
* Get filled
|
|
8372
|
-
* @param
|
|
8373
|
-
* @param pageSize
|
|
8374
|
-
* @param lastDoc
|
|
8375
|
-
* @returns Array of filled documents and the last document for pagination
|
|
8697
|
+
* Get all filled user forms for a specific appointment.
|
|
8698
|
+
* @param appointmentId ID of the appointment.
|
|
8699
|
+
* @param pageSize Number of documents to retrieve.
|
|
8700
|
+
* @param lastDoc Last document from previous page for pagination.
|
|
8376
8701
|
*/
|
|
8377
|
-
async
|
|
8702
|
+
async getFilledUserFormsForAppointment(appointmentId, pageSize = 20, lastDoc) {
|
|
8703
|
+
const subcollectionRef = collection17(
|
|
8704
|
+
this.db,
|
|
8705
|
+
APPOINTMENTS_COLLECTION,
|
|
8706
|
+
// Replaced "appointments"
|
|
8707
|
+
appointmentId,
|
|
8708
|
+
USER_FORMS_SUBCOLLECTION
|
|
8709
|
+
);
|
|
8378
8710
|
let q = query17(
|
|
8379
|
-
|
|
8380
|
-
where17("patientId", "==", patientId),
|
|
8711
|
+
subcollectionRef,
|
|
8381
8712
|
orderBy6("updatedAt", "desc"),
|
|
8382
8713
|
limit9(pageSize)
|
|
8383
8714
|
);
|
|
8384
8715
|
if (lastDoc) {
|
|
8385
8716
|
q = query17(q, startAfter9(lastDoc));
|
|
8386
8717
|
}
|
|
8387
|
-
|
|
8388
|
-
const documents = [];
|
|
8389
|
-
let lastVisible = null;
|
|
8390
|
-
querySnapshot.forEach((doc32) => {
|
|
8391
|
-
documents.push(doc32.data());
|
|
8392
|
-
lastVisible = doc32;
|
|
8393
|
-
});
|
|
8394
|
-
return {
|
|
8395
|
-
documents,
|
|
8396
|
-
lastDoc: lastVisible
|
|
8397
|
-
};
|
|
8718
|
+
return this.executeQuery(q);
|
|
8398
8719
|
}
|
|
8399
8720
|
/**
|
|
8400
|
-
* Get filled
|
|
8401
|
-
* @param
|
|
8402
|
-
* @param pageSize
|
|
8403
|
-
* @param lastDoc
|
|
8404
|
-
* @returns Array of filled documents and the last document for pagination
|
|
8721
|
+
* Get all filled doctor forms for a specific appointment.
|
|
8722
|
+
* @param appointmentId ID of the appointment.
|
|
8723
|
+
* @param pageSize Number of documents to retrieve.
|
|
8724
|
+
* @param lastDoc Last document from previous page for pagination.
|
|
8405
8725
|
*/
|
|
8406
|
-
async
|
|
8726
|
+
async getFilledDoctorFormsForAppointment(appointmentId, pageSize = 20, lastDoc) {
|
|
8727
|
+
const subcollectionRef = collection17(
|
|
8728
|
+
this.db,
|
|
8729
|
+
APPOINTMENTS_COLLECTION,
|
|
8730
|
+
// Replaced "appointments"
|
|
8731
|
+
appointmentId,
|
|
8732
|
+
DOCTOR_FORMS_SUBCOLLECTION
|
|
8733
|
+
);
|
|
8407
8734
|
let q = query17(
|
|
8408
|
-
|
|
8409
|
-
where17("practitionerId", "==", practitionerId),
|
|
8735
|
+
subcollectionRef,
|
|
8410
8736
|
orderBy6("updatedAt", "desc"),
|
|
8411
8737
|
limit9(pageSize)
|
|
8412
8738
|
);
|
|
8413
8739
|
if (lastDoc) {
|
|
8414
8740
|
q = query17(q, startAfter9(lastDoc));
|
|
8415
8741
|
}
|
|
8742
|
+
return this.executeQuery(q);
|
|
8743
|
+
}
|
|
8744
|
+
// Helper to execute query and return documents + lastDoc
|
|
8745
|
+
async executeQuery(q) {
|
|
8416
8746
|
const querySnapshot = await getDocs17(q);
|
|
8417
8747
|
const documents = [];
|
|
8418
8748
|
let lastVisible = null;
|
|
8419
|
-
querySnapshot.forEach((
|
|
8420
|
-
documents.push(
|
|
8421
|
-
lastVisible =
|
|
8749
|
+
querySnapshot.forEach((doc33) => {
|
|
8750
|
+
documents.push(doc33.data());
|
|
8751
|
+
lastVisible = doc33;
|
|
8422
8752
|
});
|
|
8423
8753
|
return {
|
|
8424
8754
|
documents,
|
|
8425
8755
|
lastDoc: lastVisible
|
|
8426
8756
|
};
|
|
8427
8757
|
}
|
|
8758
|
+
// IMPORTANT: The following methods that query across all patients/practitioners/clinics
|
|
8759
|
+
// (e.g., getFilledDocumentsByPatient, getFilledDocumentsByPractitioner)
|
|
8760
|
+
// will NOT work correctly if FILLED_DOCUMENTS_COLLECTION is no longer a top-level collection
|
|
8761
|
+
// and data is only in appointment subcollections. You would need to use
|
|
8762
|
+
// Firestore Collection Group Queries for that, which require an index and a different query approach.
|
|
8763
|
+
// These methods are left here for now but would need significant rework or removal.
|
|
8764
|
+
/**
|
|
8765
|
+
* Get filled documents for a patient (NEEDS REWORK for subcollections or Collection Group Query)
|
|
8766
|
+
* @param patientId - ID of the patient
|
|
8767
|
+
* @param pageSize - Number of documents to retrieve
|
|
8768
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
8769
|
+
* @returns Array of filled documents and the last document for pagination
|
|
8770
|
+
*/
|
|
8771
|
+
async getFilledDocumentsByPatient(patientId, pageSize = 20, lastDoc) {
|
|
8772
|
+
console.warn(
|
|
8773
|
+
"getFilledDocumentsByPatient needs rework for subcollection model or Collection Group Queries."
|
|
8774
|
+
);
|
|
8775
|
+
return { documents: [], lastDoc: null };
|
|
8776
|
+
}
|
|
8428
8777
|
/**
|
|
8429
|
-
* Get filled documents for a
|
|
8778
|
+
* Get filled documents for a practitioner (NEEDS REWORK for subcollections or Collection Group Query)
|
|
8779
|
+
* @param practitionerId - ID of the practitioner
|
|
8780
|
+
* @param pageSize - Number of documents to retrieve
|
|
8781
|
+
* @param lastDoc - Last document from previous page for pagination
|
|
8782
|
+
* @returns Array of filled documents and the last document for pagination
|
|
8783
|
+
*/
|
|
8784
|
+
async getFilledDocumentsByPractitioner(practitionerId, pageSize = 20, lastDoc) {
|
|
8785
|
+
console.warn(
|
|
8786
|
+
"getFilledDocumentsByPractitioner needs rework for subcollection model or Collection Group Queries."
|
|
8787
|
+
);
|
|
8788
|
+
return { documents: [], lastDoc: null };
|
|
8789
|
+
}
|
|
8790
|
+
/**
|
|
8791
|
+
* Get filled documents for a clinic (NEEDS REWORK for subcollections or Collection Group Query)
|
|
8430
8792
|
* @param clinicId - ID of the clinic
|
|
8431
8793
|
* @param pageSize - Number of documents to retrieve
|
|
8432
8794
|
* @param lastDoc - Last document from previous page for pagination
|
|
8433
8795
|
* @returns Array of filled documents and the last document for pagination
|
|
8434
8796
|
*/
|
|
8435
8797
|
async getFilledDocumentsByClinic(clinicId, pageSize = 20, lastDoc) {
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
where17("clinicId", "==", clinicId),
|
|
8439
|
-
orderBy6("updatedAt", "desc"),
|
|
8440
|
-
limit9(pageSize)
|
|
8798
|
+
console.warn(
|
|
8799
|
+
"getFilledDocumentsByClinic needs rework for subcollection model or Collection Group Queries."
|
|
8441
8800
|
);
|
|
8442
|
-
|
|
8443
|
-
q = query17(q, startAfter9(lastDoc));
|
|
8444
|
-
}
|
|
8445
|
-
const querySnapshot = await getDocs17(q);
|
|
8446
|
-
const documents = [];
|
|
8447
|
-
let lastVisible = null;
|
|
8448
|
-
querySnapshot.forEach((doc32) => {
|
|
8449
|
-
documents.push(doc32.data());
|
|
8450
|
-
lastVisible = doc32;
|
|
8451
|
-
});
|
|
8452
|
-
return {
|
|
8453
|
-
documents,
|
|
8454
|
-
lastDoc: lastVisible
|
|
8455
|
-
};
|
|
8801
|
+
return { documents: [], lastDoc: null };
|
|
8456
8802
|
}
|
|
8457
8803
|
/**
|
|
8458
|
-
* Get filled documents by template
|
|
8804
|
+
* Get filled documents by template (NEEDS REWORK for subcollections or Collection Group Query)
|
|
8459
8805
|
* @param templateId - ID of the template
|
|
8460
8806
|
* @param pageSize - Number of documents to retrieve
|
|
8461
8807
|
* @param lastDoc - Last document from previous page for pagination
|
|
8462
8808
|
* @returns Array of filled documents and the last document for pagination
|
|
8463
8809
|
*/
|
|
8464
8810
|
async getFilledDocumentsByTemplate(templateId, pageSize = 20, lastDoc) {
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
where17("templateId", "==", templateId),
|
|
8468
|
-
orderBy6("updatedAt", "desc"),
|
|
8469
|
-
limit9(pageSize)
|
|
8811
|
+
console.warn(
|
|
8812
|
+
"getFilledDocumentsByTemplate needs rework for subcollection model or Collection Group Queries."
|
|
8470
8813
|
);
|
|
8471
|
-
|
|
8472
|
-
q = query17(q, startAfter9(lastDoc));
|
|
8473
|
-
}
|
|
8474
|
-
const querySnapshot = await getDocs17(q);
|
|
8475
|
-
const documents = [];
|
|
8476
|
-
let lastVisible = null;
|
|
8477
|
-
querySnapshot.forEach((doc32) => {
|
|
8478
|
-
documents.push(doc32.data());
|
|
8479
|
-
lastVisible = doc32;
|
|
8480
|
-
});
|
|
8481
|
-
return {
|
|
8482
|
-
documents,
|
|
8483
|
-
lastDoc: lastVisible
|
|
8484
|
-
};
|
|
8814
|
+
return { documents: [], lastDoc: null };
|
|
8485
8815
|
}
|
|
8486
8816
|
/**
|
|
8487
|
-
* Get filled documents by status
|
|
8817
|
+
* Get filled documents by status (NEEDS REWORK for subcollections or Collection Group Query)
|
|
8488
8818
|
* @param status - Status to filter by
|
|
8489
8819
|
* @param pageSize - Number of documents to retrieve
|
|
8490
8820
|
* @param lastDoc - Last document from previous page for pagination
|
|
8491
8821
|
* @returns Array of filled documents and the last document for pagination
|
|
8492
8822
|
*/
|
|
8493
8823
|
async getFilledDocumentsByStatus(status, pageSize = 20, lastDoc) {
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
where17("status", "==", status),
|
|
8497
|
-
orderBy6("updatedAt", "desc"),
|
|
8498
|
-
limit9(pageSize)
|
|
8824
|
+
console.warn(
|
|
8825
|
+
"getFilledDocumentsByStatus needs rework for subcollection model or Collection Group Queries."
|
|
8499
8826
|
);
|
|
8500
|
-
|
|
8501
|
-
q = query17(q, startAfter9(lastDoc));
|
|
8502
|
-
}
|
|
8503
|
-
const querySnapshot = await getDocs17(q);
|
|
8504
|
-
const documents = [];
|
|
8505
|
-
let lastVisible = null;
|
|
8506
|
-
querySnapshot.forEach((doc32) => {
|
|
8507
|
-
documents.push(doc32.data());
|
|
8508
|
-
lastVisible = doc32;
|
|
8509
|
-
});
|
|
8510
|
-
return {
|
|
8511
|
-
documents,
|
|
8512
|
-
lastDoc: lastVisible
|
|
8513
|
-
};
|
|
8827
|
+
return { documents: [], lastDoc: null };
|
|
8514
8828
|
}
|
|
8515
8829
|
};
|
|
8516
8830
|
|
|
@@ -9061,7 +9375,7 @@ async function searchCalendarEventsUtil(db, params) {
|
|
|
9061
9375
|
const finalQuery = query21(collectionRef, ...constraints);
|
|
9062
9376
|
const querySnapshot = await getDocs21(finalQuery);
|
|
9063
9377
|
const events = querySnapshot.docs.map(
|
|
9064
|
-
(
|
|
9378
|
+
(doc33) => ({ id: doc33.id, ...doc33.data() })
|
|
9065
9379
|
);
|
|
9066
9380
|
return events;
|
|
9067
9381
|
} catch (error) {
|
|
@@ -9154,7 +9468,7 @@ async function getPractitionerSyncedCalendarsUtil(db, practitionerId) {
|
|
|
9154
9468
|
);
|
|
9155
9469
|
const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
|
|
9156
9470
|
const querySnapshot = await getDocs22(q);
|
|
9157
|
-
return querySnapshot.docs.map((
|
|
9471
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
9158
9472
|
}
|
|
9159
9473
|
async function getPatientSyncedCalendarUtil(db, patientId, calendarId) {
|
|
9160
9474
|
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
@@ -9171,7 +9485,7 @@ async function getPatientSyncedCalendarsUtil(db, patientId) {
|
|
|
9171
9485
|
);
|
|
9172
9486
|
const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
|
|
9173
9487
|
const querySnapshot = await getDocs22(q);
|
|
9174
|
-
return querySnapshot.docs.map((
|
|
9488
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
9175
9489
|
}
|
|
9176
9490
|
async function getClinicSyncedCalendarUtil(db, clinicId, calendarId) {
|
|
9177
9491
|
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
@@ -9188,7 +9502,7 @@ async function getClinicSyncedCalendarsUtil(db, clinicId) {
|
|
|
9188
9502
|
);
|
|
9189
9503
|
const q = query22(calendarsRef, orderBy11("createdAt", "desc"));
|
|
9190
9504
|
const querySnapshot = await getDocs22(q);
|
|
9191
|
-
return querySnapshot.docs.map((
|
|
9505
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
9192
9506
|
}
|
|
9193
9507
|
async function updatePractitionerSyncedCalendarUtil(db, practitionerId, calendarId, updateData) {
|
|
9194
9508
|
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
@@ -10543,9 +10857,9 @@ var CalendarServiceV2 = class extends BaseService {
|
|
|
10543
10857
|
where23("eventTime.start", "<=", Timestamp25.fromDate(endDate))
|
|
10544
10858
|
);
|
|
10545
10859
|
const eventsSnapshot = await getDocs23(q);
|
|
10546
|
-
const events = eventsSnapshot.docs.map((
|
|
10547
|
-
id:
|
|
10548
|
-
...
|
|
10860
|
+
const events = eventsSnapshot.docs.map((doc33) => ({
|
|
10861
|
+
id: doc33.id,
|
|
10862
|
+
...doc33.data()
|
|
10549
10863
|
}));
|
|
10550
10864
|
const calendars = await this.syncedCalendarsService.getPractitionerSyncedCalendars(
|
|
10551
10865
|
doctorId
|
|
@@ -11177,7 +11491,7 @@ var CalendarServiceV2 = class extends BaseService {
|
|
|
11177
11491
|
])
|
|
11178
11492
|
);
|
|
11179
11493
|
const querySnapshot = await getDocs23(q);
|
|
11180
|
-
return querySnapshot.docs.map((
|
|
11494
|
+
return querySnapshot.docs.map((doc33) => doc33.data());
|
|
11181
11495
|
}
|
|
11182
11496
|
/**
|
|
11183
11497
|
* Calculates available time slots based on working hours, schedule and existing appointments
|
|
@@ -11456,7 +11770,7 @@ var ReviewService = class extends BaseService {
|
|
|
11456
11770
|
where24("patientId", "==", patientId)
|
|
11457
11771
|
);
|
|
11458
11772
|
const snapshot = await getDocs24(q);
|
|
11459
|
-
return snapshot.docs.map((
|
|
11773
|
+
return snapshot.docs.map((doc33) => doc33.data());
|
|
11460
11774
|
}
|
|
11461
11775
|
/**
|
|
11462
11776
|
* Gets all reviews for a specific clinic
|
|
@@ -11469,7 +11783,7 @@ var ReviewService = class extends BaseService {
|
|
|
11469
11783
|
where24("clinicReview.clinicId", "==", clinicId)
|
|
11470
11784
|
);
|
|
11471
11785
|
const snapshot = await getDocs24(q);
|
|
11472
|
-
return snapshot.docs.map((
|
|
11786
|
+
return snapshot.docs.map((doc33) => doc33.data());
|
|
11473
11787
|
}
|
|
11474
11788
|
/**
|
|
11475
11789
|
* Gets all reviews for a specific practitioner
|
|
@@ -11482,7 +11796,7 @@ var ReviewService = class extends BaseService {
|
|
|
11482
11796
|
where24("practitionerReview.practitionerId", "==", practitionerId)
|
|
11483
11797
|
);
|
|
11484
11798
|
const snapshot = await getDocs24(q);
|
|
11485
|
-
return snapshot.docs.map((
|
|
11799
|
+
return snapshot.docs.map((doc33) => doc33.data());
|
|
11486
11800
|
}
|
|
11487
11801
|
/**
|
|
11488
11802
|
* Gets all reviews for a specific procedure
|
|
@@ -11495,7 +11809,7 @@ var ReviewService = class extends BaseService {
|
|
|
11495
11809
|
where24("procedureReview.procedureId", "==", procedureId)
|
|
11496
11810
|
);
|
|
11497
11811
|
const snapshot = await getDocs24(q);
|
|
11498
|
-
return snapshot.docs.map((
|
|
11812
|
+
return snapshot.docs.map((doc33) => doc33.data());
|
|
11499
11813
|
}
|
|
11500
11814
|
/**
|
|
11501
11815
|
* Gets all reviews for a specific appointment
|
|
@@ -11924,7 +12238,10 @@ var ReviewService = class extends BaseService {
|
|
|
11924
12238
|
|
|
11925
12239
|
// src/services/appointment/appointment.service.ts
|
|
11926
12240
|
import {
|
|
11927
|
-
Timestamp as Timestamp28
|
|
12241
|
+
Timestamp as Timestamp28,
|
|
12242
|
+
serverTimestamp as serverTimestamp24,
|
|
12243
|
+
arrayUnion as arrayUnion8,
|
|
12244
|
+
arrayRemove as arrayRemove7
|
|
11928
12245
|
} from "firebase/firestore";
|
|
11929
12246
|
import { getFunctions, httpsCallable } from "firebase/functions";
|
|
11930
12247
|
|
|
@@ -12210,7 +12527,7 @@ async function updateCalendarEventStatus(db, calendarEventId, appointmentStatus)
|
|
|
12210
12527
|
case "canceled_clinic" /* CANCELED_CLINIC */:
|
|
12211
12528
|
calendarStatus = "canceled";
|
|
12212
12529
|
break;
|
|
12213
|
-
case
|
|
12530
|
+
case AppointmentStatus.RESCHEDULED:
|
|
12214
12531
|
calendarStatus = "rescheduled";
|
|
12215
12532
|
break;
|
|
12216
12533
|
case "completed" /* COMPLETED */:
|
|
@@ -12284,7 +12601,7 @@ async function searchAppointmentsUtil(db, params) {
|
|
|
12284
12601
|
const q = query25(collection25(db, APPOINTMENTS_COLLECTION), ...constraints);
|
|
12285
12602
|
const querySnapshot = await getDocs25(q);
|
|
12286
12603
|
const appointments = querySnapshot.docs.map(
|
|
12287
|
-
(
|
|
12604
|
+
(doc33) => doc33.data()
|
|
12288
12605
|
);
|
|
12289
12606
|
const lastDoc = querySnapshot.docs.length > 0 ? querySnapshot.docs[querySnapshot.docs.length - 1] : null;
|
|
12290
12607
|
return { appointments, lastDoc };
|
|
@@ -12307,12 +12624,13 @@ var AppointmentService = class extends BaseService {
|
|
|
12307
12624
|
* @param practitionerService Practitioner service instance
|
|
12308
12625
|
* @param clinicService Clinic service instance
|
|
12309
12626
|
*/
|
|
12310
|
-
constructor(db, auth, app, calendarService, patientService, practitionerService, clinicService) {
|
|
12627
|
+
constructor(db, auth, app, calendarService, patientService, practitionerService, clinicService, filledDocumentService) {
|
|
12311
12628
|
super(db, auth, app);
|
|
12312
12629
|
this.calendarService = calendarService;
|
|
12313
12630
|
this.patientService = patientService;
|
|
12314
12631
|
this.practitionerService = practitionerService;
|
|
12315
12632
|
this.clinicService = clinicService;
|
|
12633
|
+
this.filledDocumentService = filledDocumentService;
|
|
12316
12634
|
this.functions = getFunctions(app, "europe-west6");
|
|
12317
12635
|
}
|
|
12318
12636
|
/**
|
|
@@ -12668,152 +12986,331 @@ var AppointmentService = class extends BaseService {
|
|
|
12668
12986
|
*
|
|
12669
12987
|
* @param appointmentId ID of the appointment
|
|
12670
12988
|
* @param newStatus New status to set
|
|
12671
|
-
* @param
|
|
12672
|
-
* @param canceledBy Required if canceling the appointment
|
|
12989
|
+
* @param details Optional details for the status change
|
|
12673
12990
|
* @returns The updated appointment
|
|
12674
12991
|
*/
|
|
12675
|
-
async updateAppointmentStatus(appointmentId, newStatus,
|
|
12992
|
+
async updateAppointmentStatus(appointmentId, newStatus, details) {
|
|
12676
12993
|
console.log(
|
|
12677
12994
|
`[APPOINTMENT_SERVICE] Updating status of appointment ${appointmentId} to ${newStatus}`
|
|
12678
12995
|
);
|
|
12679
|
-
const updateData = {
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12996
|
+
const updateData = {
|
|
12997
|
+
status: newStatus,
|
|
12998
|
+
updatedAt: serverTimestamp24()
|
|
12999
|
+
};
|
|
13000
|
+
if (newStatus === "canceled_clinic" /* CANCELED_CLINIC */ || newStatus === "canceled_patient" /* CANCELED_PATIENT */ || newStatus === "canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */) {
|
|
13001
|
+
if (!(details == null ? void 0 : details.cancellationReason)) {
|
|
13002
|
+
throw new Error("Cancellation reason is required when canceling.");
|
|
12685
13003
|
}
|
|
12686
|
-
if (!canceledBy) {
|
|
12687
|
-
throw new Error(
|
|
12688
|
-
"Canceled by is required when canceling an appointment"
|
|
12689
|
-
);
|
|
13004
|
+
if (!(details == null ? void 0 : details.canceledBy)) {
|
|
13005
|
+
throw new Error("Canceled by is required when canceling.");
|
|
12690
13006
|
}
|
|
12691
|
-
updateData.cancellationReason = cancellationReason;
|
|
12692
|
-
updateData.canceledBy = canceledBy;
|
|
13007
|
+
updateData.cancellationReason = details.cancellationReason;
|
|
13008
|
+
updateData.canceledBy = details.canceledBy;
|
|
13009
|
+
updateData.cancellationTime = Timestamp28.now();
|
|
12693
13010
|
}
|
|
12694
13011
|
if (newStatus === "confirmed" /* CONFIRMED */) {
|
|
12695
13012
|
updateData.confirmationTime = Timestamp28.now();
|
|
12696
13013
|
}
|
|
13014
|
+
if (newStatus === "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
13015
|
+
updateData.rescheduleTime = Timestamp28.now();
|
|
13016
|
+
}
|
|
12697
13017
|
return this.updateAppointment(appointmentId, updateData);
|
|
12698
13018
|
}
|
|
12699
13019
|
/**
|
|
12700
|
-
* Confirms an
|
|
12701
|
-
*
|
|
12702
|
-
* @param appointmentId ID of the appointment to confirm
|
|
12703
|
-
* @returns The confirmed appointment
|
|
13020
|
+
* Confirms a PENDING appointment by an Admin/Clinic.
|
|
12704
13021
|
*/
|
|
12705
|
-
async
|
|
13022
|
+
async confirmAppointmentAdmin(appointmentId) {
|
|
12706
13023
|
console.log(
|
|
12707
|
-
`[APPOINTMENT_SERVICE]
|
|
13024
|
+
`[APPOINTMENT_SERVICE] Admin confirming appointment: ${appointmentId}`
|
|
12708
13025
|
);
|
|
13026
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13027
|
+
if (!appointment)
|
|
13028
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13029
|
+
if (appointment.status !== "pending" /* PENDING */) {
|
|
13030
|
+
throw new Error(
|
|
13031
|
+
`Appointment ${appointmentId} is not in PENDING state to be confirmed.`
|
|
13032
|
+
);
|
|
13033
|
+
}
|
|
12709
13034
|
return this.updateAppointmentStatus(
|
|
12710
13035
|
appointmentId,
|
|
12711
13036
|
"confirmed" /* CONFIRMED */
|
|
12712
13037
|
);
|
|
12713
13038
|
}
|
|
12714
13039
|
/**
|
|
12715
|
-
* Cancels an appointment
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
13040
|
+
* Cancels an appointment by the User (Patient).
|
|
13041
|
+
*/
|
|
13042
|
+
async cancelAppointmentUser(appointmentId, reason) {
|
|
13043
|
+
console.log(
|
|
13044
|
+
`[APPOINTMENT_SERVICE] User canceling appointment: ${appointmentId}`
|
|
13045
|
+
);
|
|
13046
|
+
return this.updateAppointmentStatus(
|
|
13047
|
+
appointmentId,
|
|
13048
|
+
"canceled_patient" /* CANCELED_PATIENT */,
|
|
13049
|
+
{
|
|
13050
|
+
cancellationReason: reason,
|
|
13051
|
+
canceledBy: "patient"
|
|
13052
|
+
}
|
|
13053
|
+
);
|
|
13054
|
+
}
|
|
13055
|
+
/**
|
|
13056
|
+
* Cancels an appointment by an Admin/Clinic.
|
|
12720
13057
|
*/
|
|
12721
|
-
async
|
|
13058
|
+
async cancelAppointmentAdmin(appointmentId, reason) {
|
|
12722
13059
|
console.log(
|
|
12723
|
-
`[APPOINTMENT_SERVICE]
|
|
13060
|
+
`[APPOINTMENT_SERVICE] Admin canceling appointment: ${appointmentId}`
|
|
12724
13061
|
);
|
|
12725
13062
|
return this.updateAppointmentStatus(
|
|
12726
13063
|
appointmentId,
|
|
12727
13064
|
"canceled_clinic" /* CANCELED_CLINIC */,
|
|
12728
|
-
|
|
12729
|
-
|
|
13065
|
+
{
|
|
13066
|
+
cancellationReason: reason,
|
|
13067
|
+
canceledBy: "clinic"
|
|
13068
|
+
}
|
|
12730
13069
|
);
|
|
12731
13070
|
}
|
|
12732
13071
|
/**
|
|
12733
|
-
*
|
|
12734
|
-
*
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
13072
|
+
* Admin proposes to reschedule an appointment.
|
|
13073
|
+
* Sets status to RESCHEDULED_BY_CLINIC and updates times.
|
|
13074
|
+
*/
|
|
13075
|
+
async rescheduleAppointmentAdmin(appointmentId, newStartTime, newEndTime) {
|
|
13076
|
+
console.log(
|
|
13077
|
+
`[APPOINTMENT_SERVICE] Admin rescheduling appointment: ${appointmentId}`
|
|
13078
|
+
);
|
|
13079
|
+
if (newEndTime.toMillis() <= newStartTime.toMillis()) {
|
|
13080
|
+
throw new Error("New end time must be after new start time.");
|
|
13081
|
+
}
|
|
13082
|
+
const updateData = {
|
|
13083
|
+
status: "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */,
|
|
13084
|
+
appointmentStartTime: newStartTime,
|
|
13085
|
+
appointmentEndTime: newEndTime,
|
|
13086
|
+
rescheduleTime: Timestamp28.now(),
|
|
13087
|
+
confirmationTime: null,
|
|
13088
|
+
updatedAt: serverTimestamp24()
|
|
13089
|
+
};
|
|
13090
|
+
return this.updateAppointment(appointmentId, updateData);
|
|
13091
|
+
}
|
|
13092
|
+
/**
|
|
13093
|
+
* User confirms a reschedule proposed by the clinic.
|
|
13094
|
+
* Status changes from RESCHEDULED_BY_CLINIC to CONFIRMED.
|
|
12738
13095
|
*/
|
|
12739
|
-
async
|
|
13096
|
+
async rescheduleAppointmentConfirmUser(appointmentId) {
|
|
12740
13097
|
console.log(
|
|
12741
|
-
`[APPOINTMENT_SERVICE]
|
|
13098
|
+
`[APPOINTMENT_SERVICE] User confirming reschedule for: ${appointmentId}`
|
|
12742
13099
|
);
|
|
13100
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13101
|
+
if (!appointment)
|
|
13102
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13103
|
+
if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
13104
|
+
throw new Error(
|
|
13105
|
+
`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
|
|
13106
|
+
);
|
|
13107
|
+
}
|
|
12743
13108
|
return this.updateAppointmentStatus(
|
|
12744
13109
|
appointmentId,
|
|
12745
|
-
"
|
|
12746
|
-
reason,
|
|
12747
|
-
"patient"
|
|
13110
|
+
"confirmed" /* CONFIRMED */
|
|
12748
13111
|
);
|
|
12749
13112
|
}
|
|
12750
13113
|
/**
|
|
12751
|
-
*
|
|
12752
|
-
*
|
|
12753
|
-
* @param appointmentId ID of the appointment
|
|
12754
|
-
* @returns The updated appointment
|
|
13114
|
+
* User rejects a reschedule proposed by the clinic.
|
|
13115
|
+
* Status changes from RESCHEDULED_BY_CLINIC to CANCELED_PATIENT_RESCHEDULED.
|
|
12755
13116
|
*/
|
|
12756
|
-
async
|
|
13117
|
+
async rescheduleAppointmentRejectUser(appointmentId, reason) {
|
|
12757
13118
|
console.log(
|
|
12758
|
-
`[APPOINTMENT_SERVICE]
|
|
13119
|
+
`[APPOINTMENT_SERVICE] User rejecting reschedule for: ${appointmentId}`
|
|
12759
13120
|
);
|
|
13121
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13122
|
+
if (!appointment)
|
|
13123
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13124
|
+
if (appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
13125
|
+
throw new Error(
|
|
13126
|
+
`Appointment ${appointmentId} is not in RESCHEDULED_BY_CLINIC state.`
|
|
13127
|
+
);
|
|
13128
|
+
}
|
|
12760
13129
|
return this.updateAppointmentStatus(
|
|
12761
13130
|
appointmentId,
|
|
12762
|
-
"
|
|
13131
|
+
"canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */,
|
|
13132
|
+
{
|
|
13133
|
+
cancellationReason: reason,
|
|
13134
|
+
canceledBy: "patient"
|
|
13135
|
+
}
|
|
12763
13136
|
);
|
|
12764
13137
|
}
|
|
12765
13138
|
/**
|
|
12766
|
-
*
|
|
12767
|
-
*
|
|
12768
|
-
* @param appointmentId ID of the appointment
|
|
12769
|
-
* @returns The updated appointment
|
|
13139
|
+
* Admin checks in a patient for their appointment.
|
|
13140
|
+
* Requires all pending user forms to be completed.
|
|
12770
13141
|
*/
|
|
12771
|
-
async
|
|
12772
|
-
console.log(
|
|
13142
|
+
async checkInPatientAdmin(appointmentId) {
|
|
13143
|
+
console.log(
|
|
13144
|
+
`[APPOINTMENT_SERVICE] Admin checking in patient for: ${appointmentId}`
|
|
13145
|
+
);
|
|
13146
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13147
|
+
if (!appointment)
|
|
13148
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13149
|
+
if (appointment.pendingUserFormsIds && appointment.pendingUserFormsIds.length > 0) {
|
|
13150
|
+
throw new Error(
|
|
13151
|
+
`Cannot check in: Patient has ${appointment.pendingUserFormsIds.length} pending required form(s). IDs: ${appointment.pendingUserFormsIds.join(
|
|
13152
|
+
", "
|
|
13153
|
+
)}`
|
|
13154
|
+
);
|
|
13155
|
+
}
|
|
13156
|
+
if (appointment.status !== "confirmed" /* CONFIRMED */ && appointment.status !== "rescheduled_by_clinic" /* RESCHEDULED_BY_CLINIC */) {
|
|
13157
|
+
console.warn(
|
|
13158
|
+
`Checking in appointment ${appointmentId} with status ${appointment.status}. Ensure this is intended.`
|
|
13159
|
+
);
|
|
13160
|
+
}
|
|
12773
13161
|
return this.updateAppointmentStatus(
|
|
12774
13162
|
appointmentId,
|
|
12775
|
-
"
|
|
13163
|
+
"checked_in" /* CHECKED_IN */
|
|
12776
13164
|
);
|
|
12777
13165
|
}
|
|
12778
13166
|
/**
|
|
12779
|
-
*
|
|
12780
|
-
*
|
|
12781
|
-
* @param appointmentId ID of the appointment
|
|
12782
|
-
* @param actualDurationMinutes Actual duration of the appointment in minutes
|
|
12783
|
-
* @returns The updated appointment
|
|
13167
|
+
* Doctor starts the appointment procedure.
|
|
12784
13168
|
*/
|
|
12785
|
-
async
|
|
13169
|
+
async startAppointmentDoctor(appointmentId) {
|
|
12786
13170
|
console.log(
|
|
12787
|
-
`[APPOINTMENT_SERVICE]
|
|
13171
|
+
`[APPOINTMENT_SERVICE] Doctor starting appointment: ${appointmentId}`
|
|
12788
13172
|
);
|
|
13173
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13174
|
+
if (!appointment)
|
|
13175
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13176
|
+
if (appointment.status !== "checked_in" /* CHECKED_IN */) {
|
|
13177
|
+
throw new Error(
|
|
13178
|
+
`Appointment ${appointmentId} must be CHECKED_IN to start.`
|
|
13179
|
+
);
|
|
13180
|
+
}
|
|
13181
|
+
const updateData = {
|
|
13182
|
+
status: "in_progress" /* IN_PROGRESS */,
|
|
13183
|
+
procedureActualStartTime: Timestamp28.now(),
|
|
13184
|
+
// Set actual start time
|
|
13185
|
+
updatedAt: serverTimestamp24()
|
|
13186
|
+
};
|
|
13187
|
+
return this.updateAppointment(appointmentId, updateData);
|
|
13188
|
+
}
|
|
13189
|
+
/**
|
|
13190
|
+
* Doctor completes and finalizes the appointment.
|
|
13191
|
+
*/
|
|
13192
|
+
async completeAppointmentDoctor(appointmentId, finalizationNotes, actualDurationMinutesInput) {
|
|
13193
|
+
console.log(
|
|
13194
|
+
`[APPOINTMENT_SERVICE] Doctor completing appointment: ${appointmentId}`
|
|
13195
|
+
);
|
|
13196
|
+
const currentUser = this.auth.currentUser;
|
|
13197
|
+
if (!currentUser)
|
|
13198
|
+
throw new Error("Authentication required to complete appointment.");
|
|
13199
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13200
|
+
if (!appointment)
|
|
13201
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13202
|
+
let calculatedDurationMinutes = actualDurationMinutesInput;
|
|
13203
|
+
const procedureCompletionTime = Timestamp28.now();
|
|
13204
|
+
if (calculatedDurationMinutes === void 0 && appointment.procedureActualStartTime) {
|
|
13205
|
+
const startTimeMillis = appointment.procedureActualStartTime.toMillis();
|
|
13206
|
+
const endTimeMillis = procedureCompletionTime.toMillis();
|
|
13207
|
+
if (endTimeMillis > startTimeMillis) {
|
|
13208
|
+
calculatedDurationMinutes = Math.round(
|
|
13209
|
+
(endTimeMillis - startTimeMillis) / 6e4
|
|
13210
|
+
);
|
|
13211
|
+
}
|
|
13212
|
+
}
|
|
12789
13213
|
const updateData = {
|
|
12790
13214
|
status: "completed" /* COMPLETED */,
|
|
12791
|
-
actualDurationMinutes
|
|
13215
|
+
actualDurationMinutes: calculatedDurationMinutes,
|
|
13216
|
+
// Use calculated or provided duration
|
|
13217
|
+
finalizedDetails: {
|
|
13218
|
+
by: currentUser.uid,
|
|
13219
|
+
// This is used ID, not practitioner's profile ID (just so we know who completed the appointment)
|
|
13220
|
+
at: procedureCompletionTime,
|
|
13221
|
+
// Use consistent completion timestamp
|
|
13222
|
+
notes: finalizationNotes
|
|
13223
|
+
},
|
|
13224
|
+
// Optionally update appointmentEndTime to the actual completion time
|
|
13225
|
+
// appointmentEndTime: procedureCompletionTime,
|
|
13226
|
+
updatedAt: serverTimestamp24()
|
|
12792
13227
|
};
|
|
12793
13228
|
return this.updateAppointment(appointmentId, updateData);
|
|
12794
13229
|
}
|
|
12795
13230
|
/**
|
|
12796
|
-
*
|
|
12797
|
-
*
|
|
12798
|
-
* @param appointmentId ID of the appointment
|
|
12799
|
-
* @returns The updated appointment
|
|
13231
|
+
* Admin marks an appointment as No-Show.
|
|
12800
13232
|
*/
|
|
12801
|
-
async
|
|
13233
|
+
async markNoShowAdmin(appointmentId) {
|
|
12802
13234
|
console.log(
|
|
12803
|
-
`[APPOINTMENT_SERVICE]
|
|
13235
|
+
`[APPOINTMENT_SERVICE] Admin marking no-show for: ${appointmentId}`
|
|
12804
13236
|
);
|
|
13237
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13238
|
+
if (!appointment)
|
|
13239
|
+
throw new Error(`Appointment ${appointmentId} not found.`);
|
|
13240
|
+
if (Timestamp28.now().toMillis() < appointment.appointmentStartTime.toMillis()) {
|
|
13241
|
+
throw new Error("Cannot mark no-show before appointment start time.");
|
|
13242
|
+
}
|
|
12805
13243
|
return this.updateAppointmentStatus(
|
|
12806
13244
|
appointmentId,
|
|
12807
|
-
"no_show" /* NO_SHOW
|
|
13245
|
+
"no_show" /* NO_SHOW */,
|
|
13246
|
+
{
|
|
13247
|
+
cancellationReason: "Patient did not show up for the appointment.",
|
|
13248
|
+
canceledBy: "clinic"
|
|
13249
|
+
}
|
|
12808
13250
|
);
|
|
12809
13251
|
}
|
|
13252
|
+
/**
|
|
13253
|
+
* Adds a media item to an appointment.
|
|
13254
|
+
*/
|
|
13255
|
+
async addMediaToAppointment(appointmentId, mediaItemData) {
|
|
13256
|
+
console.log(
|
|
13257
|
+
`[APPOINTMENT_SERVICE] Adding media to appointment ${appointmentId}`
|
|
13258
|
+
);
|
|
13259
|
+
const currentUser = this.auth.currentUser;
|
|
13260
|
+
if (!currentUser) throw new Error("Authentication required.");
|
|
13261
|
+
const newMediaItem = {
|
|
13262
|
+
...mediaItemData,
|
|
13263
|
+
id: this.generateId(),
|
|
13264
|
+
uploadedAt: Timestamp28.now(),
|
|
13265
|
+
uploadedBy: currentUser.uid
|
|
13266
|
+
};
|
|
13267
|
+
const updateData = {
|
|
13268
|
+
media: arrayUnion8(newMediaItem),
|
|
13269
|
+
updatedAt: serverTimestamp24()
|
|
13270
|
+
};
|
|
13271
|
+
return this.updateAppointment(appointmentId, updateData);
|
|
13272
|
+
}
|
|
13273
|
+
/**
|
|
13274
|
+
* Removes a media item from an appointment.
|
|
13275
|
+
*/
|
|
13276
|
+
async removeMediaFromAppointment(appointmentId, mediaItemId) {
|
|
13277
|
+
console.log(
|
|
13278
|
+
`[APPOINTMENT_SERVICE] Removing media ${mediaItemId} from appointment ${appointmentId}`
|
|
13279
|
+
);
|
|
13280
|
+
const appointment = await this.getAppointmentById(appointmentId);
|
|
13281
|
+
if (!appointment || !appointment.media) {
|
|
13282
|
+
throw new Error("Appointment or media list not found.");
|
|
13283
|
+
}
|
|
13284
|
+
const mediaToRemove = appointment.media.find((m) => m.id === mediaItemId);
|
|
13285
|
+
if (!mediaToRemove) {
|
|
13286
|
+
throw new Error(`Media item ${mediaItemId} not found in appointment.`);
|
|
13287
|
+
}
|
|
13288
|
+
const updateData = {
|
|
13289
|
+
media: arrayRemove7(mediaToRemove),
|
|
13290
|
+
updatedAt: serverTimestamp24()
|
|
13291
|
+
};
|
|
13292
|
+
return this.updateAppointment(appointmentId, updateData);
|
|
13293
|
+
}
|
|
13294
|
+
/**
|
|
13295
|
+
* Adds or updates review information for an appointment.
|
|
13296
|
+
*/
|
|
13297
|
+
async addReviewToAppointment(appointmentId, reviewData) {
|
|
13298
|
+
console.log(
|
|
13299
|
+
`[APPOINTMENT_SERVICE] Adding review to appointment ${appointmentId}`
|
|
13300
|
+
);
|
|
13301
|
+
const newReviewInfo = {
|
|
13302
|
+
...reviewData,
|
|
13303
|
+
reviewId: this.generateId(),
|
|
13304
|
+
reviewedAt: Timestamp28.now()
|
|
13305
|
+
};
|
|
13306
|
+
const updateData = {
|
|
13307
|
+
reviewInfo: newReviewInfo,
|
|
13308
|
+
updatedAt: serverTimestamp24()
|
|
13309
|
+
};
|
|
13310
|
+
return this.updateAppointment(appointmentId, updateData);
|
|
13311
|
+
}
|
|
12810
13312
|
/**
|
|
12811
13313
|
* Updates the payment status of an appointment.
|
|
12812
|
-
*
|
|
12813
|
-
* @param appointmentId ID of the appointment
|
|
12814
|
-
* @param paymentStatus New payment status
|
|
12815
|
-
* @param paymentTransactionId Optional transaction ID for the payment
|
|
12816
|
-
* @returns The updated appointment
|
|
12817
13314
|
*/
|
|
12818
13315
|
async updatePaymentStatus(appointmentId, paymentStatus, paymentTransactionId) {
|
|
12819
13316
|
console.log(
|
|
@@ -12821,7 +13318,8 @@ var AppointmentService = class extends BaseService {
|
|
|
12821
13318
|
);
|
|
12822
13319
|
const updateData = {
|
|
12823
13320
|
paymentStatus,
|
|
12824
|
-
paymentTransactionId: paymentTransactionId || null
|
|
13321
|
+
paymentTransactionId: paymentTransactionId || null,
|
|
13322
|
+
updatedAt: serverTimestamp24()
|
|
12825
13323
|
};
|
|
12826
13324
|
return this.updateAppointment(appointmentId, updateData);
|
|
12827
13325
|
}
|
|
@@ -12894,16 +13392,222 @@ var AppointmentService = class extends BaseService {
|
|
|
12894
13392
|
}
|
|
12895
13393
|
};
|
|
12896
13394
|
|
|
12897
|
-
// src/
|
|
13395
|
+
// src/services/patient/patientRequirements.service.ts
|
|
12898
13396
|
import {
|
|
12899
|
-
addDoc as addDoc3,
|
|
12900
13397
|
collection as collection26,
|
|
12901
|
-
doc as doc27,
|
|
12902
|
-
getDoc as getDoc29,
|
|
12903
13398
|
getDocs as getDocs26,
|
|
12904
13399
|
query as query26,
|
|
13400
|
+
where as where26,
|
|
13401
|
+
doc as doc27,
|
|
12905
13402
|
updateDoc as updateDoc26,
|
|
12906
|
-
|
|
13403
|
+
Timestamp as Timestamp29,
|
|
13404
|
+
orderBy as orderBy13,
|
|
13405
|
+
limit as limit11,
|
|
13406
|
+
startAfter as startAfter11,
|
|
13407
|
+
getDoc as getDoc29
|
|
13408
|
+
} from "firebase/firestore";
|
|
13409
|
+
|
|
13410
|
+
// src/types/patient/patient-requirements.ts
|
|
13411
|
+
var PatientInstructionStatus = /* @__PURE__ */ ((PatientInstructionStatus2) => {
|
|
13412
|
+
PatientInstructionStatus2["PENDING_NOTIFICATION"] = "pendingNotification";
|
|
13413
|
+
PatientInstructionStatus2["ACTION_DUE"] = "actionDue";
|
|
13414
|
+
PatientInstructionStatus2["ACTION_TAKEN"] = "actionTaken";
|
|
13415
|
+
PatientInstructionStatus2["MISSED"] = "missed";
|
|
13416
|
+
PatientInstructionStatus2["CANCELLED"] = "cancelled";
|
|
13417
|
+
PatientInstructionStatus2["SKIPPED"] = "skipped";
|
|
13418
|
+
return PatientInstructionStatus2;
|
|
13419
|
+
})(PatientInstructionStatus || {});
|
|
13420
|
+
var PatientRequirementOverallStatus = /* @__PURE__ */ ((PatientRequirementOverallStatus2) => {
|
|
13421
|
+
PatientRequirementOverallStatus2["ACTIVE"] = "active";
|
|
13422
|
+
PatientRequirementOverallStatus2["ALL_INSTRUCTIONS_MET"] = "allInstructionsMet";
|
|
13423
|
+
PatientRequirementOverallStatus2["PARTIALLY_COMPLETED"] = "partiallyCompleted";
|
|
13424
|
+
PatientRequirementOverallStatus2["FAILED"] = "failed";
|
|
13425
|
+
PatientRequirementOverallStatus2["CANCELLED_APPOINTMENT"] = "cancelledAppointment";
|
|
13426
|
+
PatientRequirementOverallStatus2["SUPERSEDED_RESCHEDULE"] = "supersededReschedule";
|
|
13427
|
+
PatientRequirementOverallStatus2["FAILED_TO_PROCESS"] = "failedToProcess";
|
|
13428
|
+
return PatientRequirementOverallStatus2;
|
|
13429
|
+
})(PatientRequirementOverallStatus || {});
|
|
13430
|
+
var PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME = "patientRequirements";
|
|
13431
|
+
|
|
13432
|
+
// src/services/patient/patientRequirements.service.ts
|
|
13433
|
+
var PatientRequirementsService = class extends BaseService {
|
|
13434
|
+
constructor(db, auth, app) {
|
|
13435
|
+
super(db, auth, app);
|
|
13436
|
+
}
|
|
13437
|
+
getPatientRequirementsCollectionRef(patientId) {
|
|
13438
|
+
return collection26(
|
|
13439
|
+
this.db,
|
|
13440
|
+
`patients/${patientId}/${PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME}`
|
|
13441
|
+
);
|
|
13442
|
+
}
|
|
13443
|
+
getPatientRequirementDocRef(patientId, instanceId) {
|
|
13444
|
+
return doc27(
|
|
13445
|
+
this.getPatientRequirementsCollectionRef(patientId),
|
|
13446
|
+
instanceId
|
|
13447
|
+
);
|
|
13448
|
+
}
|
|
13449
|
+
/**
|
|
13450
|
+
* Gets a specific patient requirement instance by its ID.
|
|
13451
|
+
* @param patientId - The ID of the patient.
|
|
13452
|
+
* @param instanceId - The ID of the requirement instance.
|
|
13453
|
+
* @returns The patient requirement instance or null if not found.
|
|
13454
|
+
*/
|
|
13455
|
+
async getPatientRequirementInstance(patientId, instanceId) {
|
|
13456
|
+
const docRef = this.getPatientRequirementDocRef(patientId, instanceId);
|
|
13457
|
+
const docSnap = await getDoc29(docRef);
|
|
13458
|
+
if (!docSnap.exists()) {
|
|
13459
|
+
return null;
|
|
13460
|
+
}
|
|
13461
|
+
const data = docSnap.data();
|
|
13462
|
+
return { id: docSnap.id, ...data };
|
|
13463
|
+
}
|
|
13464
|
+
/**
|
|
13465
|
+
* Retrieves patient requirement instances based on specified filters.
|
|
13466
|
+
* This is a flexible query method.
|
|
13467
|
+
*
|
|
13468
|
+
* @param patientId - The ID of the patient.
|
|
13469
|
+
* @param filters - Optional filters for appointmentId, overall statuses, instruction statuses, and due timeframes.
|
|
13470
|
+
* @param pageLimit - Optional limit for pagination.
|
|
13471
|
+
* @param lastVisible - Optional last document snapshot for pagination.
|
|
13472
|
+
* @returns A promise resolving to an array of matching patient requirement instances and the last document snapshot.
|
|
13473
|
+
*/
|
|
13474
|
+
async getAllPatientRequirementInstances(patientId, filters, pageLimit = 20, lastVisible) {
|
|
13475
|
+
const collRef = this.getPatientRequirementsCollectionRef(patientId);
|
|
13476
|
+
let q = query26(collRef, orderBy13("createdAt", "desc"));
|
|
13477
|
+
const queryConstraints = [];
|
|
13478
|
+
if ((filters == null ? void 0 : filters.appointmentId) && filters.appointmentId !== "all") {
|
|
13479
|
+
queryConstraints.push(
|
|
13480
|
+
where26("appointmentId", "==", filters.appointmentId)
|
|
13481
|
+
);
|
|
13482
|
+
}
|
|
13483
|
+
if ((filters == null ? void 0 : filters.statuses) && filters.statuses.length > 0) {
|
|
13484
|
+
queryConstraints.push(where26("overallStatus", "in", filters.statuses));
|
|
13485
|
+
}
|
|
13486
|
+
if (lastVisible) {
|
|
13487
|
+
queryConstraints.push(startAfter11(lastVisible));
|
|
13488
|
+
}
|
|
13489
|
+
queryConstraints.push(limit11(pageLimit));
|
|
13490
|
+
q = query26(collRef, ...queryConstraints);
|
|
13491
|
+
const snapshot = await getDocs26(q);
|
|
13492
|
+
let requirements = snapshot.docs.map((docSnap) => {
|
|
13493
|
+
const data = docSnap.data();
|
|
13494
|
+
return { id: docSnap.id, ...data };
|
|
13495
|
+
});
|
|
13496
|
+
if ((filters == null ? void 0 : filters.instructionStatuses) && filters.instructionStatuses.length > 0) {
|
|
13497
|
+
requirements = requirements.filter(
|
|
13498
|
+
(req) => req.instructions.some(
|
|
13499
|
+
(instr) => filters.instructionStatuses.includes(instr.status)
|
|
13500
|
+
)
|
|
13501
|
+
);
|
|
13502
|
+
}
|
|
13503
|
+
if (filters == null ? void 0 : filters.dueBefore) {
|
|
13504
|
+
const dueBeforeMillis = filters.dueBefore.toMillis();
|
|
13505
|
+
requirements = requirements.filter(
|
|
13506
|
+
(req) => req.instructions.some(
|
|
13507
|
+
(instr) => instr.dueTime.toMillis() < dueBeforeMillis
|
|
13508
|
+
)
|
|
13509
|
+
);
|
|
13510
|
+
}
|
|
13511
|
+
if (filters == null ? void 0 : filters.dueAfter) {
|
|
13512
|
+
const dueAfterMillis = filters.dueAfter.toMillis();
|
|
13513
|
+
requirements = requirements.filter(
|
|
13514
|
+
(req) => req.instructions.some(
|
|
13515
|
+
(instr) => instr.dueTime.toMillis() > dueAfterMillis
|
|
13516
|
+
)
|
|
13517
|
+
);
|
|
13518
|
+
}
|
|
13519
|
+
const newLastVisible = snapshot.docs[snapshot.docs.length - 1] || null;
|
|
13520
|
+
return { requirements, lastDoc: newLastVisible };
|
|
13521
|
+
}
|
|
13522
|
+
/**
|
|
13523
|
+
* Marks a specific instruction within a PatientRequirementInstance as ACTION_TAKEN.
|
|
13524
|
+
* If all instructions are actioned, updates the overallStatus of the instance.
|
|
13525
|
+
*
|
|
13526
|
+
* @param patientId - The ID of the patient.
|
|
13527
|
+
* @param instanceId - The ID of the PatientRequirementInstance.
|
|
13528
|
+
* @param instructionId - The ID of the instruction to complete.
|
|
13529
|
+
* @returns The updated PatientRequirementInstance.
|
|
13530
|
+
* @throws Error if the instance or instruction is not found, or if the instruction is not in a completable state.
|
|
13531
|
+
*/
|
|
13532
|
+
async completeInstruction(patientId, instanceId, instructionId) {
|
|
13533
|
+
const instanceRef = this.getPatientRequirementDocRef(patientId, instanceId);
|
|
13534
|
+
const instanceSnap = await getDoc29(instanceRef);
|
|
13535
|
+
if (!instanceSnap.exists()) {
|
|
13536
|
+
throw new Error(
|
|
13537
|
+
`PatientRequirementInstance ${instanceId} not found for patient ${patientId}.`
|
|
13538
|
+
);
|
|
13539
|
+
}
|
|
13540
|
+
const instanceData = instanceSnap.data();
|
|
13541
|
+
const instance = { id: instanceSnap.id, ...instanceData };
|
|
13542
|
+
const instructionIndex = instance.instructions.findIndex(
|
|
13543
|
+
(instr) => instr.instructionId === instructionId
|
|
13544
|
+
);
|
|
13545
|
+
if (instructionIndex === -1) {
|
|
13546
|
+
throw new Error(
|
|
13547
|
+
`Instruction ${instructionId} not found in instance ${instanceId}.`
|
|
13548
|
+
);
|
|
13549
|
+
}
|
|
13550
|
+
const instructionToUpdate = instance.instructions[instructionIndex];
|
|
13551
|
+
if (instructionToUpdate.status !== "pendingNotification" /* PENDING_NOTIFICATION */ && instructionToUpdate.status !== "actionDue" /* ACTION_DUE */ && instructionToUpdate.status !== "missed" /* MISSED */) {
|
|
13552
|
+
if (instructionToUpdate.status === "actionTaken" /* ACTION_TAKEN */) {
|
|
13553
|
+
console.warn(
|
|
13554
|
+
`Instruction ${instructionId} is already marked as ACTION_TAKEN.`
|
|
13555
|
+
);
|
|
13556
|
+
return instance;
|
|
13557
|
+
}
|
|
13558
|
+
throw new Error(
|
|
13559
|
+
`Instruction ${instructionId} is in status ${instructionToUpdate.status} and cannot be marked as completed.`
|
|
13560
|
+
);
|
|
13561
|
+
}
|
|
13562
|
+
const now = Timestamp29.now();
|
|
13563
|
+
const updatedInstructions = [...instance.instructions];
|
|
13564
|
+
updatedInstructions[instructionIndex] = {
|
|
13565
|
+
...instructionToUpdate,
|
|
13566
|
+
status: "actionTaken" /* ACTION_TAKEN */,
|
|
13567
|
+
actionTakenAt: now,
|
|
13568
|
+
updatedAt: now
|
|
13569
|
+
};
|
|
13570
|
+
const allActionTaken = updatedInstructions.every(
|
|
13571
|
+
(instr) => instr.status === "actionTaken" /* ACTION_TAKEN */
|
|
13572
|
+
);
|
|
13573
|
+
let newOverallStatus = instance.overallStatus;
|
|
13574
|
+
if (allActionTaken) {
|
|
13575
|
+
newOverallStatus = "allInstructionsMet" /* ALL_INSTRUCTIONS_MET */;
|
|
13576
|
+
} else if (updatedInstructions.some(
|
|
13577
|
+
(instr) => instr.status === "actionTaken" /* ACTION_TAKEN */
|
|
13578
|
+
)) {
|
|
13579
|
+
newOverallStatus = "partiallyCompleted" /* PARTIALLY_COMPLETED */;
|
|
13580
|
+
}
|
|
13581
|
+
const updatePayload = {
|
|
13582
|
+
// Using a general type for updateDoc payload
|
|
13583
|
+
instructions: updatedInstructions,
|
|
13584
|
+
updatedAt: now
|
|
13585
|
+
};
|
|
13586
|
+
if (newOverallStatus !== instance.overallStatus) {
|
|
13587
|
+
updatePayload.overallStatus = newOverallStatus;
|
|
13588
|
+
}
|
|
13589
|
+
await updateDoc26(instanceRef, updatePayload);
|
|
13590
|
+
return {
|
|
13591
|
+
...instance,
|
|
13592
|
+
instructions: updatedInstructions,
|
|
13593
|
+
updatedAt: now,
|
|
13594
|
+
overallStatus: newOverallStatus
|
|
13595
|
+
};
|
|
13596
|
+
}
|
|
13597
|
+
// Note: As per the request, full CRUD (create, direct update of instance, delete) is not part of this light service,
|
|
13598
|
+
// as those will be handled by Cloud Functions reacting to appointment lifecycle events.
|
|
13599
|
+
};
|
|
13600
|
+
|
|
13601
|
+
// src/backoffice/services/brand.service.ts
|
|
13602
|
+
import {
|
|
13603
|
+
addDoc as addDoc3,
|
|
13604
|
+
collection as collection27,
|
|
13605
|
+
doc as doc28,
|
|
13606
|
+
getDoc as getDoc30,
|
|
13607
|
+
getDocs as getDocs27,
|
|
13608
|
+
query as query27,
|
|
13609
|
+
updateDoc as updateDoc27,
|
|
13610
|
+
where as where27
|
|
12907
13611
|
} from "firebase/firestore";
|
|
12908
13612
|
|
|
12909
13613
|
// src/backoffice/types/brand.types.ts
|
|
@@ -12915,7 +13619,7 @@ var BrandService = class extends BaseService {
|
|
|
12915
13619
|
* Gets reference to brands collection
|
|
12916
13620
|
*/
|
|
12917
13621
|
getBrandsRef() {
|
|
12918
|
-
return
|
|
13622
|
+
return collection27(this.db, BRANDS_COLLECTION);
|
|
12919
13623
|
}
|
|
12920
13624
|
/**
|
|
12921
13625
|
* Creates a new brand
|
|
@@ -12935,12 +13639,12 @@ var BrandService = class extends BaseService {
|
|
|
12935
13639
|
* Gets all active brands
|
|
12936
13640
|
*/
|
|
12937
13641
|
async getAll() {
|
|
12938
|
-
const q =
|
|
12939
|
-
const snapshot = await
|
|
13642
|
+
const q = query27(this.getBrandsRef(), where27("isActive", "==", true));
|
|
13643
|
+
const snapshot = await getDocs27(q);
|
|
12940
13644
|
return snapshot.docs.map(
|
|
12941
|
-
(
|
|
12942
|
-
id:
|
|
12943
|
-
...
|
|
13645
|
+
(doc33) => ({
|
|
13646
|
+
id: doc33.id,
|
|
13647
|
+
...doc33.data()
|
|
12944
13648
|
})
|
|
12945
13649
|
);
|
|
12946
13650
|
}
|
|
@@ -12952,8 +13656,8 @@ var BrandService = class extends BaseService {
|
|
|
12952
13656
|
...brand,
|
|
12953
13657
|
updatedAt: /* @__PURE__ */ new Date()
|
|
12954
13658
|
};
|
|
12955
|
-
const docRef =
|
|
12956
|
-
await
|
|
13659
|
+
const docRef = doc28(this.getBrandsRef(), brandId);
|
|
13660
|
+
await updateDoc27(docRef, updateData);
|
|
12957
13661
|
return this.getById(brandId);
|
|
12958
13662
|
}
|
|
12959
13663
|
/**
|
|
@@ -12968,8 +13672,8 @@ var BrandService = class extends BaseService {
|
|
|
12968
13672
|
* Gets a brand by ID
|
|
12969
13673
|
*/
|
|
12970
13674
|
async getById(brandId) {
|
|
12971
|
-
const docRef =
|
|
12972
|
-
const docSnap = await
|
|
13675
|
+
const docRef = doc28(this.getBrandsRef(), brandId);
|
|
13676
|
+
const docSnap = await getDoc30(docRef);
|
|
12973
13677
|
if (!docSnap.exists()) return null;
|
|
12974
13678
|
return {
|
|
12975
13679
|
id: docSnap.id,
|
|
@@ -12981,13 +13685,13 @@ var BrandService = class extends BaseService {
|
|
|
12981
13685
|
// src/backoffice/services/category.service.ts
|
|
12982
13686
|
import {
|
|
12983
13687
|
addDoc as addDoc4,
|
|
12984
|
-
collection as
|
|
12985
|
-
doc as
|
|
12986
|
-
getDoc as
|
|
12987
|
-
getDocs as
|
|
12988
|
-
query as
|
|
12989
|
-
updateDoc as
|
|
12990
|
-
where as
|
|
13688
|
+
collection as collection28,
|
|
13689
|
+
doc as doc29,
|
|
13690
|
+
getDoc as getDoc31,
|
|
13691
|
+
getDocs as getDocs28,
|
|
13692
|
+
query as query28,
|
|
13693
|
+
updateDoc as updateDoc28,
|
|
13694
|
+
where as where28
|
|
12991
13695
|
} from "firebase/firestore";
|
|
12992
13696
|
|
|
12993
13697
|
// src/backoffice/types/category.types.ts
|
|
@@ -12999,7 +13703,7 @@ var CategoryService = class extends BaseService {
|
|
|
12999
13703
|
* Referenca na Firestore kolekciju kategorija
|
|
13000
13704
|
*/
|
|
13001
13705
|
get categoriesRef() {
|
|
13002
|
-
return
|
|
13706
|
+
return collection28(this.db, CATEGORIES_COLLECTION);
|
|
13003
13707
|
}
|
|
13004
13708
|
/**
|
|
13005
13709
|
* Kreira novu kategoriju u sistemu
|
|
@@ -13022,12 +13726,12 @@ var CategoryService = class extends BaseService {
|
|
|
13022
13726
|
* @returns Lista aktivnih kategorija
|
|
13023
13727
|
*/
|
|
13024
13728
|
async getAll() {
|
|
13025
|
-
const q =
|
|
13026
|
-
const snapshot = await
|
|
13729
|
+
const q = query28(this.categoriesRef, where28("isActive", "==", true));
|
|
13730
|
+
const snapshot = await getDocs28(q);
|
|
13027
13731
|
return snapshot.docs.map(
|
|
13028
|
-
(
|
|
13029
|
-
id:
|
|
13030
|
-
...
|
|
13732
|
+
(doc33) => ({
|
|
13733
|
+
id: doc33.id,
|
|
13734
|
+
...doc33.data()
|
|
13031
13735
|
})
|
|
13032
13736
|
);
|
|
13033
13737
|
}
|
|
@@ -13037,16 +13741,16 @@ var CategoryService = class extends BaseService {
|
|
|
13037
13741
|
* @returns Lista kategorija koje pripadaju traženoj familiji
|
|
13038
13742
|
*/
|
|
13039
13743
|
async getAllByFamily(family) {
|
|
13040
|
-
const q =
|
|
13744
|
+
const q = query28(
|
|
13041
13745
|
this.categoriesRef,
|
|
13042
|
-
|
|
13043
|
-
|
|
13746
|
+
where28("family", "==", family),
|
|
13747
|
+
where28("isActive", "==", true)
|
|
13044
13748
|
);
|
|
13045
|
-
const snapshot = await
|
|
13749
|
+
const snapshot = await getDocs28(q);
|
|
13046
13750
|
return snapshot.docs.map(
|
|
13047
|
-
(
|
|
13048
|
-
id:
|
|
13049
|
-
...
|
|
13751
|
+
(doc33) => ({
|
|
13752
|
+
id: doc33.id,
|
|
13753
|
+
...doc33.data()
|
|
13050
13754
|
})
|
|
13051
13755
|
);
|
|
13052
13756
|
}
|
|
@@ -13061,8 +13765,8 @@ var CategoryService = class extends BaseService {
|
|
|
13061
13765
|
...category,
|
|
13062
13766
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13063
13767
|
};
|
|
13064
|
-
const docRef =
|
|
13065
|
-
await
|
|
13768
|
+
const docRef = doc29(this.categoriesRef, id);
|
|
13769
|
+
await updateDoc28(docRef, updateData);
|
|
13066
13770
|
return this.getById(id);
|
|
13067
13771
|
}
|
|
13068
13772
|
/**
|
|
@@ -13078,8 +13782,8 @@ var CategoryService = class extends BaseService {
|
|
|
13078
13782
|
* @returns Kategorija ili null ako ne postoji
|
|
13079
13783
|
*/
|
|
13080
13784
|
async getById(id) {
|
|
13081
|
-
const docRef =
|
|
13082
|
-
const docSnap = await
|
|
13785
|
+
const docRef = doc29(this.categoriesRef, id);
|
|
13786
|
+
const docSnap = await getDoc31(docRef);
|
|
13083
13787
|
if (!docSnap.exists()) return null;
|
|
13084
13788
|
return {
|
|
13085
13789
|
id: docSnap.id,
|
|
@@ -13091,13 +13795,13 @@ var CategoryService = class extends BaseService {
|
|
|
13091
13795
|
// src/backoffice/services/subcategory.service.ts
|
|
13092
13796
|
import {
|
|
13093
13797
|
addDoc as addDoc5,
|
|
13094
|
-
collection as
|
|
13095
|
-
doc as
|
|
13096
|
-
getDoc as
|
|
13097
|
-
getDocs as
|
|
13098
|
-
query as
|
|
13099
|
-
updateDoc as
|
|
13100
|
-
where as
|
|
13798
|
+
collection as collection29,
|
|
13799
|
+
doc as doc30,
|
|
13800
|
+
getDoc as getDoc32,
|
|
13801
|
+
getDocs as getDocs29,
|
|
13802
|
+
query as query29,
|
|
13803
|
+
updateDoc as updateDoc29,
|
|
13804
|
+
where as where29
|
|
13101
13805
|
} from "firebase/firestore";
|
|
13102
13806
|
|
|
13103
13807
|
// src/backoffice/types/subcategory.types.ts
|
|
@@ -13110,7 +13814,7 @@ var SubcategoryService = class extends BaseService {
|
|
|
13110
13814
|
* @param categoryId - ID roditeljske kategorije
|
|
13111
13815
|
*/
|
|
13112
13816
|
getSubcategoriesRef(categoryId) {
|
|
13113
|
-
return
|
|
13817
|
+
return collection29(
|
|
13114
13818
|
this.db,
|
|
13115
13819
|
CATEGORIES_COLLECTION,
|
|
13116
13820
|
categoryId,
|
|
@@ -13144,15 +13848,15 @@ var SubcategoryService = class extends BaseService {
|
|
|
13144
13848
|
* @returns Lista aktivnih podkategorija
|
|
13145
13849
|
*/
|
|
13146
13850
|
async getAllByCategoryId(categoryId) {
|
|
13147
|
-
const q =
|
|
13851
|
+
const q = query29(
|
|
13148
13852
|
this.getSubcategoriesRef(categoryId),
|
|
13149
|
-
|
|
13853
|
+
where29("isActive", "==", true)
|
|
13150
13854
|
);
|
|
13151
|
-
const snapshot = await
|
|
13855
|
+
const snapshot = await getDocs29(q);
|
|
13152
13856
|
return snapshot.docs.map(
|
|
13153
|
-
(
|
|
13154
|
-
id:
|
|
13155
|
-
...
|
|
13857
|
+
(doc33) => ({
|
|
13858
|
+
id: doc33.id,
|
|
13859
|
+
...doc33.data()
|
|
13156
13860
|
})
|
|
13157
13861
|
);
|
|
13158
13862
|
}
|
|
@@ -13168,8 +13872,8 @@ var SubcategoryService = class extends BaseService {
|
|
|
13168
13872
|
...subcategory,
|
|
13169
13873
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13170
13874
|
};
|
|
13171
|
-
const docRef =
|
|
13172
|
-
await
|
|
13875
|
+
const docRef = doc30(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
13876
|
+
await updateDoc29(docRef, updateData);
|
|
13173
13877
|
return this.getById(categoryId, subcategoryId);
|
|
13174
13878
|
}
|
|
13175
13879
|
/**
|
|
@@ -13187,8 +13891,8 @@ var SubcategoryService = class extends BaseService {
|
|
|
13187
13891
|
* @returns Podkategorija ili null ako ne postoji
|
|
13188
13892
|
*/
|
|
13189
13893
|
async getById(categoryId, subcategoryId) {
|
|
13190
|
-
const docRef =
|
|
13191
|
-
const docSnap = await
|
|
13894
|
+
const docRef = doc30(this.getSubcategoriesRef(categoryId), subcategoryId);
|
|
13895
|
+
const docSnap = await getDoc32(docRef);
|
|
13192
13896
|
if (!docSnap.exists()) return null;
|
|
13193
13897
|
return {
|
|
13194
13898
|
id: docSnap.id,
|
|
@@ -13200,15 +13904,15 @@ var SubcategoryService = class extends BaseService {
|
|
|
13200
13904
|
// src/backoffice/services/technology.service.ts
|
|
13201
13905
|
import {
|
|
13202
13906
|
addDoc as addDoc6,
|
|
13203
|
-
collection as
|
|
13204
|
-
doc as
|
|
13205
|
-
getDoc as
|
|
13206
|
-
getDocs as
|
|
13207
|
-
query as
|
|
13208
|
-
updateDoc as
|
|
13209
|
-
where as
|
|
13210
|
-
arrayUnion as
|
|
13211
|
-
arrayRemove as
|
|
13907
|
+
collection as collection30,
|
|
13908
|
+
doc as doc31,
|
|
13909
|
+
getDoc as getDoc33,
|
|
13910
|
+
getDocs as getDocs30,
|
|
13911
|
+
query as query30,
|
|
13912
|
+
updateDoc as updateDoc30,
|
|
13913
|
+
where as where30,
|
|
13914
|
+
arrayUnion as arrayUnion9,
|
|
13915
|
+
arrayRemove as arrayRemove8
|
|
13212
13916
|
} from "firebase/firestore";
|
|
13213
13917
|
var DEFAULT_CERTIFICATION_REQUIREMENT = {
|
|
13214
13918
|
minimumLevel: "aesthetician" /* AESTHETICIAN */,
|
|
@@ -13219,7 +13923,7 @@ var TechnologyService = class extends BaseService {
|
|
|
13219
13923
|
* Vraća referencu na Firestore kolekciju tehnologija
|
|
13220
13924
|
*/
|
|
13221
13925
|
getTechnologiesRef() {
|
|
13222
|
-
return
|
|
13926
|
+
return collection30(this.db, TECHNOLOGIES_COLLECTION);
|
|
13223
13927
|
}
|
|
13224
13928
|
/**
|
|
13225
13929
|
* Kreira novu tehnologiju
|
|
@@ -13250,12 +13954,12 @@ var TechnologyService = class extends BaseService {
|
|
|
13250
13954
|
* @returns Lista aktivnih tehnologija
|
|
13251
13955
|
*/
|
|
13252
13956
|
async getAll() {
|
|
13253
|
-
const q =
|
|
13254
|
-
const snapshot = await
|
|
13957
|
+
const q = query30(this.getTechnologiesRef(), where30("isActive", "==", true));
|
|
13958
|
+
const snapshot = await getDocs30(q);
|
|
13255
13959
|
return snapshot.docs.map(
|
|
13256
|
-
(
|
|
13257
|
-
id:
|
|
13258
|
-
...
|
|
13960
|
+
(doc33) => ({
|
|
13961
|
+
id: doc33.id,
|
|
13962
|
+
...doc33.data()
|
|
13259
13963
|
})
|
|
13260
13964
|
);
|
|
13261
13965
|
}
|
|
@@ -13265,16 +13969,16 @@ var TechnologyService = class extends BaseService {
|
|
|
13265
13969
|
* @returns Lista aktivnih tehnologija
|
|
13266
13970
|
*/
|
|
13267
13971
|
async getAllByFamily(family) {
|
|
13268
|
-
const q =
|
|
13972
|
+
const q = query30(
|
|
13269
13973
|
this.getTechnologiesRef(),
|
|
13270
|
-
|
|
13271
|
-
|
|
13974
|
+
where30("isActive", "==", true),
|
|
13975
|
+
where30("family", "==", family)
|
|
13272
13976
|
);
|
|
13273
|
-
const snapshot = await
|
|
13977
|
+
const snapshot = await getDocs30(q);
|
|
13274
13978
|
return snapshot.docs.map(
|
|
13275
|
-
(
|
|
13276
|
-
id:
|
|
13277
|
-
...
|
|
13979
|
+
(doc33) => ({
|
|
13980
|
+
id: doc33.id,
|
|
13981
|
+
...doc33.data()
|
|
13278
13982
|
})
|
|
13279
13983
|
);
|
|
13280
13984
|
}
|
|
@@ -13284,16 +13988,16 @@ var TechnologyService = class extends BaseService {
|
|
|
13284
13988
|
* @returns Lista aktivnih tehnologija
|
|
13285
13989
|
*/
|
|
13286
13990
|
async getAllByCategoryId(categoryId) {
|
|
13287
|
-
const q =
|
|
13991
|
+
const q = query30(
|
|
13288
13992
|
this.getTechnologiesRef(),
|
|
13289
|
-
|
|
13290
|
-
|
|
13993
|
+
where30("isActive", "==", true),
|
|
13994
|
+
where30("categoryId", "==", categoryId)
|
|
13291
13995
|
);
|
|
13292
|
-
const snapshot = await
|
|
13996
|
+
const snapshot = await getDocs30(q);
|
|
13293
13997
|
return snapshot.docs.map(
|
|
13294
|
-
(
|
|
13295
|
-
id:
|
|
13296
|
-
...
|
|
13998
|
+
(doc33) => ({
|
|
13999
|
+
id: doc33.id,
|
|
14000
|
+
...doc33.data()
|
|
13297
14001
|
})
|
|
13298
14002
|
);
|
|
13299
14003
|
}
|
|
@@ -13303,16 +14007,16 @@ var TechnologyService = class extends BaseService {
|
|
|
13303
14007
|
* @returns Lista aktivnih tehnologija
|
|
13304
14008
|
*/
|
|
13305
14009
|
async getAllBySubcategoryId(subcategoryId) {
|
|
13306
|
-
const q =
|
|
14010
|
+
const q = query30(
|
|
13307
14011
|
this.getTechnologiesRef(),
|
|
13308
|
-
|
|
13309
|
-
|
|
14012
|
+
where30("isActive", "==", true),
|
|
14013
|
+
where30("subcategoryId", "==", subcategoryId)
|
|
13310
14014
|
);
|
|
13311
|
-
const snapshot = await
|
|
14015
|
+
const snapshot = await getDocs30(q);
|
|
13312
14016
|
return snapshot.docs.map(
|
|
13313
|
-
(
|
|
13314
|
-
id:
|
|
13315
|
-
...
|
|
14017
|
+
(doc33) => ({
|
|
14018
|
+
id: doc33.id,
|
|
14019
|
+
...doc33.data()
|
|
13316
14020
|
})
|
|
13317
14021
|
);
|
|
13318
14022
|
}
|
|
@@ -13327,8 +14031,8 @@ var TechnologyService = class extends BaseService {
|
|
|
13327
14031
|
...technology,
|
|
13328
14032
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13329
14033
|
};
|
|
13330
|
-
const docRef =
|
|
13331
|
-
await
|
|
14034
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14035
|
+
await updateDoc30(docRef, updateData);
|
|
13332
14036
|
return this.getById(technologyId);
|
|
13333
14037
|
}
|
|
13334
14038
|
/**
|
|
@@ -13346,8 +14050,8 @@ var TechnologyService = class extends BaseService {
|
|
|
13346
14050
|
* @returns Tehnologija ili null ako ne postoji
|
|
13347
14051
|
*/
|
|
13348
14052
|
async getById(technologyId) {
|
|
13349
|
-
const docRef =
|
|
13350
|
-
const docSnap = await
|
|
14053
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14054
|
+
const docSnap = await getDoc33(docRef);
|
|
13351
14055
|
if (!docSnap.exists()) return null;
|
|
13352
14056
|
return {
|
|
13353
14057
|
id: docSnap.id,
|
|
@@ -13361,10 +14065,10 @@ var TechnologyService = class extends BaseService {
|
|
|
13361
14065
|
* @returns Ažurirana tehnologija sa novim zahtevom
|
|
13362
14066
|
*/
|
|
13363
14067
|
async addRequirement(technologyId, requirement) {
|
|
13364
|
-
const docRef =
|
|
14068
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
13365
14069
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
13366
|
-
await
|
|
13367
|
-
[requirementType]:
|
|
14070
|
+
await updateDoc30(docRef, {
|
|
14071
|
+
[requirementType]: arrayUnion9(requirement),
|
|
13368
14072
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13369
14073
|
});
|
|
13370
14074
|
return this.getById(technologyId);
|
|
@@ -13376,10 +14080,10 @@ var TechnologyService = class extends BaseService {
|
|
|
13376
14080
|
* @returns Ažurirana tehnologija bez uklonjenog zahteva
|
|
13377
14081
|
*/
|
|
13378
14082
|
async removeRequirement(technologyId, requirement) {
|
|
13379
|
-
const docRef =
|
|
14083
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
13380
14084
|
const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
|
|
13381
|
-
await
|
|
13382
|
-
[requirementType]:
|
|
14085
|
+
await updateDoc30(docRef, {
|
|
14086
|
+
[requirementType]: arrayRemove8(requirement),
|
|
13383
14087
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13384
14088
|
});
|
|
13385
14089
|
return this.getById(technologyId);
|
|
@@ -13416,9 +14120,9 @@ var TechnologyService = class extends BaseService {
|
|
|
13416
14120
|
* @returns Ažurirana tehnologija
|
|
13417
14121
|
*/
|
|
13418
14122
|
async addBlockingCondition(technologyId, condition) {
|
|
13419
|
-
const docRef =
|
|
13420
|
-
await
|
|
13421
|
-
blockingConditions:
|
|
14123
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14124
|
+
await updateDoc30(docRef, {
|
|
14125
|
+
blockingConditions: arrayUnion9(condition),
|
|
13422
14126
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13423
14127
|
});
|
|
13424
14128
|
return this.getById(technologyId);
|
|
@@ -13430,9 +14134,9 @@ var TechnologyService = class extends BaseService {
|
|
|
13430
14134
|
* @returns Ažurirana tehnologija
|
|
13431
14135
|
*/
|
|
13432
14136
|
async removeBlockingCondition(technologyId, condition) {
|
|
13433
|
-
const docRef =
|
|
13434
|
-
await
|
|
13435
|
-
blockingConditions:
|
|
14137
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14138
|
+
await updateDoc30(docRef, {
|
|
14139
|
+
blockingConditions: arrayRemove8(condition),
|
|
13436
14140
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13437
14141
|
});
|
|
13438
14142
|
return this.getById(technologyId);
|
|
@@ -13444,9 +14148,9 @@ var TechnologyService = class extends BaseService {
|
|
|
13444
14148
|
* @returns Ažurirana tehnologija
|
|
13445
14149
|
*/
|
|
13446
14150
|
async addContraindication(technologyId, contraindication) {
|
|
13447
|
-
const docRef =
|
|
13448
|
-
await
|
|
13449
|
-
contraindications:
|
|
14151
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14152
|
+
await updateDoc30(docRef, {
|
|
14153
|
+
contraindications: arrayUnion9(contraindication),
|
|
13450
14154
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13451
14155
|
});
|
|
13452
14156
|
return this.getById(technologyId);
|
|
@@ -13458,9 +14162,9 @@ var TechnologyService = class extends BaseService {
|
|
|
13458
14162
|
* @returns Ažurirana tehnologija
|
|
13459
14163
|
*/
|
|
13460
14164
|
async removeContraindication(technologyId, contraindication) {
|
|
13461
|
-
const docRef =
|
|
13462
|
-
await
|
|
13463
|
-
contraindications:
|
|
14165
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14166
|
+
await updateDoc30(docRef, {
|
|
14167
|
+
contraindications: arrayRemove8(contraindication),
|
|
13464
14168
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13465
14169
|
});
|
|
13466
14170
|
return this.getById(technologyId);
|
|
@@ -13472,9 +14176,9 @@ var TechnologyService = class extends BaseService {
|
|
|
13472
14176
|
* @returns Ažurirana tehnologija
|
|
13473
14177
|
*/
|
|
13474
14178
|
async addBenefit(technologyId, benefit) {
|
|
13475
|
-
const docRef =
|
|
13476
|
-
await
|
|
13477
|
-
benefits:
|
|
14179
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14180
|
+
await updateDoc30(docRef, {
|
|
14181
|
+
benefits: arrayUnion9(benefit),
|
|
13478
14182
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13479
14183
|
});
|
|
13480
14184
|
return this.getById(technologyId);
|
|
@@ -13486,9 +14190,9 @@ var TechnologyService = class extends BaseService {
|
|
|
13486
14190
|
* @returns Ažurirana tehnologija
|
|
13487
14191
|
*/
|
|
13488
14192
|
async removeBenefit(technologyId, benefit) {
|
|
13489
|
-
const docRef =
|
|
13490
|
-
await
|
|
13491
|
-
benefits:
|
|
14193
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14194
|
+
await updateDoc30(docRef, {
|
|
14195
|
+
benefits: arrayRemove8(benefit),
|
|
13492
14196
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13493
14197
|
});
|
|
13494
14198
|
return this.getById(technologyId);
|
|
@@ -13527,8 +14231,8 @@ var TechnologyService = class extends BaseService {
|
|
|
13527
14231
|
* @returns Ažurirana tehnologija
|
|
13528
14232
|
*/
|
|
13529
14233
|
async updateCertificationRequirement(technologyId, certificationRequirement) {
|
|
13530
|
-
const docRef =
|
|
13531
|
-
await
|
|
14234
|
+
const docRef = doc31(this.getTechnologiesRef(), technologyId);
|
|
14235
|
+
await updateDoc30(docRef, {
|
|
13532
14236
|
certificationRequirement,
|
|
13533
14237
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13534
14238
|
});
|
|
@@ -13630,13 +14334,13 @@ var TechnologyService = class extends BaseService {
|
|
|
13630
14334
|
// src/backoffice/services/product.service.ts
|
|
13631
14335
|
import {
|
|
13632
14336
|
addDoc as addDoc7,
|
|
13633
|
-
collection as
|
|
13634
|
-
doc as
|
|
13635
|
-
getDoc as
|
|
13636
|
-
getDocs as
|
|
13637
|
-
query as
|
|
13638
|
-
updateDoc as
|
|
13639
|
-
where as
|
|
14337
|
+
collection as collection31,
|
|
14338
|
+
doc as doc32,
|
|
14339
|
+
getDoc as getDoc34,
|
|
14340
|
+
getDocs as getDocs31,
|
|
14341
|
+
query as query31,
|
|
14342
|
+
updateDoc as updateDoc31,
|
|
14343
|
+
where as where31
|
|
13640
14344
|
} from "firebase/firestore";
|
|
13641
14345
|
|
|
13642
14346
|
// src/backoffice/types/product.types.ts
|
|
@@ -13650,7 +14354,7 @@ var ProductService = class extends BaseService {
|
|
|
13650
14354
|
* @returns Firestore collection reference
|
|
13651
14355
|
*/
|
|
13652
14356
|
getProductsRef(technologyId) {
|
|
13653
|
-
return
|
|
14357
|
+
return collection31(
|
|
13654
14358
|
this.db,
|
|
13655
14359
|
TECHNOLOGIES_COLLECTION,
|
|
13656
14360
|
technologyId,
|
|
@@ -13680,15 +14384,15 @@ var ProductService = class extends BaseService {
|
|
|
13680
14384
|
* Gets all products for a technology
|
|
13681
14385
|
*/
|
|
13682
14386
|
async getAllByTechnology(technologyId) {
|
|
13683
|
-
const q =
|
|
14387
|
+
const q = query31(
|
|
13684
14388
|
this.getProductsRef(technologyId),
|
|
13685
|
-
|
|
14389
|
+
where31("isActive", "==", true)
|
|
13686
14390
|
);
|
|
13687
|
-
const snapshot = await
|
|
14391
|
+
const snapshot = await getDocs31(q);
|
|
13688
14392
|
return snapshot.docs.map(
|
|
13689
|
-
(
|
|
13690
|
-
id:
|
|
13691
|
-
...
|
|
14393
|
+
(doc33) => ({
|
|
14394
|
+
id: doc33.id,
|
|
14395
|
+
...doc33.data()
|
|
13692
14396
|
})
|
|
13693
14397
|
);
|
|
13694
14398
|
}
|
|
@@ -13696,21 +14400,21 @@ var ProductService = class extends BaseService {
|
|
|
13696
14400
|
* Gets all products for a brand by filtering through all technologies
|
|
13697
14401
|
*/
|
|
13698
14402
|
async getAllByBrand(brandId) {
|
|
13699
|
-
const allTechnologiesRef =
|
|
13700
|
-
const technologiesSnapshot = await
|
|
14403
|
+
const allTechnologiesRef = collection31(this.db, TECHNOLOGIES_COLLECTION);
|
|
14404
|
+
const technologiesSnapshot = await getDocs31(allTechnologiesRef);
|
|
13701
14405
|
const products = [];
|
|
13702
14406
|
for (const techDoc of technologiesSnapshot.docs) {
|
|
13703
|
-
const q =
|
|
14407
|
+
const q = query31(
|
|
13704
14408
|
this.getProductsRef(techDoc.id),
|
|
13705
|
-
|
|
13706
|
-
|
|
14409
|
+
where31("brandId", "==", brandId),
|
|
14410
|
+
where31("isActive", "==", true)
|
|
13707
14411
|
);
|
|
13708
|
-
const snapshot = await
|
|
14412
|
+
const snapshot = await getDocs31(q);
|
|
13709
14413
|
products.push(
|
|
13710
14414
|
...snapshot.docs.map(
|
|
13711
|
-
(
|
|
13712
|
-
id:
|
|
13713
|
-
...
|
|
14415
|
+
(doc33) => ({
|
|
14416
|
+
id: doc33.id,
|
|
14417
|
+
...doc33.data()
|
|
13714
14418
|
})
|
|
13715
14419
|
)
|
|
13716
14420
|
);
|
|
@@ -13725,8 +14429,8 @@ var ProductService = class extends BaseService {
|
|
|
13725
14429
|
...product,
|
|
13726
14430
|
updatedAt: /* @__PURE__ */ new Date()
|
|
13727
14431
|
};
|
|
13728
|
-
const docRef =
|
|
13729
|
-
await
|
|
14432
|
+
const docRef = doc32(this.getProductsRef(technologyId), productId);
|
|
14433
|
+
await updateDoc31(docRef, updateData);
|
|
13730
14434
|
return this.getById(technologyId, productId);
|
|
13731
14435
|
}
|
|
13732
14436
|
/**
|
|
@@ -13741,8 +14445,8 @@ var ProductService = class extends BaseService {
|
|
|
13741
14445
|
* Gets a product by ID
|
|
13742
14446
|
*/
|
|
13743
14447
|
async getById(technologyId, productId) {
|
|
13744
|
-
const docRef =
|
|
13745
|
-
const docSnap = await
|
|
14448
|
+
const docRef = doc32(this.getProductsRef(technologyId), productId);
|
|
14449
|
+
const docSnap = await getDoc34(docRef);
|
|
13746
14450
|
if (!docSnap.exists()) return null;
|
|
13747
14451
|
return {
|
|
13748
14452
|
id: docSnap.id,
|
|
@@ -13771,14 +14475,14 @@ var baseNotificationSchema = z24.object({
|
|
|
13771
14475
|
userRole: z24.nativeEnum(UserRole)
|
|
13772
14476
|
});
|
|
13773
14477
|
var preRequirementNotificationSchema = baseNotificationSchema.extend({
|
|
13774
|
-
notificationType: z24.literal("
|
|
14478
|
+
notificationType: z24.literal("requirementInstructionDue" /* REQUIREMENT_INSTRUCTION_DUE */),
|
|
13775
14479
|
treatmentId: z24.string(),
|
|
13776
14480
|
requirements: z24.array(z24.string()),
|
|
13777
14481
|
deadline: z24.any()
|
|
13778
14482
|
// Timestamp
|
|
13779
14483
|
});
|
|
13780
14484
|
var postRequirementNotificationSchema = baseNotificationSchema.extend({
|
|
13781
|
-
notificationType: z24.literal("
|
|
14485
|
+
notificationType: z24.literal("requirementInstructionDue" /* REQUIREMENT_INSTRUCTION_DUE */),
|
|
13782
14486
|
treatmentId: z24.string(),
|
|
13783
14487
|
requirements: z24.array(z24.string()),
|
|
13784
14488
|
deadline: z24.any()
|
|
@@ -13793,7 +14497,7 @@ var appointmentReminderNotificationSchema = baseNotificationSchema.extend({
|
|
|
13793
14497
|
doctorName: z24.string()
|
|
13794
14498
|
});
|
|
13795
14499
|
var appointmentNotificationSchema = baseNotificationSchema.extend({
|
|
13796
|
-
notificationType: z24.literal("
|
|
14500
|
+
notificationType: z24.literal("appointmentStatusChange" /* APPOINTMENT_STATUS_CHANGE */),
|
|
13797
14501
|
appointmentId: z24.string(),
|
|
13798
14502
|
appointmentStatus: z24.string(),
|
|
13799
14503
|
previousStatus: z24.string(),
|
|
@@ -13889,9 +14593,13 @@ export {
|
|
|
13889
14593
|
PATIENT_LOCATION_INFO_COLLECTION,
|
|
13890
14594
|
PATIENT_MEDICAL_HISTORY_COLLECTION,
|
|
13891
14595
|
PATIENT_MEDICAL_INFO_COLLECTION,
|
|
14596
|
+
PATIENT_REQUIREMENTS_SUBCOLLECTION_NAME,
|
|
13892
14597
|
PATIENT_SENSITIVE_INFO_COLLECTION,
|
|
13893
14598
|
PRACTITIONERS_COLLECTION,
|
|
13894
14599
|
PROCEDURES_COLLECTION,
|
|
14600
|
+
PatientInstructionStatus,
|
|
14601
|
+
PatientRequirementOverallStatus,
|
|
14602
|
+
PatientRequirementsService,
|
|
13895
14603
|
PatientService,
|
|
13896
14604
|
PaymentStatus,
|
|
13897
14605
|
PracticeType,
|
|
@@ -13956,6 +14664,7 @@ export {
|
|
|
13956
14664
|
createDefaultClinicGroupSchema,
|
|
13957
14665
|
createDocumentTemplateSchema,
|
|
13958
14666
|
createDraftPractitionerSchema,
|
|
14667
|
+
createFilledDocumentDataSchema,
|
|
13959
14668
|
createPatientLocationInfoSchema,
|
|
13960
14669
|
createPatientMedicalInfoSchema,
|
|
13961
14670
|
createPatientProfileSchema,
|
|
@@ -13971,6 +14680,8 @@ export {
|
|
|
13971
14680
|
documentTemplateSchema,
|
|
13972
14681
|
emailSchema,
|
|
13973
14682
|
emergencyContactSchema,
|
|
14683
|
+
filledDocumentSchema,
|
|
14684
|
+
filledDocumentStatusSchema,
|
|
13974
14685
|
gamificationSchema,
|
|
13975
14686
|
getFirebaseApp,
|
|
13976
14687
|
getFirebaseAuth,
|
|
@@ -14020,6 +14731,7 @@ export {
|
|
|
14020
14731
|
updateClinicSchema,
|
|
14021
14732
|
updateContraindicationSchema,
|
|
14022
14733
|
updateDocumentTemplateSchema,
|
|
14734
|
+
updateFilledDocumentDataSchema,
|
|
14023
14735
|
updateMedicationSchema,
|
|
14024
14736
|
updatePatientMedicalInfoSchema,
|
|
14025
14737
|
updateVitalStatsSchema,
|