@blackcode_sa/metaestetics-api 1.11.0 → 1.11.2

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.
@@ -1516,7 +1516,8 @@ interface BeforeAfterPerZone {
1516
1516
  /** URL for after photo or null if not available */
1517
1517
  after: MediaResource | null;
1518
1518
  /** Optional note for the zone */
1519
- note: string | null;
1519
+ afterNote?: string | null;
1520
+ beforeNote?: string | null;
1520
1521
  }
1521
1522
  /**
1522
1523
  * Interface for billing information per zone
@@ -1538,6 +1539,8 @@ interface BillingPerZone {
1538
1539
  Subtotal: number;
1539
1540
  /** Optional billing note */
1540
1541
  Note: string | null;
1542
+ /** Ion/Batch number for traceability */
1543
+ IonNumber: string | null;
1541
1544
  }
1542
1545
  /**
1543
1546
  * Interface for final billing calculations of the appointment
@@ -1612,7 +1615,7 @@ interface Appointment {
1612
1615
  actualDurationMinutes?: number;
1613
1616
  /** Cancellation Details */
1614
1617
  cancellationReason?: string | null;
1615
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
1618
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
1616
1619
  /** Notes */
1617
1620
  internalNotes?: string | null;
1618
1621
  patientNotes?: string | null;
@@ -1516,7 +1516,8 @@ interface BeforeAfterPerZone {
1516
1516
  /** URL for after photo or null if not available */
1517
1517
  after: MediaResource | null;
1518
1518
  /** Optional note for the zone */
1519
- note: string | null;
1519
+ afterNote?: string | null;
1520
+ beforeNote?: string | null;
1520
1521
  }
1521
1522
  /**
1522
1523
  * Interface for billing information per zone
@@ -1538,6 +1539,8 @@ interface BillingPerZone {
1538
1539
  Subtotal: number;
1539
1540
  /** Optional billing note */
1540
1541
  Note: string | null;
1542
+ /** Ion/Batch number for traceability */
1543
+ IonNumber: string | null;
1541
1544
  }
1542
1545
  /**
1543
1546
  * Interface for final billing calculations of the appointment
@@ -1612,7 +1615,7 @@ interface Appointment {
1612
1615
  actualDurationMinutes?: number;
1613
1616
  /** Cancellation Details */
1614
1617
  cancellationReason?: string | null;
1615
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
1618
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
1616
1619
  /** Notes */
1617
1620
  internalNotes?: string | null;
1618
1621
  patientNotes?: string | null;
package/dist/index.d.mts CHANGED
@@ -2830,7 +2830,8 @@ interface BeforeAfterPerZone {
2830
2830
  /** URL for after photo or null if not available */
2831
2831
  after: MediaResource | null;
2832
2832
  /** Optional note for the zone */
2833
- note: string | null;
2833
+ afterNote?: string | null;
2834
+ beforeNote?: string | null;
2834
2835
  }
2835
2836
  /**
2836
2837
  * Interface for billing information per zone
@@ -2852,6 +2853,8 @@ interface BillingPerZone {
2852
2853
  Subtotal: number;
2853
2854
  /** Optional billing note */
2854
2855
  Note: string | null;
2856
+ /** Ion/Batch number for traceability */
2857
+ IonNumber: string | null;
2855
2858
  }
2856
2859
  /**
2857
2860
  * Interface for final billing calculations of the appointment
@@ -2926,7 +2929,7 @@ interface Appointment {
2926
2929
  actualDurationMinutes?: number;
2927
2930
  /** Cancellation Details */
2928
2931
  cancellationReason?: string | null;
2929
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
2932
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
2930
2933
  /** Notes */
2931
2934
  internalNotes?: string | null;
2932
2935
  patientNotes?: string | null;
@@ -3006,7 +3009,7 @@ interface UpdateAppointmentData {
3006
3009
  procedureActualStartTime?: Timestamp | FieldValue | null;
3007
3010
  actualDurationMinutes?: number;
3008
3011
  cancellationReason?: string | null;
3009
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
3012
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
3010
3013
  internalNotes?: string | null;
3011
3014
  patientNotes?: string | FieldValue | null;
3012
3015
  paymentStatus?: PaymentStatus;
package/dist/index.d.ts CHANGED
@@ -2830,7 +2830,8 @@ interface BeforeAfterPerZone {
2830
2830
  /** URL for after photo or null if not available */
2831
2831
  after: MediaResource | null;
2832
2832
  /** Optional note for the zone */
2833
- note: string | null;
2833
+ afterNote?: string | null;
2834
+ beforeNote?: string | null;
2834
2835
  }
2835
2836
  /**
2836
2837
  * Interface for billing information per zone
@@ -2852,6 +2853,8 @@ interface BillingPerZone {
2852
2853
  Subtotal: number;
2853
2854
  /** Optional billing note */
2854
2855
  Note: string | null;
2856
+ /** Ion/Batch number for traceability */
2857
+ IonNumber: string | null;
2855
2858
  }
2856
2859
  /**
2857
2860
  * Interface for final billing calculations of the appointment
@@ -2926,7 +2929,7 @@ interface Appointment {
2926
2929
  actualDurationMinutes?: number;
2927
2930
  /** Cancellation Details */
2928
2931
  cancellationReason?: string | null;
2929
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
2932
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
2930
2933
  /** Notes */
2931
2934
  internalNotes?: string | null;
2932
2935
  patientNotes?: string | null;
