@blackcode_sa/metaestetics-api 1.6.5 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.mts +195 -172
- package/dist/admin/index.d.ts +195 -172
- package/dist/admin/index.js +8928 -8364
- package/dist/admin/index.mjs +8920 -8356
- package/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +3 -0
- package/dist/index.mjs +3 -0
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/README.md +128 -0
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +950 -218
- package/src/admin/booking/README.md +125 -0
- package/src/admin/booking/booking.admin.ts +288 -26
- package/src/admin/calendar/calendar.admin.service.ts +183 -0
- package/src/admin/documentation-templates/document-manager.admin.ts +131 -0
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +264 -0
- package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -0
- package/src/admin/mailing/base.mailing.service.ts +1 -1
- package/src/admin/mailing/index.ts +2 -0
- package/src/admin/notifications/notifications.admin.ts +397 -1
- package/src/types/appointment/index.ts +1 -0
- package/src/types/notifications/index.ts +4 -2
- package/src/validations/appointment.schema.ts +1 -0
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Notification,
|
|
3
|
+
NotificationStatus,
|
|
4
|
+
NotificationType,
|
|
5
|
+
} from "../../types/notifications";
|
|
2
6
|
import * as admin from "firebase-admin";
|
|
3
7
|
import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";
|
|
8
|
+
import { Appointment, PaymentStatus } from "../../types/appointment";
|
|
9
|
+
import { UserRole } from "../../types";
|
|
10
|
+
import { Timestamp as FirebaseClientTimestamp } from "@firebase/firestore";
|
|
4
11
|
|
|
5
12
|
export class NotificationsAdmin {
|
|
6
13
|
private expo: Expo;
|
|
@@ -222,4 +229,393 @@ export class NotificationsAdmin {
|
|
|
222
229
|
);
|
|
223
230
|
}
|
|
224
231
|
}
|
|
232
|
+
|
|
233
|
+
// --- Business Specific Notification Methods (Revised) ---
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Creates and potentially sends a push notification for a confirmed appointment.
|
|
237
|
+
* @param appointment The confirmed appointment object.
|
|
238
|
+
* @param recipientUserId The ID of the user receiving the notification.
|
|
239
|
+
* @param recipientExpoTokens Array of Expo push tokens for the recipient.
|
|
240
|
+
* @param recipientRole The role of the recipient (e.g., PATIENT, PRACTITIONER).
|
|
241
|
+
*/
|
|
242
|
+
async sendAppointmentConfirmedPush(
|
|
243
|
+
appointment: Appointment,
|
|
244
|
+
recipientUserId: string,
|
|
245
|
+
recipientExpoTokens: string[],
|
|
246
|
+
recipientRole: UserRole
|
|
247
|
+
): Promise<string | null> {
|
|
248
|
+
if (recipientRole === UserRole.CLINIC_ADMIN) {
|
|
249
|
+
console.log(
|
|
250
|
+
`[NotificationsAdmin] Clinic admin roles do not receive push notifications for appointment ${appointment.id} confirmation. Skipping.`
|
|
251
|
+
);
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
if (!recipientExpoTokens || recipientExpoTokens.length === 0) {
|
|
255
|
+
console.log(
|
|
256
|
+
`[NotificationsAdmin] No expo tokens for ${recipientRole} ${recipientUserId} for appointment ${appointment.id} confirmation. Skipping push.`
|
|
257
|
+
);
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let title = "Appointment Confirmed!";
|
|
262
|
+
let body = `Your appointment for ${
|
|
263
|
+
appointment.procedureInfo.name
|
|
264
|
+
} on ${appointment.appointmentStartTime
|
|
265
|
+
.toDate()
|
|
266
|
+
.toLocaleDateString()} at ${appointment.appointmentStartTime
|
|
267
|
+
.toDate()
|
|
268
|
+
.toLocaleTimeString()} has been confirmed.`;
|
|
269
|
+
|
|
270
|
+
if (recipientRole === UserRole.PRACTITIONER) {
|
|
271
|
+
title = "New Appointment Confirmed";
|
|
272
|
+
body = `Appointment for ${appointment.procedureInfo.name} with ${
|
|
273
|
+
appointment.patientInfo.fullName
|
|
274
|
+
} on ${appointment.appointmentStartTime
|
|
275
|
+
.toDate()
|
|
276
|
+
.toLocaleDateString()} is confirmed.`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
280
|
+
const clientCompatibleNotificationTime = new FirebaseClientTimestamp(
|
|
281
|
+
adminTsNow.seconds,
|
|
282
|
+
adminTsNow.nanoseconds
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const notificationData: Omit<
|
|
286
|
+
Notification,
|
|
287
|
+
"id" | "createdAt" | "updatedAt" | "status" | "isRead"
|
|
288
|
+
> = {
|
|
289
|
+
userId: recipientUserId,
|
|
290
|
+
userRole: recipientRole,
|
|
291
|
+
notificationType: NotificationType.APPOINTMENT_STATUS_CHANGE,
|
|
292
|
+
notificationTime: clientCompatibleNotificationTime,
|
|
293
|
+
notificationTokens: recipientExpoTokens,
|
|
294
|
+
title,
|
|
295
|
+
body,
|
|
296
|
+
appointmentId: appointment.id,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const notificationId = await this.createNotification(
|
|
301
|
+
notificationData as Notification
|
|
302
|
+
);
|
|
303
|
+
console.log(
|
|
304
|
+
`[NotificationsAdmin] Created APPOINTMENT_STATUS_CHANGE (Confirmed) notification ${notificationId} for ${recipientRole} ${recipientUserId}.`
|
|
305
|
+
);
|
|
306
|
+
return notificationId;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error(
|
|
309
|
+
`[NotificationsAdmin] Error creating APPOINTMENT_STATUS_CHANGE (Confirmed) notification for ${recipientRole} ${recipientUserId}:`,
|
|
310
|
+
error
|
|
311
|
+
);
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async sendAppointmentCancelledPush(
|
|
317
|
+
appointment: Appointment,
|
|
318
|
+
recipientUserId: string,
|
|
319
|
+
recipientExpoTokens: string[],
|
|
320
|
+
recipientRole: UserRole
|
|
321
|
+
): Promise<string | null> {
|
|
322
|
+
if (recipientRole === UserRole.CLINIC_ADMIN) {
|
|
323
|
+
console.log(
|
|
324
|
+
`[NotificationsAdmin] Clinic admin roles do not receive push notifications for appointment ${appointment.id} cancellation. Skipping.`
|
|
325
|
+
);
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
if (!recipientExpoTokens || recipientExpoTokens.length === 0) {
|
|
329
|
+
console.log(
|
|
330
|
+
`[NotificationsAdmin] No expo tokens for ${recipientRole} ${recipientUserId} for appointment ${appointment.id} cancellation. Skipping push.`
|
|
331
|
+
);
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let title = "Appointment Cancelled";
|
|
336
|
+
let body = `Your appointment for ${
|
|
337
|
+
appointment.procedureInfo.name
|
|
338
|
+
} on ${appointment.appointmentStartTime
|
|
339
|
+
.toDate()
|
|
340
|
+
.toLocaleDateString()} has been cancelled.`;
|
|
341
|
+
if (appointment.cancellationReason) {
|
|
342
|
+
body += ` Reason: ${appointment.cancellationReason}`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (recipientRole === UserRole.PRACTITIONER) {
|
|
346
|
+
body = `The appointment for ${appointment.procedureInfo.name} with ${
|
|
347
|
+
appointment.patientInfo.fullName
|
|
348
|
+
} on ${appointment.appointmentStartTime
|
|
349
|
+
.toDate()
|
|
350
|
+
.toLocaleDateString()} has been cancelled.`;
|
|
351
|
+
if (appointment.cancellationReason) {
|
|
352
|
+
body += ` Reason: ${appointment.cancellationReason}`;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
357
|
+
const clientCompatibleNotificationTime = new FirebaseClientTimestamp(
|
|
358
|
+
adminTsNow.seconds,
|
|
359
|
+
adminTsNow.nanoseconds
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
const notificationData: Omit<
|
|
363
|
+
Notification,
|
|
364
|
+
"id" | "createdAt" | "updatedAt" | "status" | "isRead"
|
|
365
|
+
> = {
|
|
366
|
+
userId: recipientUserId,
|
|
367
|
+
userRole: recipientRole,
|
|
368
|
+
notificationType: NotificationType.APPOINTMENT_CANCELLED,
|
|
369
|
+
notificationTime: clientCompatibleNotificationTime,
|
|
370
|
+
notificationTokens: recipientExpoTokens,
|
|
371
|
+
title,
|
|
372
|
+
body,
|
|
373
|
+
appointmentId: appointment.id,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const notificationId = await this.createNotification(
|
|
378
|
+
notificationData as Notification
|
|
379
|
+
);
|
|
380
|
+
console.log(
|
|
381
|
+
`[NotificationsAdmin] Created APPOINTMENT_CANCELLED notification ${notificationId} for ${recipientRole} ${recipientUserId}.`
|
|
382
|
+
);
|
|
383
|
+
return notificationId;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error(
|
|
386
|
+
`[NotificationsAdmin] Error creating APPOINTMENT_CANCELLED notification for ${recipientRole} ${recipientUserId}:`,
|
|
387
|
+
error
|
|
388
|
+
);
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async sendAppointmentRescheduledProposalPush(
|
|
394
|
+
appointment: Appointment,
|
|
395
|
+
patientUserId: string,
|
|
396
|
+
patientExpoTokens: string[]
|
|
397
|
+
): Promise<string | null> {
|
|
398
|
+
if (!patientExpoTokens || patientExpoTokens.length === 0) {
|
|
399
|
+
console.log(
|
|
400
|
+
`[NotificationsAdmin] No expo tokens for patient ${patientUserId} for appointment ${appointment.id} reschedule proposal. Skipping push.`
|
|
401
|
+
);
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const title = "Appointment Reschedule Proposed";
|
|
406
|
+
const body = `Action Required: A new time has been proposed for your appointment for ${appointment.procedureInfo.name}. Please review in the app.`;
|
|
407
|
+
|
|
408
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
409
|
+
const clientCompatibleNotificationTime = new FirebaseClientTimestamp(
|
|
410
|
+
adminTsNow.seconds,
|
|
411
|
+
adminTsNow.nanoseconds
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const notificationData: Omit<
|
|
415
|
+
Notification,
|
|
416
|
+
"id" | "createdAt" | "updatedAt" | "status" | "isRead"
|
|
417
|
+
> = {
|
|
418
|
+
userId: patientUserId,
|
|
419
|
+
userRole: UserRole.PATIENT,
|
|
420
|
+
notificationType: NotificationType.APPOINTMENT_RESCHEDULED_PROPOSAL,
|
|
421
|
+
notificationTime: clientCompatibleNotificationTime,
|
|
422
|
+
notificationTokens: patientExpoTokens,
|
|
423
|
+
title,
|
|
424
|
+
body,
|
|
425
|
+
appointmentId: appointment.id,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
const notificationId = await this.createNotification(
|
|
430
|
+
notificationData as Notification
|
|
431
|
+
);
|
|
432
|
+
console.log(
|
|
433
|
+
`[NotificationsAdmin] Created APPOINTMENT_RESCHEDULED_PROPOSAL notification ${notificationId} for patient ${patientUserId}.`
|
|
434
|
+
);
|
|
435
|
+
return notificationId;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.error(
|
|
438
|
+
`[NotificationsAdmin] Error creating APPOINTMENT_RESCHEDULED_PROPOSAL notification for patient ${patientUserId}:`,
|
|
439
|
+
error
|
|
440
|
+
);
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async sendPaymentUpdatePush(
|
|
446
|
+
appointment: Appointment,
|
|
447
|
+
patientUserId: string,
|
|
448
|
+
patientExpoTokens: string[]
|
|
449
|
+
): Promise<string | null> {
|
|
450
|
+
if (!patientExpoTokens || patientExpoTokens.length === 0) {
|
|
451
|
+
console.log(
|
|
452
|
+
`[NotificationsAdmin] No expo tokens for patient ${patientUserId} for appointment ${appointment.id} payment update. Skipping push.`
|
|
453
|
+
);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
const title = "Payment Updated";
|
|
457
|
+
const body = `Your payment status for the appointment (${
|
|
458
|
+
appointment.procedureInfo.name
|
|
459
|
+
}) on ${appointment.appointmentStartTime
|
|
460
|
+
.toDate()
|
|
461
|
+
.toLocaleDateString()} is now ${appointment.paymentStatus}.`;
|
|
462
|
+
|
|
463
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
464
|
+
const clientCompatibleNotificationTime = new FirebaseClientTimestamp(
|
|
465
|
+
adminTsNow.seconds,
|
|
466
|
+
adminTsNow.nanoseconds
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
const notificationType =
|
|
470
|
+
appointment.paymentStatus === PaymentStatus.PAID
|
|
471
|
+
? NotificationType.PAYMENT_CONFIRMATION
|
|
472
|
+
: NotificationType.GENERAL_MESSAGE;
|
|
473
|
+
|
|
474
|
+
const notificationData: Omit<
|
|
475
|
+
Notification,
|
|
476
|
+
"id" | "createdAt" | "updatedAt" | "status" | "isRead"
|
|
477
|
+
> = {
|
|
478
|
+
userId: patientUserId,
|
|
479
|
+
userRole: UserRole.PATIENT,
|
|
480
|
+
notificationType: notificationType,
|
|
481
|
+
notificationTime: clientCompatibleNotificationTime,
|
|
482
|
+
notificationTokens: patientExpoTokens,
|
|
483
|
+
title,
|
|
484
|
+
body,
|
|
485
|
+
appointmentId: appointment.id,
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
try {
|
|
489
|
+
const notificationId = await this.createNotification(
|
|
490
|
+
notificationData as Notification
|
|
491
|
+
);
|
|
492
|
+
console.log(
|
|
493
|
+
`[NotificationsAdmin] Created PAYMENT_UPDATE notification ${notificationId} for patient ${patientUserId}.`
|
|
494
|
+
);
|
|
495
|
+
return notificationId;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
console.error(
|
|
498
|
+
`[NotificationsAdmin] Error creating PAYMENT_UPDATE notification for patient ${patientUserId}:`,
|
|
499
|
+
error
|
|
500
|
+
);
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async sendReviewRequestPush(
|
|
506
|
+
appointment: Appointment,
|
|
507
|
+
patientUserId: string,
|
|
508
|
+
patientExpoTokens: string[]
|
|
509
|
+
): Promise<string | null> {
|
|
510
|
+
if (!patientExpoTokens || patientExpoTokens.length === 0) {
|
|
511
|
+
console.log(
|
|
512
|
+
`[NotificationsAdmin] No expo tokens for patient ${patientUserId} for appointment ${appointment.id} review request. Skipping push.`
|
|
513
|
+
);
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const title = "Leave a Review";
|
|
518
|
+
const body = `How was your recent appointment for ${appointment.procedureInfo.name}? We'd love to hear your feedback!`;
|
|
519
|
+
|
|
520
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
521
|
+
const clientCompatibleNotificationTime = new FirebaseClientTimestamp(
|
|
522
|
+
adminTsNow.seconds,
|
|
523
|
+
adminTsNow.nanoseconds
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
const notificationData: Omit<
|
|
527
|
+
Notification,
|
|
528
|
+
"id" | "createdAt" | "updatedAt" | "status" | "isRead"
|
|
529
|
+
> = {
|
|
530
|
+
userId: patientUserId,
|
|
531
|
+
userRole: UserRole.PATIENT,
|
|
532
|
+
notificationType: NotificationType.REVIEW_REQUEST,
|
|
533
|
+
notificationTime: clientCompatibleNotificationTime,
|
|
534
|
+
notificationTokens: patientExpoTokens,
|
|
535
|
+
title,
|
|
536
|
+
body,
|
|
537
|
+
appointmentId: appointment.id,
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
try {
|
|
541
|
+
const notificationId = await this.createNotification(
|
|
542
|
+
notificationData as Notification
|
|
543
|
+
);
|
|
544
|
+
console.log(
|
|
545
|
+
`[NotificationsAdmin] Created REVIEW_REQUEST notification ${notificationId} for patient ${patientUserId}.`
|
|
546
|
+
);
|
|
547
|
+
return notificationId;
|
|
548
|
+
} catch (error) {
|
|
549
|
+
console.error(
|
|
550
|
+
`[NotificationsAdmin] Error creating REVIEW_REQUEST notification for patient ${patientUserId}:`,
|
|
551
|
+
error
|
|
552
|
+
);
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async sendReviewAddedPush(
|
|
558
|
+
appointment: Appointment,
|
|
559
|
+
recipientUserId: string,
|
|
560
|
+
recipientExpoTokens: string[],
|
|
561
|
+
recipientRole: UserRole
|
|
562
|
+
): Promise<string | null> {
|
|
563
|
+
if (recipientRole !== UserRole.PRACTITIONER) {
|
|
564
|
+
console.log(
|
|
565
|
+
`[NotificationsAdmin] Only Practitioners receive review added push notifications (Role: ${recipientRole}). Skipping for appointment ${appointment.id}.`
|
|
566
|
+
);
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
if (!recipientExpoTokens || recipientExpoTokens.length === 0) {
|
|
570
|
+
console.log(
|
|
571
|
+
`[NotificationsAdmin] No expo tokens for practitioner ${recipientUserId} for appointment ${appointment.id} review added. Skipping push.`
|
|
572
|
+
);
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const title = "New Review Received";
|
|
577
|
+
const body = `A new review has been added by ${
|
|
578
|
+
appointment.patientInfo.fullName
|
|
579
|
+
} for your appointment on ${appointment.appointmentStartTime
|
|
580
|
+
.toDate()
|
|
581
|
+
.toLocaleDateString()}.`;
|
|
582
|
+
|
|
583
|
+
const adminTsNow = admin.firestore.Timestamp.now();
|
|
584
|
+
const clientCompatibleNotificationTime = new FirebaseClientTimestamp(
|
|
585
|
+
adminTsNow.seconds,
|
|
586
|
+
adminTsNow.nanoseconds
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
const tempNotificationType = NotificationType.GENERAL_MESSAGE;
|
|
590
|
+
|
|
591
|
+
const notificationData: Omit<
|
|
592
|
+
Notification,
|
|
593
|
+
"id" | "createdAt" | "updatedAt" | "status" | "isRead"
|
|
594
|
+
> = {
|
|
595
|
+
userId: recipientUserId,
|
|
596
|
+
userRole: UserRole.PRACTITIONER,
|
|
597
|
+
notificationType: tempNotificationType,
|
|
598
|
+
notificationTime: clientCompatibleNotificationTime,
|
|
599
|
+
notificationTokens: recipientExpoTokens,
|
|
600
|
+
title,
|
|
601
|
+
body,
|
|
602
|
+
appointmentId: appointment.id,
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const notificationId = await this.createNotification(
|
|
607
|
+
notificationData as Notification
|
|
608
|
+
);
|
|
609
|
+
console.log(
|
|
610
|
+
`[NotificationsAdmin] Created REVIEW_ADDED (using temp type ${tempNotificationType}) notification ${notificationId} for practitioner ${recipientUserId}.`
|
|
611
|
+
);
|
|
612
|
+
return notificationId;
|
|
613
|
+
} catch (error) {
|
|
614
|
+
console.error(
|
|
615
|
+
`[NotificationsAdmin] Error creating REVIEW_ADDED (using temp type ${tempNotificationType}) notification for practitioner ${recipientUserId}:`,
|
|
616
|
+
error
|
|
617
|
+
);
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
225
621
|
}
|
|
@@ -93,6 +93,7 @@ export interface LinkedFormInfo {
|
|
|
93
93
|
templateVersion: number;
|
|
94
94
|
title: string; // For display, usually from DocumentTemplate.title
|
|
95
95
|
isUserForm: boolean;
|
|
96
|
+
isRequired?: boolean;
|
|
96
97
|
status: FilledDocumentStatus; // Status of the filled form (e.g., draft, completed, signed)
|
|
97
98
|
path: string; // Full Firestore path to the filled document (e.g., appointments/{aid}/user-forms/{fid})
|
|
98
99
|
submittedAt?: Timestamp;
|
|
@@ -11,6 +11,8 @@ export enum NotificationType {
|
|
|
11
11
|
APPOINTMENT_CANCELLED = "appointmentCancelled", // When an appointment is cancelled
|
|
12
12
|
|
|
13
13
|
// --- Requirement-Driven Instructions ---
|
|
14
|
+
PRE_REQUIREMENT_INSTRUCTION_DUE = "preRequirementInstructionDue",
|
|
15
|
+
POST_REQUIREMENT_INSTRUCTION_DUE = "postRequirementInstructionDue",
|
|
14
16
|
REQUIREMENT_INSTRUCTION_DUE = "requirementInstructionDue", // For specific pre/post care instructions
|
|
15
17
|
|
|
16
18
|
// --- Form Related ---
|
|
@@ -106,7 +108,7 @@ export enum NotificationStatus {
|
|
|
106
108
|
* Notifikacija za pre-requirement
|
|
107
109
|
*/
|
|
108
110
|
export interface PreRequirementNotification extends BaseNotification {
|
|
109
|
-
notificationType: NotificationType.
|
|
111
|
+
notificationType: NotificationType.PRE_REQUIREMENT_INSTRUCTION_DUE;
|
|
110
112
|
/** ID tretmana za koji je vezan pre-requirement */
|
|
111
113
|
treatmentId: string;
|
|
112
114
|
/** Lista pre-requirements koji treba da se ispune */
|
|
@@ -119,7 +121,7 @@ export interface PreRequirementNotification extends BaseNotification {
|
|
|
119
121
|
* Notifikacija za post-requirement
|
|
120
122
|
*/
|
|
121
123
|
export interface PostRequirementNotification extends BaseNotification {
|
|
122
|
-
notificationType: NotificationType.
|
|
124
|
+
notificationType: NotificationType.POST_REQUIREMENT_INSTRUCTION_DUE;
|
|
123
125
|
/** ID tretmana za koji je vezan post-requirement */
|
|
124
126
|
treatmentId: string;
|
|
125
127
|
/** Lista post-requirements koji treba da se ispune */
|
|
@@ -70,6 +70,7 @@ export const linkedFormInfoSchema = z.object({
|
|
|
70
70
|
.positive("Template version must be a positive integer"),
|
|
71
71
|
title: z.string().min(MIN_STRING_LENGTH, "Form title is required"),
|
|
72
72
|
isUserForm: z.boolean(),
|
|
73
|
+
isRequired: z.boolean().optional(),
|
|
73
74
|
status: filledDocumentStatusSchema,
|
|
74
75
|
path: z.string().min(MIN_STRING_LENGTH, "Form path is required"),
|
|
75
76
|
submittedAt: z
|