@blackcode_sa/metaestetics-api 1.6.3 → 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/admin/index.d.mts +439 -25
  2. package/dist/admin/index.d.ts +439 -25
  3. package/dist/admin/index.js +36107 -2493
  4. package/dist/admin/index.mjs +36093 -2461
  5. package/dist/backoffice/index.d.mts +254 -1
  6. package/dist/backoffice/index.d.ts +254 -1
  7. package/dist/backoffice/index.js +86 -12
  8. package/dist/backoffice/index.mjs +86 -13
  9. package/dist/index.d.mts +1434 -621
  10. package/dist/index.d.ts +1434 -621
  11. package/dist/index.js +1381 -970
  12. package/dist/index.mjs +1433 -1016
  13. package/package.json +1 -1
  14. package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +321 -0
  15. package/src/admin/booking/booking.admin.ts +376 -3
  16. package/src/admin/index.ts +15 -1
  17. package/src/admin/notifications/notifications.admin.ts +1 -1
  18. package/src/admin/requirements/README.md +128 -0
  19. package/src/admin/requirements/patient-requirements.admin.service.ts +482 -0
  20. package/src/backoffice/types/product.types.ts +2 -0
  21. package/src/index.ts +16 -1
  22. package/src/services/appointment/appointment.service.ts +386 -250
  23. package/src/services/clinic/clinic-admin.service.ts +3 -0
  24. package/src/services/clinic/clinic-group.service.ts +8 -0
  25. package/src/services/documentation-templates/documentation-template.service.ts +24 -16
  26. package/src/services/documentation-templates/filled-document.service.ts +253 -136
  27. package/src/services/patient/patientRequirements.service.ts +285 -0
  28. package/src/services/procedure/procedure.service.ts +1 -0
  29. package/src/types/appointment/index.ts +136 -11
  30. package/src/types/documentation-templates/index.ts +34 -2
  31. package/src/types/notifications/README.md +77 -0
  32. package/src/types/notifications/index.ts +154 -27
  33. package/src/types/patient/patient-requirements.ts +81 -0
  34. package/src/types/procedure/index.ts +7 -0
  35. package/src/validations/appointment.schema.ts +298 -62
  36. package/src/validations/documentation-templates/template.schema.ts +55 -0
  37. package/src/validations/documentation-templates.schema.ts +9 -14
  38. package/src/validations/notification.schema.ts +3 -3
  39. package/src/validations/patient/patient-requirements.schema.ts +75 -0
  40. package/src/validations/procedure.schema.ts +3 -0
@@ -1,86 +1,275 @@
1
1
  import { z } from "zod";
2
- import { AppointmentStatus, PaymentStatus } from "../types/appointment";
2
+ import {
3
+ AppointmentStatus,
4
+ PaymentStatus,
5
+ MediaType,
6
+ } from "../types/appointment";
7
+ import { filledDocumentStatusSchema } from "./documentation-templates.schema";
3
8
 