@@ -3006,7 +3009,7 @@ interface UpdateAppointmentData {
3006
3009
  procedureActualStartTime?: Timestamp | FieldValue | null;
3007
3010
  actualDurationMinutes?: number;
3008
3011
  cancellationReason?: string | null;
3009
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
3012
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
3010
3013
  internalNotes?: string | null;
3011
3014
  patientNotes?: string | FieldValue | null;
3012
3015
  paymentStatus?: PaymentStatus;
package/dist/index.js CHANGED
@@ -539,7 +539,8 @@ var finalizedDetailsSchema = import_zod3.z.object({
539
539
  var beforeAfterPerZoneSchema = import_zod3.z.object({
540
540
  before: mediaResourceSchema.nullable(),
541
541
  after: mediaResourceSchema.nullable(),
542
- note: import_zod3.z.string().nullable()
542
+ afterNote: import_zod3.z.string().nullable().optional(),
543
+ beforeNote: import_zod3.z.string().nullable().optional()
543
544
  });
544
545
  var billingPerZoneSchema = import_zod3.z.object({
545
546
  Product: import_zod3.z.string().min(MIN_STRING_LENGTH, "Product name is required"),
@@ -549,7 +550,8 @@ var billingPerZoneSchema = import_zod3.z.object({
549
550
  UnitPrice: import_zod3.z.number().min(0, "Unit price must be non-negative"),
550
551
  UnitCurency: import_zod3.z.nativeEnum(Currency),
551
552
  Subtotal: import_zod3.z.number().min(0, "Subtotal must be non-negative"),
552
- Note: import_zod3.z.string().nullable()
553
+ Note: import_zod3.z.string().nullable(),
554
+ IonNumber: import_zod3.z.string().nullable()
553
555
  });
554
556
  var finalBillingSchema = import_zod3.z.object({
555
557
  subtotalAll: import_zod3.z.number().min(0, "Subtotal all must be non-negative"),
@@ -620,10 +622,7 @@ var updateAppointmentSchema = import_zod3.z.object({
620
622
  practitionerId: import_zod3.z.string().min(MIN_STRING_LENGTH).optional(),
621
623
  clinic_tz: import_zod3.z.string().min(MIN_STRING_LENGTH).optional(),
622
624
  linkedForms: import_zod3.z.union([import_zod3.z.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), import_zod3.z.any()]).optional(),
623
- media: import_zod3.z.union([
624
- import_zod3.z.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
625
- import_zod3.z.any()
626
- ]).optional(),
625
+ media: import_zod3.z.union([import_zod3.z.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH), import_zod3.z.any()]).optional(),
627
626
  reviewInfo: import_zod3.z.union([patientReviewInfoSchema.nullable(), import_zod3.z.any()]).optional(),
628
627
  finalizedDetails: import_zod3.z.union([finalizedDetailsSchema.nullable(), import_zod3.z.any()]).optional(),
629
628
  isArchived: import_zod3.z.boolean().optional(),
@@ -664,10 +663,7 @@ var searchAppointmentsSchema = import_zod3.z.object({
664
663
  (val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || (val == null ? void 0 : val.seconds) !== void 0 || typeof val === "number" || typeof val === "string" || val && typeof val.toMillis === "function",
665
664
  "End date must be a valid timestamp or Date object"
666
665
  ).optional(),
667
- status: import_zod3.z.union([
668
- appointmentStatusSchema,
669
- import_zod3.z.array(appointmentStatusSchema).nonempty()
670
- ]).optional(),
666
+ status: import_zod3.z.union([appointmentStatusSchema, import_zod3.z.array(appointmentStatusSchema).nonempty()]).optional(),
671
667
  limit: import_zod3.z.number().positive().int().optional().default(20),
672
668
  startAfter: import_zod3.z.any().optional()
673
669
  }).refine(
package/dist/index.mjs CHANGED
@@ -411,7 +411,8 @@ var finalizedDetailsSchema = z3.object({
411
411
  var beforeAfterPerZoneSchema = z3.object({
412
412
  before: mediaResourceSchema.nullable(),
413
413
  after: mediaResourceSchema.nullable(),
414
- note: z3.string().nullable()
414
+ afterNote: z3.string().nullable().optional(),
415
+ beforeNote: z3.string().nullable().optional()
415
416
  });
416
417
  var billingPerZoneSchema = z3.object({
417
418
  Product: z3.string().min(MIN_STRING_LENGTH, "Product name is required"),
@@ -421,7 +422,8 @@ var billingPerZoneSchema = z3.object({
421
422
  UnitPrice: z3.number().min(0, "Unit price must be non-negative"),
422
423
  UnitCurency: z3.nativeEnum(Currency),
423
424
  Subtotal: z3.number().min(0, "Subtotal must be non-negative"),
424
- Note: z3.string().nullable()
425
+ Note: z3.string().nullable(),
426
+ IonNumber: z3.string().nullable()
425
427
  });
426
428
  var finalBillingSchema = z3.object({
427
429
  subtotalAll: z3.number().min(0, "Subtotal all must be non-negative"),
@@ -492,10 +494,7 @@ var updateAppointmentSchema = z3.object({
492
494
  practitionerId: z3.string().min(MIN_STRING_LENGTH).optional(),
493
495
  clinic_tz: z3.string().min(MIN_STRING_LENGTH).optional(),
494
496
  linkedForms: z3.union([z3.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z3.any()]).optional(),
495
- media: z3.union([
496
- z3.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
497
- z3.any()
498
- ]).optional(),
497
+ media: z3.union([z3.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH), z3.any()]).optional(),
499
498
  reviewInfo: z3.union([patientReviewInfoSchema.nullable(), z3.any()]).optional(),
500
499
  finalizedDetails: z3.union([finalizedDetailsSchema.nullable(), z3.any()]).optional(),
501
500
  isArchived: z3.boolean().optional(),
@@ -536,10 +535,7 @@ var searchAppointmentsSchema = z3.object({
536
535
  (val) => val === void 0 || val instanceof Date || (val == null ? void 0 : val._seconds) !== void 0 || (val == null ? void 0 : val.seconds) !== void 0 || typeof val === "number" || typeof val === "string" || val && typeof val.toMillis === "function",
537
536
  "End date must be a valid timestamp or Date object"
538
537
  ).optional(),
539
- status: z3.union([
540
- appointmentStatusSchema,
541
- z3.array(appointmentStatusSchema).nonempty()
542
- ]).optional(),
538
+ status: z3.union([appointmentStatusSchema, z3.array(appointmentStatusSchema).nonempty()]).optional(),
543
539
  limit: z3.number().positive().int().optional().default(20),
544
540
  startAfter: z3.any().optional()
545
541
  }).refine(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.11.0",
4
+ "version": "1.11.2",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -1,56 +1,49 @@
1
- import { Timestamp, FieldValue } from "firebase/firestore";
2
- import {
3
- ClinicInfo,
4
- PractitionerProfileInfo,
5
- PatientProfileInfo,
6
- } from "../profile";
7
- import { ProcedureSummaryInfo } from "../procedure";
8
- import {
9
- Currency,
10
- type PricingMeasure,
11
- } from "../../backoffice/types/static/pricing.types";
12
- import { BlockingCondition } from "../../backoffice/types/static/blocking-condition.types";
13
- import { Contraindication } from "../../backoffice/types/static/contraindication.types";
14
- import { Requirement } from "../../backoffice/types/requirement.types";
15
- import { FilledDocumentStatus } from "../documentation-templates";
16
- import type { ProcedureFamily } from "../../backoffice";
17
- import type { MediaResource } from "../../services/media/media.service";
1
+ import { Timestamp, FieldValue } from 'firebase/firestore';
2
+ import { ClinicInfo, PractitionerProfileInfo, PatientProfileInfo } from '../profile';
3
+ import { ProcedureSummaryInfo } from '../procedure';
4
+ import { Currency, type PricingMeasure } from '../../backoffice/types/static/pricing.types';
5
+ import { BlockingCondition } from '../../backoffice/types/static/blocking-condition.types';
6
+ import { Contraindication } from '../../backoffice/types/static/contraindication.types';
7
+ import { Requirement } from '../../backoffice/types/requirement.types';
8
+ import { FilledDocumentStatus } from '../documentation-templates';
9
+ import type { ProcedureFamily } from '../../backoffice';
10
+ import type { MediaResource } from '../../services/media/media.service';
18
11
 
19
12
  /**
20
13
  * Enum defining the possible statuses of an appointment.
21
14
  */
22
15
  export enum AppointmentStatus {
23
- PENDING = "pending", // Initial state after booking, before confirmation (if applicable)
24
- CONFIRMED = "confirmed", // Confirmed by clinic/practitioner
25
- CHECKED_IN = "checked_in", // Patient has arrived
26
- IN_PROGRESS = "in_progress", // Procedure has started
27
- COMPLETED = "completed", // Procedure finished successfully
28
- CANCELED_PATIENT = "canceled_patient", // Canceled by the patient
29
- CANCELED_PATIENT_RESCHEDULED = "canceled_patient_rescheduled", // Canceled by the patient and rescheduled by the clinic
30
- CANCELED_CLINIC = "canceled_clinic", // Canceled by the clinic/practitioner
31
- NO_SHOW = "no_show", // Patient did not attend
32
- RESCHEDULED_BY_CLINIC = "rescheduled_by_clinic", // When appointment is rescheduled by the clinic, waiting for patient confirmation or cancellation (when reschedule is accepted, status goes to confirmed, if not accepted, then status goes to canceled_patient_rescheduled)
16
+ PENDING = 'pending', // Initial state after booking, before confirmation (if applicable)
17
+ CONFIRMED = 'confirmed', // Confirmed by clinic/practitioner
18
+ CHECKED_IN = 'checked_in', // Patient has arrived
19
+ IN_PROGRESS = 'in_progress', // Procedure has started
20
+ COMPLETED = 'completed', // Procedure finished successfully
21
+ CANCELED_PATIENT = 'canceled_patient', // Canceled by the patient
22
+ CANCELED_PATIENT_RESCHEDULED = 'canceled_patient_rescheduled', // Canceled by the patient and rescheduled by the clinic
23
+ CANCELED_CLINIC = 'canceled_clinic', // Canceled by the clinic/practitioner
24
+ NO_SHOW = 'no_show', // Patient did not attend
25
+ RESCHEDULED_BY_CLINIC = 'rescheduled_by_clinic', // When appointment is rescheduled by the clinic, waiting for patient confirmation or cancellation (when reschedule is accepted, status goes to confirmed, if not accepted, then status goes to canceled_patient_rescheduled)
33
26
  }
34
27
 
35
28
  /**
36
29
  * Enum defining the payment status of an appointment.
37
30
  */
38
31
  export enum PaymentStatus {
39
- UNPAID = "unpaid",
40
- PAID = "paid",
41
- PARTIALLY_PAID = "partially_paid",
42
- REFUNDED = "refunded",
43
- NOT_APPLICABLE = "not_applicable", // For free services or other scenarios
32
+ UNPAID = 'unpaid',
33
+ PAID = 'paid',
34
+ PARTIALLY_PAID = 'partially_paid',
35
+ REFUNDED = 'refunded',
36
+ NOT_APPLICABLE = 'not_applicable', // For free services or other scenarios
44
37
  }
45
38
 
46
39
  /**
47
40
  * Enum for different types of media that can be attached to an appointment.
48
41
  */
49
42
  export enum MediaType {
50
- BEFORE_PHOTO = "before_photo",
51
- AFTER_PHOTO = "after_photo",
52
- CONSENT_SCAN = "consent_scan",
53
- OTHER_DOCUMENT = "other_document",
43
+ BEFORE_PHOTO = 'before_photo',
44
+ AFTER_PHOTO = 'after_photo',
45
+ CONSENT_SCAN = 'consent_scan',
46
+ OTHER_DOCUMENT = 'other_document',
54
47
  }
55
48
 
56
49
  /**
@@ -124,7 +117,8 @@ export interface BeforeAfterPerZone {
124
117
  /** URL for after photo or null if not available */
125
118
  after: MediaResource | null;
126
119
  /** Optional note for the zone */
127
- note: string | null;
120
+ afterNote?: string | null;
121
+ beforeNote?: string | null;
128
122
  }
129
123
 
130
124
  /**
@@ -147,6 +141,8 @@ export interface BillingPerZone {
147
141
  Subtotal: number;
148
142
  /** Optional billing note */
149
143
  Note: string | null;
144
+ /** Ion/Batch number for traceability */
145
+ IonNumber: string | null;
150
146
  }
151
147
 
152
148
  /**
@@ -231,7 +227,7 @@ export interface Appointment {
231
227
 
232
228
  /** Cancellation Details */
233
229
  cancellationReason?: string | null;
234
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
230
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
235
231
 
236
232
  /** Notes */
237
233
  internalNotes?: string | null;
@@ -326,7 +322,7 @@ export interface UpdateAppointmentData {
326
322
  procedureActualStartTime?: Timestamp | FieldValue | null; // NEW
327
323
  actualDurationMinutes?: number;
328
324
  cancellationReason?: string | null;
329
- canceledBy?: "patient" | "clinic" | "practitioner" | "system";
325
+ canceledBy?: 'patient' | 'clinic' | 'practitioner' | 'system';
330
326
  internalNotes?: string | null;
331
327
  patientNotes?: string | FieldValue | null; // Allow FieldValue for deleting
332
328
  paymentStatus?: PaymentStatus;
@@ -378,4 +374,4 @@ export interface SearchAppointmentsParams {
378
374
  }
379
375
 
380
376
  /** Firestore collection name */
381
- export const APPOINTMENTS_COLLECTION = "appointments";
377
+ export const APPOINTMENTS_COLLECTION = 'appointments';
@@ -1,15 +1,8 @@
1
- import { z } from "zod";
2
- import {
3
- AppointmentStatus,
4
- PaymentStatus,
5
- MediaType,
6
- } from "../types/appointment";
7
- import { filledDocumentStatusSchema } from "./documentation-templates.schema";
8
- import {
9
- Currency,
10
- PricingMeasure,
11
- } from "../backoffice/types/static/pricing.types";
12
- import { mediaResourceSchema } from "./media.schema";
1
+ import { z } from 'zod';
2
+ import { AppointmentStatus, PaymentStatus, MediaType } from '../types/appointment';
3
+ import { filledDocumentStatusSchema } from './documentation-templates.schema';
4
+ import { Currency, PricingMeasure } from '../backoffice/types/static/pricing.types';
5
+ import { mediaResourceSchema } from './media.schema';
13
6
 
14
7
  // Define common constants locally if not available from common.schema.ts
15
8
  const MIN_STRING_LENGTH = 1;
@@ -25,9 +18,9 @@ export const mediaTypeSchema = z.nativeEnum(MediaType);
25
18
  // --- Schemas for Nested Objects from types/appointment/index.ts ---
26
19
 
27
20
  export const appointmentMediaItemSchema = z.object({
28
- id: z.string().min(MIN_STRING_LENGTH, "Media item ID is required"),
21
+ id: z.string().min(MIN_STRING_LENGTH, 'Media item ID is required'),
29
22
  type: mediaTypeSchema,
30
- url: z.string().url("Media URL must be a valid URL"),
23
+ url: z.string().url('Media URL must be a valid URL'),
31
24
  fileName: z.string().optional(),
32
25
  uploadedAt: z
33
26
  .any()
@@ -36,18 +29,13 @@ export const appointmentMediaItemSchema = z.object({
36
29
  val instanceof Date ||
37
30
  val?._seconds !== undefined ||
38
31
  val?.seconds !== undefined ||
39
- typeof val === "number" ||
40
- typeof val === "string" ||
41
- (val && typeof val.toMillis === "function"),
42
- "uploadedAt must be a valid timestamp or Date object"
32
+ typeof val === 'number' ||
33
+ typeof val === 'string' ||
34
+ (val && typeof val.toMillis === 'function'),
35
+ 'uploadedAt must be a valid timestamp or Date object',
43
36
  ),
44
- uploadedBy: z
45
- .string()
46
- .min(MIN_STRING_LENGTH, "Uploaded by user ID is required"),
47
- description: z
48
- .string()
49
- .max(MAX_STRING_LENGTH, "Description too long")
50
- .optional(),
37
+ uploadedBy: z.string().min(MIN_STRING_LENGTH, 'Uploaded by user ID is required'),
38
+ description: z.string().max(MAX_STRING_LENGTH, 'Description too long').optional(),
51
39
  });
52
40
 
53
41
  export const procedureExtendedInfoSchema = z.object({
@@ -70,17 +58,14 @@ export const procedureExtendedInfoSchema = z.object({
70
58
  });
71
59
 
72
60
  export const linkedFormInfoSchema = z.object({
73
- formId: z.string().min(MIN_STRING_LENGTH, "Form ID is required"),
74
- templateId: z.string().min(MIN_STRING_LENGTH, "Template ID is required"),
75
- templateVersion: z
76
- .number()
77
- .int()
78
- .positive("Template version must be a positive integer"),
79
- title: z.string().min(MIN_STRING_LENGTH, "Form title is required"),
61
+ formId: z.string().min(MIN_STRING_LENGTH, 'Form ID is required'),
62
+ templateId: z.string().min(MIN_STRING_LENGTH, 'Template ID is required'),
63
+ templateVersion: z.number().int().positive('Template version must be a positive integer'),
64
+ title: z.string().min(MIN_STRING_LENGTH, 'Form title is required'),
80
65
  isUserForm: z.boolean(),
81
66
  isRequired: z.boolean().optional(),
82
67
  status: filledDocumentStatusSchema,
83
- path: z.string().min(MIN_STRING_LENGTH, "Form path is required"),
68
+ path: z.string().min(MIN_STRING_LENGTH, 'Form path is required'),
84
69
  submittedAt: z
85
70
  .any()
86
71
  .refine(
@@ -89,10 +74,10 @@ export const linkedFormInfoSchema = z.object({
89
74
  val instanceof Date ||
90
75
  val?._seconds !== undefined ||
91
76
  val?.seconds !== undefined ||
92
- typeof val === "number" ||
93
- typeof val === "string" ||
94
- (val && typeof val.toMillis === "function"),
95
- "submittedAt must be a valid timestamp or Date object"
77
+ typeof val === 'number' ||
78
+ typeof val === 'string' ||
79
+ (val && typeof val.toMillis === 'function'),
80
+ 'submittedAt must be a valid timestamp or Date object',
96
81
  )
97
82
  .optional(),
98
83
  completedAt: z
@@ -103,21 +88,18 @@ export const linkedFormInfoSchema = z.object({
103
88
  val instanceof Date ||
104
89
  val?._seconds !== undefined ||
105
90
  val?.seconds !== undefined ||
106
- typeof val === "number" ||
107
- typeof val === "string" ||
108
- (val && typeof val.toMillis === "function"),
109
- "completedAt must be a valid timestamp or Date object"
91
+ typeof val === 'number' ||
92
+ typeof val === 'string' ||
93
+ (val && typeof val.toMillis === 'function'),
94
+ 'completedAt must be a valid timestamp or Date object',
110
95
  )
111
96
  .optional(),
112
97
  });
113
98
 
114
99
  export const patientReviewInfoSchema = z.object({
115
- reviewId: z.string().min(MIN_STRING_LENGTH, "Review ID is required"),
116
- rating: z.number().min(1).max(5, "Rating must be between 1 and 5"),
117
- comment: z
118
- .string()
119
- .max(MAX_STRING_LENGTH_LONG, "Comment too long")
120
- .optional(),
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.string().max(MAX_STRING_LENGTH_LONG, 'Comment too long').optional(),
121
103
  reviewedAt: z
122
104
  .any()
123
105
  .refine(
@@ -125,15 +107,15 @@ export const patientReviewInfoSchema = z.object({
125
107
  val instanceof Date ||
126
108
  val?._seconds !== undefined ||
127
109
  val?.seconds !== undefined ||
128
- typeof val === "number" ||
129
- typeof val === "string" ||
130
- (val && typeof val.toMillis === "function"),
131
- "reviewedAt must be a valid timestamp or Date object"
110
+ typeof val === 'number' ||
111
+ typeof val === 'string' ||
112
+ (val && typeof val.toMillis === 'function'),
113
+ 'reviewedAt must be a valid timestamp or Date object',
132
114
  ),
133
115
  });
134
116
 
135
117
  export const finalizedDetailsSchema = z.object({
136
- by: z.string().min(MIN_STRING_LENGTH, "Finalized by user ID is required"),
118
+ by: z.string().min(MIN_STRING_LENGTH, 'Finalized by user ID is required'),
137
119
  at: z
138
120
  .any()
139
121
  .refine(
@@ -141,15 +123,12 @@ export const finalizedDetailsSchema = z.object({
141
123
  val instanceof Date ||
142
124
  val?._seconds !== undefined ||
143
125
  val?.seconds !== undefined ||
144
- typeof val === "number" ||
145
- typeof val === "string" ||
146
- (val && typeof val.toMillis === "function"),
147
- "Finalized at must be a valid timestamp or Date object"
126
+ typeof val === 'number' ||
127
+ typeof val === 'string' ||
128
+ (val && typeof val.toMillis === 'function'),
129
+ 'Finalized at must be a valid timestamp or Date object',
148
130
  ),
149
- notes: z
150
- .string()
151
- .max(MAX_STRING_LENGTH_LONG, "Finalization notes too long")
152
- .optional(),
131
+ notes: z.string().max(MAX_STRING_LENGTH_LONG, 'Finalization notes too long').optional(),
153
132
  });
154
133
 
155
134
  /**
@@ -158,32 +137,34 @@ export const finalizedDetailsSchema = z.object({
158
137
  export const beforeAfterPerZoneSchema = z.object({
159
138
  before: mediaResourceSchema.nullable(),
160
139
  after: mediaResourceSchema.nullable(),
161
- note: z.string().nullable(),
140
+ afterNote: z.string().nullable().optional(),
141
+ beforeNote: z.string().nullable().optional(),
162
142
  });
163
143
 
164
144
  /**
165
145
  * Schema for billing information per zone
166
146
  */
167
147
  export const billingPerZoneSchema = z.object({
168
- Product: z.string().min(MIN_STRING_LENGTH, "Product name is required"),
148
+ Product: z.string().min(MIN_STRING_LENGTH, 'Product name is required'),
169
149
  ProductId: z.string().nullable(),
170
- Quantity: z.number().min(0, "Quantity must be non-negative"),
150
+ Quantity: z.number().min(0, 'Quantity must be non-negative'),
171
151
  UnitOfMeasurement: z.nativeEnum(PricingMeasure),
172
- UnitPrice: z.number().min(0, "Unit price must be non-negative"),
152
+ UnitPrice: z.number().min(0, 'Unit price must be non-negative'),
173
153
  UnitCurency: z.nativeEnum(Currency),
174
- Subtotal: z.number().min(0, "Subtotal must be non-negative"),
154
+ Subtotal: z.number().min(0, 'Subtotal must be non-negative'),
175
155
  Note: z.string().nullable(),
156
+ IonNumber: z.string().nullable(),
176
157
  });
177
158
 
178
159
  /**
179
160
  * Schema for final billing calculations of the appointment
180
161
  */
181
162
  export const finalBillingSchema = z.object({
182
- subtotalAll: z.number().min(0, "Subtotal all must be non-negative"),
183
- taxRate: z.number().min(0).max(1, "Tax rate must be between 0 and 1"),
184
- taxPrice: z.number().min(0, "Tax price must be non-negative"),
185
- finalPrice: z.number().min(0, "Final price must be non-negative"),
186
- finalQuantity: z.number().min(0, "Final quantity must be non-negative"),
163
+ subtotalAll: z.number().min(0, 'Subtotal all must be non-negative'),
164
+ taxRate: z.number().min(0).max(1, 'Tax rate must be between 0 and 1'),
165
+ taxPrice: z.number().min(0, 'Tax price must be non-negative'),
166
+ finalPrice: z.number().min(0, 'Final price must be non-negative'),
167
+ finalQuantity: z.number().min(0, 'Final quantity must be non-negative'),
187
168
  currency: z.nativeEnum(Currency),
188
169
  unitOfMeasurement: z.nativeEnum(PricingMeasure),
189
170
  });
@@ -205,14 +186,10 @@ export const appointmentMetadataSchema = z.object({
205
186
  */
206
187
  export const createAppointmentSchema = z
207
188
  .object({
208
- clinicBranchId: z
209
- .string()
210
- .min(MIN_STRING_LENGTH, "Clinic branch ID is required"),
211
- practitionerId: z
212
- .string()
213
- .min(MIN_STRING_LENGTH, "Practitioner ID is required"),
214
- patientId: z.string().min(MIN_STRING_LENGTH, "Patient ID is required"),
215
- procedureId: z.string().min(MIN_STRING_LENGTH, "Procedure ID is required"),
189
+ clinicBranchId: z.string().min(MIN_STRING_LENGTH, 'Clinic branch ID is required'),
190
+ practitionerId: z.string().min(MIN_STRING_LENGTH, 'Practitioner ID is required'),
191
+ patientId: z.string().min(MIN_STRING_LENGTH, 'Patient ID is required'),
192
+ procedureId: z.string().min(MIN_STRING_LENGTH, 'Procedure ID is required'),
216
193
  appointmentStartTime: z
217
194
  .any()
218
195
  .refine(
@@ -220,10 +197,10 @@ export const createAppointmentSchema = z
220
197
  val instanceof Date ||
221
198
  val?._seconds !== undefined ||
222
199
  val?.seconds !== undefined ||
223
- typeof val === "number" ||
224
- typeof val === "string" ||
225
- (val && typeof val.toMillis === "function"),
226
- "Appointment start time must be a valid timestamp or Date object"
200
+ typeof val === 'number' ||
201
+ typeof val === 'string' ||
202
+ (val && typeof val.toMillis === 'function'),
203
+ 'Appointment start time must be a valid timestamp or Date object',
227
204
  ),
228
205
  appointmentEndTime: z
229
206
  .any()
@@ -232,27 +209,21 @@ export const createAppointmentSchema = z
232
209
  val instanceof Date ||
233
210
  val?._seconds !== undefined ||
234
211
  val?.seconds !== undefined ||
235
- typeof val === "number" ||
236
- typeof val === "string" ||
237
- (val && typeof val.toMillis === "function"),
238
- "Appointment end time must be a valid timestamp or Date object"
212
+ typeof val === 'number' ||
213
+ typeof val === 'string' ||
214
+ (val && typeof val.toMillis === 'function'),
215
+ 'Appointment end time must be a valid timestamp or Date object',
239
216
  ),
240
- cost: z.number().min(0, "Cost must be a non-negative number"),
241
- currency: z.string().min(1, "Currency is required"),
242
- patientNotes: z
243
- .string()
244
- .max(MAX_STRING_LENGTH, "Patient notes too long")
245
- .nullable()
246
- .optional(),
217
+ cost: z.number().min(0, 'Cost must be a non-negative number'),
218
+ currency: z.string().min(1, 'Currency is required'),
219
+ patientNotes: z.string().max(MAX_STRING_LENGTH, 'Patient notes too long').nullable().optional(),
247
220
  initialStatus: appointmentStatusSchema,
248
- initialPaymentStatus: paymentStatusSchema
249
- .optional()
250
- .default(PaymentStatus.UNPAID),
251
- clinic_tz: z.string().min(1, "Timezone is required"),
221
+ initialPaymentStatus: paymentStatusSchema.optional().default(PaymentStatus.UNPAID),
222
+ clinic_tz: z.string().min(1, 'Timezone is required'),
252
223
  })
253
224
  .refine((data) => data.appointmentEndTime > data.appointmentStartTime, {
254
- message: "Appointment end time must be after start time",
255
- path: ["appointmentEndTime"],
225
+ message: 'Appointment end time must be after start time',
226
+ path: ['appointmentEndTime'],
256
227
  });
257
228
 
258
229
  /**
@@ -268,30 +239,24 @@ export const updateAppointmentSchema = z
268
239
  actualDurationMinutes: z
269
240
  .number()
270
241
  .int()
271
- .positive("Duration must be a positive integer")
242
+ .positive('Duration must be a positive integer')
272
243
  .optional(),
273
244
  cancellationReason: z
274
245
  .string()
275
- .max(MAX_STRING_LENGTH, "Cancellation reason too long")
246
+ .max(MAX_STRING_LENGTH, 'Cancellation reason too long')
276
247
  .nullable()
277
248
  .optional(),
278
- canceledBy: z
279
- .enum(["patient", "clinic", "practitioner", "system"])
280
- .optional(),
249
+ canceledBy: z.enum(['patient', 'clinic', 'practitioner', 'system']).optional(),
281
250
  internalNotes: z
282
251
  .string()
283
- .max(MAX_STRING_LENGTH_LONG, "Internal notes too long")
252
+ .max(MAX_STRING_LENGTH_LONG, 'Internal notes too long')
284
253
  .nullable()
285
254
  .optional(),
286
255
  patientNotes: z.any().optional().nullable(),
287
256
  paymentStatus: paymentStatusSchema.optional(),
288
257
  paymentTransactionId: z.any().optional().nullable(),
289
- completedPreRequirements: z
290
- .union([z.array(z.string()), z.any()])
291
- .optional(),
292
- completedPostRequirements: z
293
- .union([z.array(z.string()), z.any()])
294
- .optional(),
258
+ completedPreRequirements: z.union([z.array(z.string()), z.any()]).optional(),
259
+ completedPostRequirements: z.union([z.array(z.string()), z.any()]).optional(),
295
260
  linkedFormIds: z.union([z.array(z.string()), z.any()]).optional(),
296
261
  pendingUserFormsIds: z.union([z.array(z.string()), z.any()]).optional(),
297
262
  appointmentStartTime: z
@@ -302,10 +267,10 @@ export const updateAppointmentSchema = z
302
267
  val instanceof Date ||
303
268
  val?._seconds !== undefined ||
304
269
  val?.seconds !== undefined ||
305
- typeof val === "number" ||
306
- typeof val === "string" ||
307
- (val && typeof val.toMillis === "function"),
308
- "Appointment start time must be a valid timestamp or Date object"
270
+ typeof val === 'number' ||
271
+ typeof val === 'string' ||
272
+ (val && typeof val.toMillis === 'function'),
273
+ 'Appointment start time must be a valid timestamp or Date object',
309
274
  )
310
275
  .optional(),
311
276
  appointmentEndTime: z
@@ -316,10 +281,10 @@ export const updateAppointmentSchema = z
316
281
  val instanceof Date ||
317
282
  val?._seconds !== undefined ||
318
283
  val?.seconds !== undefined ||
319
- typeof val === "number" ||
320
- typeof val === "string" ||
321
- (val && typeof val.toMillis === "function"),
322
- "Appointment end time must be a valid timestamp or Date object"
284
+ typeof val === 'number' ||
285
+ typeof val === 'string' ||
286
+ (val && typeof val.toMillis === 'function'),
287
+ 'Appointment end time must be a valid timestamp or Date object',
323
288
  )
324
289
  .optional(),
325
290
  calendarEventId: z.string().min(MIN_STRING_LENGTH).optional(),
@@ -327,21 +292,10 @@ export const updateAppointmentSchema = z
327
292
  clinicBranchId: z.string().min(MIN_STRING_LENGTH).optional(),
328
293
  practitionerId: z.string().min(MIN_STRING_LENGTH).optional(),
329
294
  clinic_tz: z.string().min(MIN_STRING_LENGTH).optional(),
330
- linkedForms: z
331
- .union([z.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z.any()])
332
- .optional(),
333
- media: z
334
- .union([
335
- z.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH),
336
- z.any(),
337
- ])
338
- .optional(),
339
- reviewInfo: z
340
- .union([patientReviewInfoSchema.nullable(), z.any()])
341
- .optional(),
342
- finalizedDetails: z
343
- .union([finalizedDetailsSchema.nullable(), z.any()])
344
- .optional(),
295
+ linkedForms: z.union([z.array(linkedFormInfoSchema).max(MAX_ARRAY_LENGTH), z.any()]).optional(),
296
+ media: z.union([z.array(appointmentMediaItemSchema).max(MAX_ARRAY_LENGTH), z.any()]).optional(),
297
+ reviewInfo: z.union([patientReviewInfoSchema.nullable(), z.any()]).optional(),
298
+ finalizedDetails: z.union([finalizedDetailsSchema.nullable(), z.any()]).optional(),
345
299
  isArchived: z.boolean().optional(),
346
300
  updatedAt: z.any().optional(),
347
301
  metadata: appointmentMetadataSchema.optional(),
@@ -359,9 +313,9 @@ export const updateAppointmentSchema = z
359
313
  },
360
314
  {
361
315
  message:
362
- "Cancellation reason and canceled by must be provided when canceling an appointment with patient or clinic origin.",
363
- path: ["status"],
364
- }
316
+ 'Cancellation reason and canceled by must be provided when canceling an appointment with patient or clinic origin.',
317
+ path: ['status'],
318
+ },
365
319
  )
366
320
  .refine(
367
321
  (data) => {
@@ -371,10 +325,9 @@ export const updateAppointmentSchema = z
371
325
  return true;
372
326
  },
373
327
  {
374
- message:
375
- "Appointment end time must be after start time if both are provided",
376
- path: ["appointmentEndTime"],
377
- }
328
+ message: 'Appointment end time must be after start time if both are provided',
329
+ path: ['appointmentEndTime'],
330
+ },
378
331
  );
379
332
 
380
333
  /**
@@ -393,10 +346,10 @@ export const searchAppointmentsSchema = z
393
346
  val instanceof Date ||
394
347
  val?._seconds !== undefined ||
395
348
  val?.seconds !== undefined ||
396
- typeof val === "number" ||
397
- typeof val === "string" ||
398
- (val && typeof val.toMillis === "function"),
399
- "Start date must be a valid timestamp or Date object"
349
+ typeof val === 'number' ||
350
+ typeof val === 'string' ||
351
+ (val && typeof val.toMillis === 'function'),
352
+ 'Start date must be a valid timestamp or Date object',
400
353
  )
401
354
  .optional(),
402
355
  endDate: z
@@ -407,17 +360,14 @@ export const searchAppointmentsSchema = z
407
360
  val instanceof Date ||
408
361
  val?._seconds !== undefined ||
409
362
  val?.seconds !== undefined ||
410
- typeof val === "number" ||
411
- typeof val === "string" ||
412
- (val && typeof val.toMillis === "function"),
413
- "End date must be a valid timestamp or Date object"
363
+ typeof val === 'number' ||
364
+ typeof val === 'string' ||
365
+ (val && typeof val.toMillis === 'function'),
366
+ 'End date must be a valid timestamp or Date object',
414
367
  )
415
368
  .optional(),
416
369
  status: z
417
- .union([
418
- appointmentStatusSchema,
419
- z.array(appointmentStatusSchema).nonempty(),
420
- ])
370
+ .union([appointmentStatusSchema, z.array(appointmentStatusSchema).nonempty()])
421
371
  .optional(),
422
372
  limit: z.number().positive().int().optional().default(20),
423
373
  startAfter: z.any().optional(),
@@ -431,9 +381,9 @@ export const searchAppointmentsSchema = z
431
381
  },
432
382
  {
433
383
  message:
434
- "At least one of patientId, practitionerId, or clinicBranchId must be provided if no date or status filters are set.",
435
- path: ["patientId"],
436
- }
384
+ 'At least one of patientId, practitionerId, or clinicBranchId must be provided if no date or status filters are set.',
385
+ path: ['patientId'],
386
+ },
437
387
  )
438
388
  .refine(
439
389
  (data) => {
@@ -443,18 +393,16 @@ export const searchAppointmentsSchema = z
443
393
  return true;
444
394
  },
445
395
  {
446
- message: "End date must be after or the same as start date",
447
- path: ["endDate"],
448
- }
396
+ message: 'End date must be after or the same as start date',
397
+ path: ['endDate'],
398
+ },
449
399
  );
450
400
 
451
401
  /**
452
402
  * Schema for validating appointment reschedule data
453
403
  */
454
404
  export const rescheduleAppointmentSchema = z.object({
455
- appointmentId: z
456
- .string()
457
- .min(MIN_STRING_LENGTH, "Appointment ID is required"),
405
+ appointmentId: z.string().min(MIN_STRING_LENGTH, 'Appointment ID is required'),
458
406
  newStartTime: z
459
407
  .any()
460
408
  .refine(
@@ -462,10 +410,10 @@ export const rescheduleAppointmentSchema = z.object({
462
410
  val instanceof Date ||
463
411
  val?._seconds !== undefined ||
464
412
  val?.seconds !== undefined ||
465
- typeof val === "number" ||
466
- typeof val === "string" ||
467
- (val && typeof val.toMillis === "function"),
468
- "New start time must be a valid timestamp, Date object, number, or string"
413
+ typeof val === 'number' ||
414
+ typeof val === 'string' ||
415
+ (val && typeof val.toMillis === 'function'),
416
+ 'New start time must be a valid timestamp, Date object, number, or string',
469
417
  ),
470
418
  newEndTime: z
471
419
  .any()
@@ -474,9 +422,9 @@ export const rescheduleAppointmentSchema = z.object({
474
422
  val instanceof Date ||
475
423
  val?._seconds !== undefined ||
476
424
  val?.seconds !== undefined ||
477
- typeof val === "number" ||
478
- typeof val === "string" ||
479
- (val && typeof val.toMillis === "function"),
480
- "New end time must be a valid timestamp, Date object, number, or string"
425
+ typeof val === 'number' ||
426
+ typeof val === 'string' ||
427
+ (val && typeof val.toMillis === 'function'),
428
+ 'New end time must be a valid timestamp, Date object, number, or string',
481
429
  ),
482
430
  });