@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.
Files changed (37) hide show
  1. package/dist/admin/index.d.mts +228 -25
  2. package/dist/admin/index.d.ts +228 -25
  3. package/dist/admin/index.js +35867 -2493
  4. package/dist/admin/index.mjs +35856 -2464
  5. package/dist/backoffice/index.d.mts +252 -1
  6. package/dist/backoffice/index.d.ts +252 -1
  7. package/dist/backoffice/index.js +86 -12
  8. package/dist/backoffice/index.mjs +86 -13
  9. package/dist/index.d.mts +1417 -554
  10. package/dist/index.d.ts +1417 -554
  11. package/dist/index.js +1393 -687
  12. package/dist/index.mjs +1423 -711
  13. package/package.json +1 -1
  14. package/src/admin/index.ts +15 -1
  15. package/src/admin/notifications/notifications.admin.ts +1 -1
  16. package/src/admin/requirements/README.md +128 -0
  17. package/src/admin/requirements/patient-requirements.admin.service.ts +482 -0
  18. package/src/index.ts +16 -1
  19. package/src/services/appointment/appointment.service.ts +315 -86
  20. package/src/services/clinic/clinic-admin.service.ts +3 -0
  21. package/src/services/clinic/clinic-group.service.ts +8 -0
  22. package/src/services/documentation-templates/documentation-template.service.ts +24 -16
  23. package/src/services/documentation-templates/filled-document.service.ts +253 -136
  24. package/src/services/patient/patient.service.ts +31 -1
  25. package/src/services/patient/patientRequirements.service.ts +285 -0
  26. package/src/services/patient/utils/practitioner.utils.ts +79 -1
  27. package/src/types/appointment/index.ts +134 -10
  28. package/src/types/documentation-templates/index.ts +34 -2
  29. package/src/types/notifications/README.md +77 -0
  30. package/src/types/notifications/index.ts +154 -27
  31. package/src/types/patient/index.ts +6 -0
  32. package/src/types/patient/patient-requirements.ts +81 -0
  33. package/src/validations/appointment.schema.ts +300 -62
  34. package/src/validations/documentation-templates/template.schema.ts +55 -0
  35. package/src/validations/documentation-templates.schema.ts +9 -14
  36. package/src/validations/notification.schema.ts +3 -3
  37. 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["SCHEDULED"] = "scheduled";
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["RESCHEDULED"] = "rescheduled";
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 createAppointmentSchema = z.object({
29
- calendarEventId: z.string().min(1, "Calendar event ID is required"),
30
- clinicBranchId: z.string().min(1, "Clinic branch ID is required"),
31
- practitionerId: z.string().min(1, "Practitioner ID is required"),
32
- patientId: z.string().min(1, "Patient ID is required"),
33
- procedureId: z.string().min(1, "Procedure ID is required"),
34
- appointmentStartTime: z.any().refine(
35
- (val) => val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0,
36
- "Appointment start time must be a valid timestamp"
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: z.any().refine(
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: z.number().min(0, "Cost must be a non-negative number"),
43
- currency: z.string().min(1, "Currency is required"),
44
- patientNotes: z.string().nullable().optional(),
45
- initialStatus: z.nativeEnum(AppointmentStatus, {
46
- errorMap: () => ({ message: "Invalid appointment status" })
47
- }),
48
- initialPaymentStatus: z.nativeEnum(PaymentStatus, {
49
- errorMap: () => ({ message: "Invalid payment status" })
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 = z.object({
53
- status: z.nativeEnum(AppointmentStatus, {
54
- errorMap: () => ({ message: "Invalid appointment status" })
55
- }).optional(),
56
- confirmationTime: z.any().refine(
57
- (val) => val === null || val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0,
58
- "Confirmation time must be a valid timestamp or null"
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
- actualDurationMinutes: z.number().positive("Duration must be positive").optional(),
61
- cancellationReason: z.string().nullable().optional(),
62
- canceledBy: z.enum(["patient", "clinic", "practitioner", "system"]).optional(),
63
- internalNotes: z.string().nullable().optional(),
64
- paymentStatus: z.nativeEnum(PaymentStatus, {
65
- errorMap: () => ({ message: "Invalid payment status" })
66
- }).optional(),
67
- paymentTransactionId: z.string().nullable().optional(),
68
- completedPreRequirements: z.array(z.string()).optional(),
69
- completedPostRequirements: z.array(z.string()).optional()
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 = z.object({
83
- patientId: z.string().optional(),
84
- practitionerId: z.string().optional(),
85
- clinicBranchId: z.string().optional(),
86
- startDate: z.date().optional(),
87
- endDate: z.date().optional(),
88
- status: z.union([
89
- z.nativeEnum(AppointmentStatus),
90
- z.array(z.nativeEnum(AppointmentStatus))
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: z.number().positive().optional(),
93
- startAfter: z.any().optional()
441
+ limit: z2.number().positive().int().optional().default(20),
442
+ startAfter: z2.any().optional()
94
443
  }).refine(
95
444
  (data) => {
96
- return !!(data.patientId || data.practitionerId || data.clinicBranchId);
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: "At least one of patientId, practitionerId, or clinicBranchId must be provided",
100
- path: ["searchCriteria"]
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1456
- if (!doc32.exists()) throw new Error("Medical info not found");
1457
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1474
- if (!doc32.exists()) throw new Error("Medical info not found");
1475
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1501
- if (!doc32.exists()) throw new Error("Medical info not found");
1502
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1519
- if (!doc32.exists()) throw new Error("Medical info not found");
1520
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1546
- if (!doc32.exists()) throw new Error("Medical info not found");
1547
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1564
- if (!doc32.exists()) throw new Error("Medical info not found");
1565
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1591
- if (!doc32.exists()) throw new Error("Medical info not found");
1592
- const medicalInfo = doc32.data();
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 doc32 = await getDoc3(getMedicalInfoDocRef(db, patientId));
1609
- if (!doc32.exists()) throw new Error("Medical info not found");
1610
- const medicalInfo = doc32.data();
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((doc32) => doc32.data());
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((doc32) => {
1913
- patients.push(doc32.data());
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((doc32) => {
2147
- patients.push(doc32.data());
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((doc32) => {
2200
- patients.push(doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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
- (doc32) => doc32.data()
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((doc32) => {
4381
- return { ...doc32.data(), id: doc32.id };
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => {
5679
- const data = doc32.data();
5934
+ const clinics = clinicsSnapshot.docs.map((doc33) => {
5935
+ const data = doc33.data();
5680
5936
  return {
5681
5937
  ...data,
5682
- id: doc32.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 doc32 of querySnapshot.docs) {
5710
- const clinic = doc32.data();
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 doc32 of querySnapshot.docs) {
5833
- const clinic = doc32.data();
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 doc32 of querySnapshot.docs) {
5930
- const clinic = { ...doc32.data(), id: doc32.id };
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((doc32) => {
5987
- return { ...doc32.data(), id: doc32.id };
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["APPOINTMENT_NOTIFICATION"] = "appointmentNotification";
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((doc32) => ({
7279
- id: doc32.id,
7280
- ...doc32.data()
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((doc32) => ({
7295
- id: doc32.id,
7296
- ...doc32.data()
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((doc32) => ({
7369
- id: doc32.id,
7370
- ...doc32.data()
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((doc32) => ({
7384
- id: doc32.id,
7385
- ...doc32.data()
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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: limit11, startAfter: startAfter11 } = await import("firebase/firestore");
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
- startAfter11(lastDoc),
7820
- limit11(pagination)
8086
+ startAfter12(lastDoc),
8087
+ limit12(pagination)
7821
8088
  );
7822
8089
  } else {
7823
8090
  proceduresQuery = query15(
7824
8091
  proceduresCollection,
7825
8092
  orderBy4("name"),
7826
- limit11(pagination)
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((doc32) => {
7835
- const data = doc32.data();
8101
+ const procedures = proceduresSnapshot.docs.map((doc33) => {
8102
+ const data = doc33.data();
7836
8103
  return {
7837
8104
  ...data,
7838
- id: doc32.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 doc32 of querySnapshot.docs) {
7921
- const procedure = { ...doc32.data(), id: doc32.id };
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((doc32) => {
7973
- return { ...doc32.data(), id: doc32.id };
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 updateData = {
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, updateData);
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((doc32) => {
8205
- templates.push(doc32.data());
8206
- lastVisible = doc32;
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((doc32) => {
8235
- templates.push(doc32.data());
8236
- lastVisible = doc32;
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((doc32) => {
8264
- templates.push(doc32.data());
8265
- lastVisible = doc32;
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 patientId - ID of the patient
8301
- * @param practitionerId - ID of the practitioner
8302
- * @param clinicId - ID of the clinic
8303
- * @returns The created filled document
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 createFilledDocument(templateId, patientId, practitionerId, clinicId) {
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: "draft" /* DRAFT */
8613
+ values: initialValues,
8614
+ status: initialStatus
8323
8615
  };
8324
- const docRef = doc17(this.collectionRef, documentId3);
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 by ID
8330
- * @param documentId - ID of the filled document to retrieve
8331
- * @returns The filled document or null if not found
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 getFilledDocumentById(documentId3) {
8334
- const docRef = doc17(this.collectionRef, documentId3);
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 documentId - ID of the filled document to update
8344
- * @param values - Updated values for elements
8345
- * @param status - Optional new status for the document
8346
- * @returns The updated filled document
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 updateFilledDocument(documentId3, values, status) {
8349
- const filledDocument = await this.getFilledDocumentById(documentId3);
8350
- if (!filledDocument) {
8351
- throw new Error(`Filled document with ID ${documentId3} not found`);
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 updateData = {
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
- updateData.status = status;
8689
+ updatePayload.status = status;
8362
8690
  }
8363
- const docRef = doc17(this.collectionRef, documentId3);
8364
- await updateDoc17(docRef, updateData);
8365
- return {
8366
- ...filledDocument,
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 documents for a patient
8372
- * @param patientId - ID of the patient
8373
- * @param pageSize - Number of documents to retrieve
8374
- * @param lastDoc - Last document from previous page for pagination
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 getFilledDocumentsByPatient(patientId, pageSize = 20, lastDoc) {
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
- this.collectionRef,
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
- const querySnapshot = await getDocs17(q);
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 documents for a practitioner
8401
- * @param practitionerId - ID of the practitioner
8402
- * @param pageSize - Number of documents to retrieve
8403
- * @param lastDoc - Last document from previous page for pagination
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 getFilledDocumentsByPractitioner(practitionerId, pageSize = 20, lastDoc) {
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
- this.collectionRef,
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((doc32) => {
8420
- documents.push(doc32.data());
8421
- lastVisible = doc32;
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 clinic
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
- let q = query17(
8437
- this.collectionRef,
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
- if (lastDoc) {
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
- let q = query17(
8466
- this.collectionRef,
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
- if (lastDoc) {
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
- let q = query17(
8495
- this.collectionRef,
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
- if (lastDoc) {
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
- (doc32) => ({ id: doc32.id, ...doc32.data() })
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => ({
10547
- id: doc32.id,
10548
- ...doc32.data()
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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((doc32) => doc32.data());
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 "rescheduled" /* RESCHEDULED */:
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
- (doc32) => doc32.data()
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 cancellationReason Required if canceling the appointment
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, cancellationReason, canceledBy) {
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 = { status: newStatus };
12680
- if (newStatus === "canceled_clinic" /* CANCELED_CLINIC */ || newStatus === "canceled_patient" /* CANCELED_PATIENT */) {
12681
- if (!cancellationReason) {
12682
- throw new Error(
12683
- "Cancellation reason is required when canceling an appointment"
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 appointment.
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 confirmAppointment(appointmentId) {
13022
+ async confirmAppointmentAdmin(appointmentId) {
12706
13023
  console.log(
12707
- `[APPOINTMENT_SERVICE] Confirming appointment: ${appointmentId}`
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 from the clinic side.
12716
- *
12717
- * @param appointmentId ID of the appointment to cancel
12718
- * @param reason Reason for cancellation
12719
- * @returns The canceled appointment
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 cancelAppointmentByClinic(appointmentId, reason) {
13058
+ async cancelAppointmentAdmin(appointmentId, reason) {
12722
13059
  console.log(
12723
- `[APPOINTMENT_SERVICE] Canceling appointment by clinic: ${appointmentId}`
13060
+ `[APPOINTMENT_SERVICE] Admin canceling appointment: ${appointmentId}`
12724
13061
  );
12725
13062
  return this.updateAppointmentStatus(
12726
13063
  appointmentId,
12727
13064
  "canceled_clinic" /* CANCELED_CLINIC */,
12728
- reason,
12729
- "clinic"
13065
+ {
13066
+ cancellationReason: reason,
13067
+ canceledBy: "clinic"
13068
+ }
12730
13069
  );
12731
13070
  }
12732
13071
  /**
12733
- * Cancels an appointment from the patient side.
12734
- *
12735
- * @param appointmentId ID of the appointment to cancel
12736
- * @param reason Reason for cancellation
12737
- * @returns The canceled appointment
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 cancelAppointmentByPatient(appointmentId, reason) {
13096
+ async rescheduleAppointmentConfirmUser(appointmentId) {
12740
13097
  console.log(
12741
- `[APPOINTMENT_SERVICE] Canceling appointment by patient: ${appointmentId}`
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
- "canceled_patient" /* CANCELED_PATIENT */,
12746
- reason,
12747
- "patient"
13110
+ "confirmed" /* CONFIRMED */
12748
13111
  );
12749
13112
  }
12750
13113
  /**
12751
- * Marks an appointment as checked in.
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 checkInAppointment(appointmentId) {
13117
+ async rescheduleAppointmentRejectUser(appointmentId, reason) {
12757
13118
  console.log(
12758
- `[APPOINTMENT_SERVICE] Checking in appointment: ${appointmentId}`
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
- "checked_in" /* CHECKED_IN */
13131
+ "canceled_patient_rescheduled" /* CANCELED_PATIENT_RESCHEDULED */,
13132
+ {
13133
+ cancellationReason: reason,
13134
+ canceledBy: "patient"
13135
+ }
12763
13136
  );
12764
13137
  }
12765
13138
  /**
12766
- * Marks an appointment as in progress.
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 startAppointment(appointmentId) {
12772
- console.log(`[APPOINTMENT_SERVICE] Starting appointment: ${appointmentId}`);
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
- "in_progress" /* IN_PROGRESS */
13163
+ "checked_in" /* CHECKED_IN */
12776
13164
  );
12777
13165
  }
12778
13166
  /**
12779
- * Marks an appointment as completed.
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 completeAppointment(appointmentId, actualDurationMinutes) {
13169
+ async startAppointmentDoctor(appointmentId) {
12786
13170
  console.log(
12787
- `[APPOINTMENT_SERVICE] Completing appointment: ${appointmentId}`
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
- * Marks an appointment as no-show.
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 markNoShow(appointmentId) {
13233
+ async markNoShowAdmin(appointmentId) {
12802
13234
  console.log(
12803
- `[APPOINTMENT_SERVICE] Marking appointment as no-show: ${appointmentId}`
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/backoffice/services/brand.service.ts
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
- where as where26
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 collection26(this.db, BRANDS_COLLECTION);
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 = query26(this.getBrandsRef(), where26("isActive", "==", true));
12939
- const snapshot = await getDocs26(q);
13642
+ const q = query27(this.getBrandsRef(), where27("isActive", "==", true));
13643
+ const snapshot = await getDocs27(q);
12940
13644
  return snapshot.docs.map(
12941
- (doc32) => ({
12942
- id: doc32.id,
12943
- ...doc32.data()
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 = doc27(this.getBrandsRef(), brandId);
12956
- await updateDoc26(docRef, updateData);
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 = doc27(this.getBrandsRef(), brandId);
12972
- const docSnap = await getDoc29(docRef);
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 collection27,
12985
- doc as doc28,
12986
- getDoc as getDoc30,
12987
- getDocs as getDocs27,
12988
- query as query27,
12989
- updateDoc as updateDoc27,
12990
- where as where27
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 collection27(this.db, CATEGORIES_COLLECTION);
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 = query27(this.categoriesRef, where27("isActive", "==", true));
13026
- const snapshot = await getDocs27(q);
13729
+ const q = query28(this.categoriesRef, where28("isActive", "==", true));
13730
+ const snapshot = await getDocs28(q);
13027
13731
  return snapshot.docs.map(
13028
- (doc32) => ({
13029
- id: doc32.id,
13030
- ...doc32.data()
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 = query27(
13744
+ const q = query28(
13041
13745
  this.categoriesRef,
13042
- where27("family", "==", family),
13043
- where27("isActive", "==", true)
13746
+ where28("family", "==", family),
13747
+ where28("isActive", "==", true)
13044
13748
  );
13045
- const snapshot = await getDocs27(q);
13749
+ const snapshot = await getDocs28(q);
13046
13750
  return snapshot.docs.map(
13047
- (doc32) => ({
13048
- id: doc32.id,
13049
- ...doc32.data()
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 = doc28(this.categoriesRef, id);
13065
- await updateDoc27(docRef, updateData);
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 = doc28(this.categoriesRef, id);
13082
- const docSnap = await getDoc30(docRef);
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 collection28,
13095
- doc as doc29,
13096
- getDoc as getDoc31,
13097
- getDocs as getDocs28,
13098
- query as query28,
13099
- updateDoc as updateDoc28,
13100
- where as where28
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 collection28(
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 = query28(
13851
+ const q = query29(
13148
13852
  this.getSubcategoriesRef(categoryId),
13149
- where28("isActive", "==", true)
13853
+ where29("isActive", "==", true)
13150
13854
  );
13151
- const snapshot = await getDocs28(q);
13855
+ const snapshot = await getDocs29(q);
13152
13856
  return snapshot.docs.map(
13153
- (doc32) => ({
13154
- id: doc32.id,
13155
- ...doc32.data()
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 = doc29(this.getSubcategoriesRef(categoryId), subcategoryId);
13172
- await updateDoc28(docRef, updateData);
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 = doc29(this.getSubcategoriesRef(categoryId), subcategoryId);
13191
- const docSnap = await getDoc31(docRef);
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 collection29,
13204
- doc as doc30,
13205
- getDoc as getDoc32,
13206
- getDocs as getDocs29,
13207
- query as query29,
13208
- updateDoc as updateDoc29,
13209
- where as where29,
13210
- arrayUnion as arrayUnion8,
13211
- arrayRemove as arrayRemove7
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 collection29(this.db, TECHNOLOGIES_COLLECTION);
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 = query29(this.getTechnologiesRef(), where29("isActive", "==", true));
13254
- const snapshot = await getDocs29(q);
13957
+ const q = query30(this.getTechnologiesRef(), where30("isActive", "==", true));
13958
+ const snapshot = await getDocs30(q);
13255
13959
  return snapshot.docs.map(
13256
- (doc32) => ({
13257
- id: doc32.id,
13258
- ...doc32.data()
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 = query29(
13972
+ const q = query30(
13269
13973
  this.getTechnologiesRef(),
13270
- where29("isActive", "==", true),
13271
- where29("family", "==", family)
13974
+ where30("isActive", "==", true),
13975
+ where30("family", "==", family)
13272
13976
  );
13273
- const snapshot = await getDocs29(q);
13977
+ const snapshot = await getDocs30(q);
13274
13978
  return snapshot.docs.map(
13275
- (doc32) => ({
13276
- id: doc32.id,
13277
- ...doc32.data()
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 = query29(
13991
+ const q = query30(
13288
13992
  this.getTechnologiesRef(),
13289
- where29("isActive", "==", true),
13290
- where29("categoryId", "==", categoryId)
13993
+ where30("isActive", "==", true),
13994
+ where30("categoryId", "==", categoryId)
13291
13995
  );
13292
- const snapshot = await getDocs29(q);
13996
+ const snapshot = await getDocs30(q);
13293
13997
  return snapshot.docs.map(
13294
- (doc32) => ({
13295
- id: doc32.id,
13296
- ...doc32.data()
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 = query29(
14010
+ const q = query30(
13307
14011
  this.getTechnologiesRef(),
13308
- where29("isActive", "==", true),
13309
- where29("subcategoryId", "==", subcategoryId)
14012
+ where30("isActive", "==", true),
14013
+ where30("subcategoryId", "==", subcategoryId)
13310
14014
  );
13311
- const snapshot = await getDocs29(q);
14015
+ const snapshot = await getDocs30(q);
13312
14016
  return snapshot.docs.map(
13313
- (doc32) => ({
13314
- id: doc32.id,
13315
- ...doc32.data()
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 = doc30(this.getTechnologiesRef(), technologyId);
13331
- await updateDoc29(docRef, updateData);
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 = doc30(this.getTechnologiesRef(), technologyId);
13350
- const docSnap = await getDoc32(docRef);
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 = doc30(this.getTechnologiesRef(), technologyId);
14068
+ const docRef = doc31(this.getTechnologiesRef(), technologyId);
13365
14069
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
13366
- await updateDoc29(docRef, {
13367
- [requirementType]: arrayUnion8(requirement),
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 = doc30(this.getTechnologiesRef(), technologyId);
14083
+ const docRef = doc31(this.getTechnologiesRef(), technologyId);
13380
14084
  const requirementType = requirement.type === "pre" ? "requirements.pre" : "requirements.post";
13381
- await updateDoc29(docRef, {
13382
- [requirementType]: arrayRemove7(requirement),
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 = doc30(this.getTechnologiesRef(), technologyId);
13420
- await updateDoc29(docRef, {
13421
- blockingConditions: arrayUnion8(condition),
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 = doc30(this.getTechnologiesRef(), technologyId);
13434
- await updateDoc29(docRef, {
13435
- blockingConditions: arrayRemove7(condition),
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 = doc30(this.getTechnologiesRef(), technologyId);
13448
- await updateDoc29(docRef, {
13449
- contraindications: arrayUnion8(contraindication),
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 = doc30(this.getTechnologiesRef(), technologyId);
13462
- await updateDoc29(docRef, {
13463
- contraindications: arrayRemove7(contraindication),
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 = doc30(this.getTechnologiesRef(), technologyId);
13476
- await updateDoc29(docRef, {
13477
- benefits: arrayUnion8(benefit),
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 = doc30(this.getTechnologiesRef(), technologyId);
13490
- await updateDoc29(docRef, {
13491
- benefits: arrayRemove7(benefit),
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 = doc30(this.getTechnologiesRef(), technologyId);
13531
- await updateDoc29(docRef, {
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 collection30,
13634
- doc as doc31,
13635
- getDoc as getDoc33,
13636
- getDocs as getDocs30,
13637
- query as query30,
13638
- updateDoc as updateDoc30,
13639
- where as where30
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 collection30(
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 = query30(
14387
+ const q = query31(
13684
14388
  this.getProductsRef(technologyId),
13685
- where30("isActive", "==", true)
14389
+ where31("isActive", "==", true)
13686
14390
  );
13687
- const snapshot = await getDocs30(q);
14391
+ const snapshot = await getDocs31(q);
13688
14392
  return snapshot.docs.map(
13689
- (doc32) => ({
13690
- id: doc32.id,
13691
- ...doc32.data()
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 = collection30(this.db, TECHNOLOGIES_COLLECTION);
13700
- const technologiesSnapshot = await getDocs30(allTechnologiesRef);
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 = query30(
14407
+ const q = query31(
13704
14408
  this.getProductsRef(techDoc.id),
13705
- where30("brandId", "==", brandId),
13706
- where30("isActive", "==", true)
14409
+ where31("brandId", "==", brandId),
14410
+ where31("isActive", "==", true)
13707
14411
  );
13708
- const snapshot = await getDocs30(q);
14412
+ const snapshot = await getDocs31(q);
13709
14413
  products.push(
13710
14414
  ...snapshot.docs.map(
13711
- (doc32) => ({
13712
- id: doc32.id,
13713
- ...doc32.data()
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 = doc31(this.getProductsRef(technologyId), productId);
13729
- await updateDoc30(docRef, updateData);
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 = doc31(this.getProductsRef(technologyId), productId);
13745
- const docSnap = await getDoc33(docRef);
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("preRequirement" /* PRE_REQUIREMENT */),
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("postRequirement" /* POST_REQUIREMENT */),
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("appointmentNotification" /* APPOINTMENT_NOTIFICATION */),
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,