4
- /**
5
- * Schema for validating appointment creation data
6
- */
7
- export const createAppointmentSchema = z.object({
8
- calendarEventId: z.string().min(1, "Calendar event ID is required"),
9
- clinicBranchId: z.string().min(1, "Clinic branch ID is required"),
10
- practitionerId: z.string().min(1, "Practitioner ID is required"),
11
- patientId: z.string().min(1, "Patient ID is required"),
12
- procedureId: z.string().min(1, "Procedure ID is required"),
13
- appointmentStartTime: z
9
+ // Define common constants locally if not available from common.schema.ts
10
+ const MIN_STRING_LENGTH = 1;
11
+ const MAX_STRING_LENGTH = 1024; // Example value
12
+ const MAX_STRING_LENGTH_LONG = 4096; // Example value for longer notes/comments
13
+ const MAX_ARRAY_LENGTH = 100; // Example value
14
+
15
+ // --- Enum Schemas ---
16
+ export const appointmentStatusSchema = z.nativeEnum(AppointmentStatus);
17
+ export const paymentStatusSchema = z.nativeEnum(PaymentStatus);
18
+ export const mediaTypeSchema = z.nativeEnum(MediaType);
19
+
20
+ // --- Schemas for Nested Objects from types/appointment/index.ts ---
21
+
22
+ export const appointmentMediaItemSchema = z.object({
23
+ id: z.string().min(MIN_STRING_LENGTH, "Media item ID is required"),
24
+ type: mediaTypeSchema,
25
+ url: z.string().url("Media URL must be a valid URL"),
26
+ fileName: z.string().optional(),
27
+ uploadedAt: z
14
28
  .any()
15
29
  .refine(
16
- (val) => val instanceof Date || val?._seconds !== undefined,
17
- "Appointment start time must be a valid timestamp"
30
+ (val) =>
31
+ val instanceof Date ||
32
+ val?._seconds !== undefined ||
33
+ typeof val === "number",
34
+ "uploadedAt must be a valid timestamp or Date object"
18
35
  ),
19
- appointmentEndTime: z
36
+ uploadedBy: z
37
+ .string()
38
+ .min(MIN_STRING_LENGTH, "Uploaded by user ID is required"),
39
+ description: z
40
+ .string()
41
+ .max(MAX_STRING_LENGTH, "Description too long")
42
+ .optional(),
43
+ });
44
+
45
+ export const procedureExtendedInfoSchema = z.object({
46
+ id: z.string().min(MIN_STRING_LENGTH),
47
+ name: z.string().min(MIN_STRING_LENGTH),
48
+ description: z.string(),
49
+ cost: z.number().min(0),
50
+ duration: z.number().min(0),
51
+ procedureFamily: z.any(),
52
+ procedureCategoryId: z.string(),
53
+ procedureCategoryName: z.string(),
54
+ procedureSubCategoryId: z.string(),
55
+ procedureSubCategoryName: z.string(),
56
+ procedureTechnologyId: z.string(),
57
+ procedureTechnologyName: z.string(),
58
+ procedureProductBrandId: z.string(),
59
+ procedureProductBrandName: z.string(),
60
+ procedureProductId: z.string(),
61
+ procedureProductName: z.string(),
62
+ });
63
+
64
+ export const linkedFormInfoSchema = z.object({
65
+ formId: z.string().min(MIN_STRING_LENGTH, "Form ID is required"),
66
+ templateId: z.string().min(MIN_STRING_LENGTH, "Template ID is required"),
67
+ templateVersion: z
68
+ .number()
69
+ .int()
70
+ .positive("Template version must be a positive integer"),
71
+ title: z.string().min(MIN_STRING_LENGTH, "Form title is required"),
72
+ isUserForm: z.boolean(),
73
+ status: filledDocumentStatusSchema,
74
+ path: z.string().min(MIN_STRING_LENGTH, "Form path is required"),
75
+ submittedAt: z
76
+ .any()
77
+ .refine(
78
+ (val) =>
79
+ val === undefined ||
80
+ val instanceof Date ||
81
+ val?._seconds !== undefined ||
82
+ typeof val === "number",
83
+ "submittedAt must be a valid timestamp or Date object"
84
+ )
85
+ .optional(),
86
+ completedAt: z
20
87
  .any()
21
88
  .refine(
22
- (val) => val instanceof Date || val?._seconds !== undefined,
23
- "Appointment end time must be a valid timestamp"
89
+ (val) =>
90
+ val === undefined ||
91
+ val instanceof Date ||
92
+ val?._seconds !== undefined ||
93
+ typeof val === "number",
94
+ "completedAt must be a valid timestamp or Date object"
95
+ )
96
+ .optional(),
97
+ });
98
+
99
+ export const patientReviewInfoSchema = z.object({
100
+ reviewId: z.string().min(MIN_STRING_LENGTH, "Review ID is required"),
101
+ rating: z.number().min(1).max(5, "Rating must be between 1 and 5"),
102
+ comment: z
103
+ .string()
104
+ .max(MAX_STRING_LENGTH_LONG, "Comment too long")
105
+ .optional(),
106
+ reviewedAt: z
107
+ .any()
108
+ .refine(
109
+ (val) =>
110
+ val instanceof Date ||
111
+ val?._seconds !== undefined ||
112
+ typeof val === "number",
113
+ "reviewedAt must be a valid timestamp or Date object"
24
114
  ),
25
- cost: z.number().min(0, "Cost must be a non-negative number"),
26
- currency: z.string().min(1, "Currency is required"),
27
- patientNotes: z.string().nullable().optional(),
28
- initialStatus: z.nativeEnum(AppointmentStatus, {
29
- errorMap: () => ({ message: "Invalid appointment status" }),
30
- }),
31
- initialPaymentStatus: z
32
- .nativeEnum(PaymentStatus, {
33
- errorMap: () => ({ message: "Invalid payment status" }),
34
- })
35
- .optional()
36
- .default(PaymentStatus.UNPAID),
37
115
  });
38
116
 
117
+ export const finalizedDetailsSchema = z.object({
118
+ by: z.string().min(MIN_STRING_LENGTH, "Finalized by user ID is required"),
119
+ at: z
120
+ .any()
121
+ .refine(
122
+ (val) =>
123
+ val instanceof Date ||
124
+ val?._seconds !== undefined ||
125
+ typeof val === "number",
126
+ "Finalized at must be a valid timestamp or Date object"
127
+ ),
128
+ notes: z
129
+ .string()
130
+ .max(MAX_STRING_LENGTH_LONG, "Finalization notes too long")
131
+ .optional(),
132
+ });
133
+
134
+ // --- Main Appointment Schemas ---
135
+
136
+ /**
137
+ * Schema for validating appointment creation data
138
+ */
139
+ export const createAppointmentSchema = z
140
+ .object({
141
+ clinicBranchId: z
142
+ .string()
143
+ .min(MIN_STRING_LENGTH, "Clinic branch ID is required"),
144
+ practitionerId: z
145
+ .string()
146
+ .min(MIN_STRING_LENGTH, "Practitioner ID is required"),
147
+ patientId: z.string().min(MIN_STRING_LENGTH, "Patient ID is required"),
148
+ procedureId: z.string().min(MIN_STRING_LENGTH, "Procedure ID is required"),
149
+ appointmentStartTime: z
150
+ .any()
151
+ .refine(
152
+ (val) =>
153
+ val instanceof Date ||
154
+ val?._seconds !== undefined ||
155
+ typeof val === "number",
156
+ "Appointment start time must be a valid timestamp or Date object"
157
+ ),
158
+ appointmentEndTime: z
159
+ .any()
160
+ .refine(
161
+ (val) =>
162
+ val instanceof Date ||
163
+ val?._seconds !== undefined ||
164
+ typeof val === "number",
165
+ "Appointment end time must be a valid timestamp or Date object"
166
+ ),
167
+ cost: z.number().min(0, "Cost must be a non-negative number"),
168
+ currency: z.string().min(1, "Currency is required"),
169
+ patientNotes: z
170
+ .string()
171
+ .max(MAX_STRING_LENGTH, "Patient notes too long")
172
+ .nullable()
173
+ .optional(),
174
+ initialStatus: appointmentStatusSchema,
175
+ initialPaymentStatus: paymentStatusSchema
176
+ .optional()
177
+ .default(PaymentStatus.UNPAID),
178
+ })
179
+ .refine((data) => data.appointmentEndTime > data.appointmentStartTime, {
180
+ message: "Appointment end time must be after start time",
181
+ path: ["appointmentEndTime"],
182
+ });
183
+
39
184
  /**
40
185
  * Schema for validating appointment update data
41
186
  */
42
187
  export const updateAppointmentSchema = z
43
188
  .object({
44
- status: z
45
- .nativeEnum(AppointmentStatus, {
46
- errorMap: () => ({ message: "Invalid appointment status" }),
47
- })
189
+ status: appointmentStatusSchema.optional(),
190
+ confirmationTime: z.any().optional().nullable(),
191
+ cancellationTime: z.any().optional().nullable(),
192
+ rescheduleTime: z.any().optional().nullable(),
193
+ procedureActualStartTime: z.any().optional().nullable(),
194
+ actualDurationMinutes: z
195
+ .number()
196
+ .int()
197
+ .positive("Duration must be a positive integer")
198
+ .optional(),
199
+ cancellationReason: z
200
+ .string()
201
+ .max(MAX_STRING_LENGTH, "Cancellation reason too long")
202
+ .nullable()
203
+ .optional(),
204
+ canceledBy: z
205
+ .enum(["patient", "clinic", "practitioner", "system"])
206
+ .optional(),
207
+ internalNotes: z
208
+ .string()
209
+ .max(MAX_STRING_LENGTH_LONG, "Internal notes too long")
210
+ .nullable()
211
+ .optional(),
212
+ patientNotes: z.any().optional().nullable(),
213
+ paymentStatus: paymentStatusSchema.optional(),
214
+ paymentTransactionId: z.any().optional().nullable(),
215
+ completedPreRequirements: z
216
+ .union([z.array(z.string()), z.any()])
48
217
  .optional(),
49
- confirmationTime: z
218
+ completedPostRequirements: z
219
+ .union([z.array(z.string()), z.any()])
220
+ .optional(),
221
+ linkedFormIds: z.union([z.array(z.string()), z.any()]).optional(),
222
+ pendingUserFormsIds: z.union([z.array(z.string()), z.any()]).optional(),
223
+ appointmentStartTime: z
50
224
  .any()
51
225
  .refine(
52
226
  (val) =>
53
- val === null ||
54
227
  val === undefined ||
55
228
  val instanceof Date ||
56
- val?._seconds !== undefined,
57
- "Confirmation time must be a valid timestamp or null"
229
+ val?._seconds !== undefined ||
230
+ typeof val === "number",
231
+ "Appointment start time must be a valid timestamp or Date object"
58
232
  )
59
233
  .optional(),
60
- actualDurationMinutes: z
61
- .number()
62
- .positive("Duration must be positive")
234
+ appointmentEndTime: z
235
+ .any()
236
+ .refine(
237
+ (val) =>
238
+ val === undefined ||
239
+ val instanceof Date ||
240
+ val?._seconds !== undefined ||
241
+ typeof val === "number",
242
+ "Appointment end time must be a valid timestamp or Date object"
243
+ )
63
244
  .optional(),
64
- cancellationReason: z.string().nullable().optional(),
65
- canceledBy: z
66
- .enum(["patient", "clinic", "practitioner", "system"])
245
+ calendarEventId: z.string().min(MIN_STRING_LENGTH).optional(),
246
+ cost: z.number().min(0).optional(),
247
+ clinicBranchId: z.string().min(MIN_STRING_LENGTH).optional(),
248
+ practitionerId: z.string().min(MIN_STRING_LENGTH).optional(),
249
+ linkedForms: z
250
+ .union([z.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z.any()])
67
251
  .optional(),
68
- internalNotes: z.string().nullable().optional(),
69
- paymentStatus: z
70
- .nativeEnum(PaymentStatus, {
71
- errorMap: () => ({ message: "Invalid payment status" }),
72
- })
252
+ media: z
253
+ .union([
254
+ z.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
255
+ z.any(),
256
+ ])
257
+ .optional(),
258
+ reviewInfo: z
259
+ .union([patientReviewInfoSchema.nullable(), z.any()])
260
+ .optional(),
261
+ finalizedDetails: z
262
+ .union([finalizedDetailsSchema.nullable(), z.any()])
73
263
  .optional(),
74
- paymentTransactionId: z.string().nullable().optional(),
75
- completedPreRequirements: z.array(z.string()).optional(),
76
- completedPostRequirements: z.array(z.string()).optional(),
264
+ isArchived: z.boolean().optional(),
265
+ updatedAt: z.any().optional(),
77
266
  })
78
267
  .refine(
79
268
  (data) => {
80
- // If status is being set to canceled, make sure reason and canceledBy are provided
81
269
  if (
82
270
  data.status === AppointmentStatus.CANCELED_CLINIC ||
83
- data.status === AppointmentStatus.CANCELED_PATIENT
271
+ data.status === AppointmentStatus.CANCELED_PATIENT ||
272
+ data.status === AppointmentStatus.CANCELED_PATIENT_RESCHEDULED
84
273
  ) {
85
274
  return !!data.cancellationReason && !!data.canceledBy;
86
275
  }
@@ -88,9 +277,22 @@ export const updateAppointmentSchema = z
88
277
  },
89
278
  {
90
279
  message:
91
- "Cancellation reason and canceled by must be provided when canceling an appointment",
280
+ "Cancellation reason and canceled by must be provided when canceling an appointment with patient or clinic origin.",
92
281
  path: ["status"],
93
282
  }
283
+ )
284
+ .refine(
285
+ (data) => {
286
+ if (data.appointmentStartTime && data.appointmentEndTime) {
287
+ return data.appointmentEndTime > data.appointmentStartTime;
288
+ }
289
+ return true;
290
+ },
291
+ {
292
+ message:
293
+ "Appointment end time must be after start time if both are provided",
294
+ path: ["appointmentEndTime"],
295
+ }
94
296
  );
95
297
 
96
298
  /**
@@ -101,25 +303,59 @@ export const searchAppointmentsSchema = z
101
303
  patientId: z.string().optional(),
102
304
  practitionerId: z.string().optional(),
103
305
  clinicBranchId: z.string().optional(),
104
- startDate: z.date().optional(),
105
- endDate: z.date().optional(),
306
+ startDate: z
307
+ .any()
308
+ .refine(
309
+ (val) =>
310
+ val === undefined ||
311
+ val instanceof Date ||
312
+ val?._seconds !== undefined ||
313
+ typeof val === "number",
314
+ "Start date must be a valid timestamp or Date object"
315
+ )
316
+ .optional(),
317
+ endDate: z
318
+ .any()
319
+ .refine(
320
+ (val) =>
321
+ val === undefined ||
322
+ val instanceof Date ||
323
+ val?._seconds !== undefined ||
324
+ typeof val === "number",
325
+ "End date must be a valid timestamp or Date object"
326
+ )
327
+ .optional(),
106
328
  status: z
107
329
  .union([
108
- z.nativeEnum(AppointmentStatus),
109
- z.array(z.nativeEnum(AppointmentStatus)),
330
+ appointmentStatusSchema,
331
+ z.array(appointmentStatusSchema).nonempty(),
110
332
  ])
111
333
  .optional(),
112
- limit: z.number().positive().optional(),
334
+ limit: z.number().positive().int().optional().default(20),
113
335
  startAfter: z.any().optional(),
114
336
  })
115
337
  .refine(
116
338
  (data) => {
117
- // Ensure at least one search parameter is provided
118
- return !!(data.patientId || data.practitionerId || data.clinicBranchId);
339
+ if (!data.startDate && !data.endDate && !data.status) {
340
+ return !!(data.patientId || data.practitionerId || data.clinicBranchId);
341
+ }
342
+ return true;
119
343
  },
120
344
  {
121
345
  message:
122
- "At least one of patientId, practitionerId, or clinicBranchId must be provided",
123
- path: ["searchCriteria"],
346
+ "At least one of patientId, practitionerId, or clinicBranchId must be provided if no date or status filters are set.",
347
+ path: ["patientId"],
348
+ }
349
+ )
350
+ .refine(
351
+ (data) => {
352
+ if (data.startDate && data.endDate) {
353
+ return data.endDate >= data.startDate;
354
+ }
355
+ return true;
356
+ },
357
+ {
358
+ message: "End date must be after or the same as start date",
359
+ path: ["endDate"],
124
360
  }
125
361
  );
@@ -3,6 +3,7 @@ import {
3
3
  DocumentElementType,
4
4
  HeadingLevel,
5
5
  ListType,
6
+ FilledDocumentStatus,
6
7
  } from "../../types/documentation-templates";
7
8
 
8
9
  // Base element schema
@@ -93,6 +94,12 @@ const signatureElementSchema = baseElementSchema.extend({
93
94
  label: z.string().min(1).max(200),
94
95
  });
95
96
 
97
+ // NEW: Digital signature element schema
98
+ const digitalSignatureElementSchema = baseElementSchema.extend({
99
+ type: z.literal(DocumentElementType.DITIGAL_SIGNATURE), // Matching your type enum
100
+ label: z.string().min(1).max(200),
101
+ });
102
+
96
103
  // File upload element schema
97
104
  const fileUploadElementSchema = baseElementSchema.extend({
98
105
  type: z.literal(DocumentElementType.FILE_UPLOAD),
@@ -114,6 +121,7 @@ export const documentElementSchema = z.discriminatedUnion("type", [
114
121
  textInputElementSchema,
115
122
  datePickerElementSchema,
116
123
  signatureElementSchema,
124
+ digitalSignatureElementSchema,
117
125
  fileUploadElementSchema,
118
126
  ]);
119
127
 
@@ -130,6 +138,7 @@ export const documentElementWithoutIdSchema = z.discriminatedUnion("type", [
130
138
  textInputElementSchema.omit({ id: true }),
131
139
  datePickerElementSchema.omit({ id: true }),
132
140
  signatureElementSchema.omit({ id: true }),
141
+ digitalSignatureElementSchema.omit({ id: true }),
133
142
  fileUploadElementSchema.omit({ id: true }),
134
143
  ]);
135
144
 
@@ -139,6 +148,9 @@ export const createDocumentTemplateSchema = z.object({
139
148
  description: z.string().max(1000).optional(),
140
149
  elements: z.array(documentElementWithoutIdSchema).min(1).max(100),
141
150
  tags: z.array(z.string().min(1).max(50)).max(20).optional(),
151
+ isUserForm: z.boolean().optional().default(false),
152
+ isRequired: z.boolean().optional().default(false),
153
+ sortingOrder: z.number().int().optional().default(0),
142
154
  });
143
155
 
144
156
  // Schema for updating a document template
@@ -148,6 +160,9 @@ export const updateDocumentTemplateSchema = z.object({
148
160
  elements: z.array(documentElementWithoutIdSchema).min(1).max(100).optional(),
149
161
  tags: z.array(z.string().min(1).max(50)).max(20).optional(),
150
162
  isActive: z.boolean().optional(),
163
+ isUserForm: z.boolean().optional(),
164
+ isRequired: z.boolean().optional(),
165
+ sortingOrder: z.number().int().optional(),
151
166
  });
152
167
 
153
168
  // Schema for a document template
@@ -162,4 +177,44 @@ export const documentTemplateSchema = z.object({
162
177
  tags: z.array(z.string().min(1).max(50)).max(20).optional(),
163
178
  version: z.number().int().positive(),
164
179
  isActive: z.boolean(),
180
+ isUserForm: z.boolean().optional().default(false),
181
+ isRequired: z.boolean().optional().default(false),
182
+ sortingOrder: z.number().int().optional().default(0),
183
+ });
184
+
185
+ // Filled Document Schemas
186
+ export const filledDocumentStatusSchema = z.nativeEnum(FilledDocumentStatus);
187
+
188
+ export const filledDocumentSchema = z.object({
189
+ id: z.string(),
190
+ templateId: z.string(),
191
+ templateVersion: z.number(),
192
+ isUserForm: z.boolean(),
193
+ procedureId: z.string(),
194
+ appointmentId: z.string(),
195
+ patientId: z.string(),
196
+ practitionerId: z.string(),
197
+ clinicId: z.string(),
198
+ createdAt: z.number(),
199
+ updatedAt: z.number(),
200
+ values: z.record(z.string(), z.any()),
201
+ status: filledDocumentStatusSchema,
202
+ });
203
+
204
+ export const createFilledDocumentDataSchema = z.object({
205
+ templateId: z.string(),
206
+ procedureId: z.string(),
207
+ appointmentId: z.string(),
208
+ patientId: z.string(),
209
+ practitionerId: z.string(),
210
+ clinicId: z.string(),
211
+ values: z.record(z.string(), z.any()).optional().default({}),
212
+ status: filledDocumentStatusSchema
213
+ .optional()
214
+ .default(FilledDocumentStatus.DRAFT),
215
+ });
216
+
217
+ export const updateFilledDocumentDataSchema = z.object({
218
+ values: z.record(z.string(), z.any()).optional(),
219
+ status: filledDocumentStatusSchema.optional(),
165
220
  });
@@ -1,15 +1,10 @@
1
- import {
2
- createDocumentTemplateSchema,
3
- updateDocumentTemplateSchema,
4
- documentTemplateSchema,
5
- documentElementSchema,
6
- documentElementWithoutIdSchema,
7
- } from "./documentation-templates/template.schema";
1
+ /**
2
+ * This file re-exports all schemas related to documentation templates and filled documents
3
+ * from their specific definition files.
4
+ */
8
5
 
9
- export {
10
- createDocumentTemplateSchema,
11
- updateDocumentTemplateSchema,
12
- documentTemplateSchema,
13
- documentElementSchema,
14
- documentElementWithoutIdSchema,
15
- };
6
+ // Assuming all relevant schemas, including filledDocumentStatusSchema,
7
+ // documentTemplateSchema, createDocumentTemplateSchema, updateDocumentTemplateSchema,
8
+ // filledDocumentSchema, createFilledDocumentDataSchema, updateFilledDocumentDataSchema etc.,
9
+ // are defined and exported from './documentation-templates/template.schema.ts'
10
+ export * from "./documentation-templates/template.schema";
@@ -24,7 +24,7 @@ export const baseNotificationSchema = z.object({
24
24
  * Validaciona šema za pre-requirement notifikaciju
25
25
  */
26
26
  export const preRequirementNotificationSchema = baseNotificationSchema.extend({
27
- notificationType: z.literal(NotificationType.PRE_REQUIREMENT),
27
+ notificationType: z.literal(NotificationType.REQUIREMENT_INSTRUCTION_DUE),
28
28
  treatmentId: z.string(),
29
29
  requirements: z.array(z.string()),
30
30
  deadline: z.any(), // Timestamp
@@ -34,7 +34,7 @@ export const preRequirementNotificationSchema = baseNotificationSchema.extend({
34
34
  * Validaciona šema za post-requirement notifikaciju
35
35
  */
36
36
  export const postRequirementNotificationSchema = baseNotificationSchema.extend({
37
- notificationType: z.literal(NotificationType.POST_REQUIREMENT),
37
+ notificationType: z.literal(NotificationType.REQUIREMENT_INSTRUCTION_DUE),
38
38
  treatmentId: z.string(),
39
39
  requirements: z.array(z.string()),
40
40
  deadline: z.any(), // Timestamp
@@ -56,7 +56,7 @@ export const appointmentReminderNotificationSchema =
56
56
  * Validaciona šema za appointment notifikaciju
57
57
  */
58
58
  export const appointmentNotificationSchema = baseNotificationSchema.extend({
59
- notificationType: z.literal(NotificationType.APPOINTMENT_NOTIFICATION),
59
+ notificationType: z.literal(NotificationType.APPOINTMENT_STATUS_CHANGE),
60
60
  appointmentId: z.string(),
61
61
  appointmentStatus: z.string(),
62
62
  previousStatus: z.string(),
@@ -0,0 +1,75 @@
1
+ import { z } from "zod";
2
+ import {
3
+ PatientInstructionStatus,
4
+ PatientRequirementOverallStatus,
5
+ } from "../../types/patient/patient-requirements";
6
+ import {
7
+ requirementTypeSchema,
8
+ timeUnitSchema, // Corrected import name to lowercase 'u'
9
+ } from "../../backoffice/validations/schemas";
10
+
11
+ /**
12
+ * Zod schema for `PatientInstructionStatus` enum.
13
+ */
14
+ export const patientInstructionStatusSchema = z.nativeEnum(
15
+ PatientInstructionStatus
16
+ );
17
+
18
+ /**
19
+ * Zod schema for `PatientRequirementInstruction` interface.
20
+ */
21
+ export const patientRequirementInstructionSchema = z.object({
22
+ instructionId: z.string().min(1, "Instruction ID is required."),
23
+ instructionText: z.string().min(1, "Instruction text is required."),
24
+ dueTime: z.any(), // Firestore Timestamp
25
+ actionableWindow: z.number().default(1),
26
+ status: patientInstructionStatusSchema,
27
+ originalNotifyAtValue: z.number(),
28
+ originalTimeframeUnit: timeUnitSchema, // Use the correctly imported timeUnitSchema
29
+ notificationId: z.string().optional(),
30
+ actionTakenAt: z.any().optional().nullable(), // Firestore Timestamp or null
31
+ updatedAt: z.any(), // Firestore Timestamp
32
+ });
33
+
34
+ /**
35
+ * Zod schema for `PatientRequirementOverallStatus` enum.
36
+ */
37
+ export const patientRequirementOverallStatusSchema = z.nativeEnum(
38
+ PatientRequirementOverallStatus
39
+ );
40
+
41
+ /**
42
+ * Define requirementImportanceSchema locally as it's not exported directly from backoffice.
43
+ */
44
+ export const requirementImportanceSchema = z.enum(["low", "medium", "high"]);
45
+
46
+ /**
47
+ * Zod schema for `PatientRequirementInstance` interface.
48
+ */
49
+ export const patientRequirementInstanceSchema = z.object({
50
+ id: z.string().min(1, "Instance ID is required."),
51
+ patientId: z.string().min(1, "Patient ID is required."),
52
+ appointmentId: z.string().min(1, "Appointment ID is required."),
53
+ originalRequirementId: z
54
+ .string()
55
+ .min(1, "Original Requirement ID is required."),
56
+ requirementType: requirementTypeSchema,
57
+ requirementName: z.string().min(1, "Requirement name is required."),
58
+ requirementDescription: z.string(),
59
+ requirementImportance: requirementImportanceSchema, // Use the locally defined schema
60
+ overallStatus: patientRequirementOverallStatusSchema,
61
+ instructions: z
62
+ .array(patientRequirementInstructionSchema)
63
+ .min(1, "At least one instruction is required."),
64
+ createdAt: z.any(), // Firestore Timestamp
65
+ updatedAt: z.any(), // Firestore Timestamp
66
+ });
67
+
68
+ /**
69
+ * Schema for updating a `PatientRequirementInstruction` status (subset of fields).
70
+ * Useful for when a patient marks an instruction as actioned.
71
+ */
72
+ export const updatePatientInstructionStatusSchema = z.object({
73
+ status: z.literal(PatientInstructionStatus.ACTION_TAKEN),
74
+ actionTakenAt: z.any(), // Should be a Timestamp from the server
75
+ });
@@ -24,6 +24,7 @@ export const createProcedureSchema = z.object({
24
24
  duration: z.number().min(1).max(480), // Max 8 hours
25
25
  practitionerId: z.string().min(1),
26
26
  clinicBranchId: z.string().min(1),
27
+ photos: z.array(z.string()).optional(),
27
28
  });
28
29
 
29
30
  /**
@@ -43,6 +44,7 @@ export const updateProcedureSchema = z.object({
43
44
  technologyId: z.string().optional(),
44
45
  productId: z.string().optional(),
45
46
  clinicBranchId: z.string().optional(),
47
+ photos: z.array(z.string()).optional(),
46
48
  });
47
49
 
48
50
  /**
@@ -55,6 +57,7 @@ export const procedureSchema = createProcedureSchema.extend({
55
57
  technology: z.any(), // We'll validate the full technology object separately
56
58
  product: z.any(), // We'll validate the full product object separately
57
59
  blockingConditions: z.array(z.any()), // We'll validate blocking conditions separately
60
+ contraindications: z.array(z.any()), // We'll validate contraindications separately
58
61
  treatmentBenefits: z.array(z.any()), // We'll validate treatment benefits separately
59
62
  preRequirements: z.array(z.any()), // We'll validate requirements separately
60
63
  postRequirements: z.array(z.any()), // We'll validate requirements separately