@blackcode_sa/metaestetics-api 1.4.17 → 1.5.0

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 (31) hide show
  1. package/dist/index.d.mts +9633 -7023
  2. package/dist/index.d.ts +9633 -7023
  3. package/dist/index.js +2773 -150
  4. package/dist/index.mjs +2809 -150
  5. package/package.json +4 -3
  6. package/src/index.ts +48 -1
  7. package/src/services/calendar/calendar-refactored.service.ts +1531 -0
  8. package/src/services/calendar/calendar.service.ts +1077 -0
  9. package/src/services/calendar/synced-calendars.service.ts +743 -0
  10. package/src/services/calendar/utils/appointment.utils.ts +314 -0
  11. package/src/services/calendar/utils/calendar-event.utils.ts +510 -0
  12. package/src/services/calendar/utils/clinic.utils.ts +237 -0
  13. package/src/services/calendar/utils/docs.utils.ts +157 -0
  14. package/src/services/calendar/utils/google-calendar.utils.ts +697 -0
  15. package/src/services/calendar/utils/index.ts +8 -0
  16. package/src/services/calendar/utils/patient.utils.ts +198 -0
  17. package/src/services/calendar/utils/practitioner.utils.ts +221 -0
  18. package/src/services/calendar/utils/synced-calendar.utils.ts +472 -0
  19. package/src/services/clinic/clinic.service.ts +2 -2
  20. package/src/services/clinic/utils/clinic.utils.ts +49 -47
  21. package/src/services/practitioner/practitioner.service.ts +1 -0
  22. package/src/types/calendar/index.ts +187 -0
  23. package/src/types/calendar/synced-calendar.types.ts +66 -0
  24. package/src/types/clinic/index.ts +4 -15
  25. package/src/types/index.ts +4 -0
  26. package/src/types/practitioner/index.ts +21 -0
  27. package/src/types/profile/index.ts +39 -0
  28. package/src/validations/calendar.schema.ts +223 -0
  29. package/src/validations/clinic.schema.ts +3 -3
  30. package/src/validations/practitioner.schema.ts +21 -0
  31. package/src/validations/profile-info.schema.ts +41 -0
@@ -0,0 +1,187 @@
1
+ import { Timestamp, FieldValue } from "firebase/firestore";
2
+ import type { ClinicLocation } from "../clinic";
3
+ import type {
4
+ Category,
5
+ ProcedureFamily,
6
+ Product,
7
+ Subcategory,
8
+ Technology,
9
+ } from "../../backoffice";
10
+ import type { SyncedCalendarProvider } from "./synced-calendar.types";
11
+ import type { Gender } from "../patient";
12
+ import type { PractitionerCertification } from "../practitioner";
13
+ import type { Currency } from "../../backoffice/types/static/pricing.types";
14
+ import type {
15
+ ClinicInfo,
16
+ PractitionerProfileInfo,
17
+ PatientProfileInfo,
18
+ } from "../profile";
19
+
20
+ /**
21
+ * Enum for calendar event status
22
+ */
23
+ export enum CalendarEventStatus {
24
+ PENDING = "pending", // When event is created, but not confirmed
25
+ CONFIRMED = "confirmed", // When event is confirmed and ready to be used
26
+ REJECTED = "rejected", // When event is rejected by the clinic administrator or patient
27
+ CANCELED = "canceled", // When event is canceled by the patient
28
+ RESCHEDULED = "rescheduled", // When event is rescheduled by the clinic administrator
29
+ COMPLETED = "completed", // When event is completed
30
+ }
31
+
32
+ /**
33
+ * Enum for calendar event sync status
34
+ */
35
+ export enum CalendarSyncStatus {
36
+ INTERNAL = "internal",
37
+ EXTERNAL = "external",
38
+ }
39
+
40
+ /**
41
+ * Enum for calendar event types
42
+ */
43
+ export enum CalendarEventType {
44
+ APPOINTMENT = "appointment",
45
+ BLOCKING = "blocking",
46
+ BREAK = "break",
47
+ FREE_DAY = "free_day",
48
+ OTHER = "other",
49
+ }
50
+
51
+ /**
52
+ * Interface for calendar event time
53
+ */
54
+ export interface CalendarEventTime {
55
+ start: Timestamp;
56
+ end: Timestamp;
57
+ }
58
+
59
+ export interface ProcedureInfo {
60
+ // TO-DO: Create detailed procedure info when procedures are integrated
61
+ name: string;
62
+ description: string;
63
+ duration: number;
64
+ price: number;
65
+ currency: Currency;
66
+ }
67
+
68
+ export interface ProcedureCategorization {
69
+ procedureFamily: ProcedureFamily;
70
+ procedureCategory: Category;
71
+ procedureSubcategory: Subcategory;
72
+ procedureTechnology: Technology;
73
+ procedureProduct: Product;
74
+ }
75
+
76
+ export interface SyncedCalendarEvent {
77
+ eventId: string;
78
+ syncedCalendarProvider: SyncedCalendarProvider;
79
+ syncedAt: Timestamp; // Timestamp when the event was last synced with the external calendar
80
+ }
81
+
82
+ /**
83
+ * Interface for calendar event
84
+ */
85
+ export interface CalendarEvent {
86
+ id: string;
87
+ clinicBranchId?: string | null;
88
+ clinicBranchInfo?: ClinicInfo | null;
89
+ practitionerProfileId?: string | null;
90
+ practitionerProfileInfo?: PractitionerProfileInfo | null;
91
+ patientProfileId?: string | null;
92
+ patientProfileInfo?: PatientProfileInfo | null;
93
+ procedureId?: string | null;
94
+ procedureInfo?: ProcedureInfo | null;
95
+ procedureCategorization?: ProcedureCategorization | null;
96
+ appointmentId?: string | null; // Created when calendar event is confirmed and it's type is APPOINTMENT
97
+ syncedCalendarEventId?: SyncedCalendarEvent[] | null;
98
+ eventName: string;
99
+ eventLocation?: ClinicLocation;
100
+ eventTime: CalendarEventTime;
101
+ description?: string;
102
+ status: CalendarEventStatus;
103
+ syncStatus: CalendarSyncStatus;
104
+ eventType: CalendarEventType;
105
+ createdAt: Timestamp;
106
+ updatedAt: Timestamp;
107
+ }
108
+
109
+ /**
110
+ * Interface for creating a calendar event
111
+ */
112
+ export interface CreateCalendarEventData {
113
+ id: string;
114
+ clinicBranchId?: string | null;
115
+ clinicBranchInfo?: ClinicInfo | null;
116
+ practitionerProfileId?: string | null;
117
+ practitionerProfileInfo?: PractitionerProfileInfo | null;
118
+ patientProfileId?: string | null;
119
+ patientProfileInfo?: PatientProfileInfo | null;
120
+ procedureId?: string | null;
121
+ appointmentId?: string | null; // Not needed if event type is not APPOINTMENT, or if there is no automatic appointment confirmation
122
+ syncedCalendarEventId?: SyncedCalendarEvent[] | null;
123
+ eventName: string;
124
+ eventLocation?: ClinicLocation;
125
+ eventTime: CalendarEventTime;
126
+ description?: string;
127
+ status: CalendarEventStatus;
128
+ syncStatus: CalendarSyncStatus;
129
+ eventType: CalendarEventType;
130
+ createdAt: FieldValue;
131
+ updatedAt: FieldValue;
132
+ }
133
+
134
+ /**
135
+ * Interface for updating a calendar event
136
+ */
137
+ export interface UpdateCalendarEventData {
138
+ syncedCalendarEventId?: SyncedCalendarEvent[] | null;
139
+ appointmentId?: string | null; // Updated when calendar event is confirmed and it's type is APPOINTMENT, plus Appointment is created
140
+ eventName?: string;
141
+ eventTime?: CalendarEventTime;
142
+ description?: string;
143
+ status?: CalendarEventStatus;
144
+ syncStatus?: CalendarSyncStatus;
145
+ eventType?: CalendarEventType;
146
+ updatedAt: FieldValue;
147
+ }
148
+
149
+ /**
150
+ * Interface for available time slot
151
+ */
152
+ export interface TimeSlot {
153
+ start: Date;
154
+ end: Date;
155
+ isAvailable: boolean;
156
+ }
157
+
158
+ /**
159
+ * Interface for appointment creation parameters
160
+ */
161
+ export interface CreateAppointmentParams {
162
+ clinicId: string;
163
+ doctorId: string;
164
+ patientId: string;
165
+ procedureId: string;
166
+ eventLocation: ClinicLocation;
167
+ eventTime: CalendarEventTime;
168
+ description?: string;
169
+ }
170
+
171
+ /**
172
+ * Interface for appointment update parameters
173
+ */
174
+ export interface UpdateAppointmentParams {
175
+ appointmentId: string;
176
+ clinicId: string;
177
+ doctorId: string;
178
+ patientId: string;
179
+ eventTime?: CalendarEventTime;
180
+ description?: string;
181
+ status?: CalendarEventStatus;
182
+ }
183
+
184
+ /**
185
+ * Collection names for calendar
186
+ */
187
+ export const CALENDAR_COLLECTION = "calendar";
@@ -0,0 +1,66 @@
1
+ import { Timestamp, FieldValue } from "firebase/firestore";
2
+
3
+ /**
4
+ * Enum for synced calendar provider
5
+ */
6
+ export enum SyncedCalendarProvider {
7
+ GOOGLE = "google",
8
+ OUTLOOK = "outlook",
9
+ APPLE = "apple",
10
+ }
11
+
12
+ /**
13
+ * Interface for synced calendar
14
+ */
15
+ export interface SyncedCalendar {
16
+ id: string;
17
+ provider: SyncedCalendarProvider;
18
+ name: string;
19
+ description?: string;
20
+ accessToken: string;
21
+ refreshToken: string;
22
+ tokenExpiry: Timestamp;
23
+ calendarId: string;
24
+ isActive: boolean;
25
+ lastSyncedAt?: Timestamp;
26
+ createdAt: Timestamp;
27
+ updatedAt: Timestamp;
28
+ }
29
+
30
+ /**
31
+ * Interface for creating a synced calendar
32
+ */
33
+ export interface CreateSyncedCalendarData {
34
+ id: string;
35
+ provider: SyncedCalendarProvider;
36
+ name: string;
37
+ description?: string;
38
+ accessToken: string;
39
+ refreshToken: string;
40
+ tokenExpiry: Timestamp;
41
+ calendarId: string;
42
+ isActive: boolean;
43
+ lastSyncedAt?: Timestamp;
44
+ createdAt: FieldValue;
45
+ updatedAt: FieldValue;
46
+ }
47
+
48
+ /**
49
+ * Interface for updating a synced calendar
50
+ */
51
+ export interface UpdateSyncedCalendarData {
52
+ name?: string;
53
+ description?: string;
54
+ accessToken?: string;
55
+ refreshToken?: string;
56
+ tokenExpiry?: Timestamp;
57
+ calendarId?: string;
58
+ isActive?: boolean;
59
+ lastSyncedAt?: Timestamp;
60
+ updatedAt: FieldValue;
61
+ }
62
+
63
+ /**
64
+ * Collection name for synced calendars
65
+ */
66
+ export const SYNCED_CALENDARS_COLLECTION = "syncedCalendars";
@@ -5,6 +5,7 @@ import type {
5
5
  Currency,
6
6
  PricingMeasure,
7
7
  } from "../../backoffice/types/static/pricing.types";
8
+ import type { ClinicInfo } from "../profile";
8
9
 
9
10
  export const CLINIC_GROUPS_COLLECTION = "clinic_groups";
10
11
  export const CLINIC_ADMINS_COLLECTION = "clinic_admins";
@@ -94,18 +95,6 @@ export interface ContactPerson {
94
95
  phoneNumber?: string | null;
95
96
  }
96
97
 
97
- /**
98
- * Interface for clinic information
99
- */
100
- export interface ClinicInfo {
101
- id: string;
102
- featuredPhoto: string;
103
- name: string;
104
- description?: string;
105
- location: ClinicLocation;
106
- contactInfo: ClinicContactInfo;
107
- }
108
-
109
98
  /**
110
99
  * Interface for clinic admin
111
100
  */
@@ -308,7 +297,7 @@ export interface Clinic {
308
297
  workingHours: WorkingHours;
309
298
  tags: ClinicTag[];
310
299
  featuredPhotos: string[];
311
- photos: string[];
300
+ coverPhoto: string | null;
312
301
  photosWithTags?: { url: string; tag: string }[];
313
302
  doctors: string[];
314
303
  doctorsInfo: DoctorInfo[];
@@ -339,7 +328,7 @@ export interface CreateClinicData {
339
328
  contactInfo: ClinicContactInfo;
340
329
  workingHours: WorkingHours;
341
330
  tags: ClinicTag[];
342
- photos: string[];
331
+ coverPhoto: string | null;
343
332
  photosWithTags?: { url: string; tag: string }[];
344
333
  doctors: string[];
345
334
  services: string[];
@@ -418,7 +407,7 @@ export interface ClinicBranchSetupData {
418
407
  workingHours: WorkingHours;
419
408
  tags: ClinicTag[];
420
409
  logo?: string;
421
- photos: string[];
410
+ coverPhoto: string | null;
422
411
  photosWithTags?: { url: string; tag: string }[];
423
412
  featuredPhotos?: string[];
424
413
  }
@@ -41,3 +41,7 @@ export type FirebaseUser = FirebaseAuthUser;
41
41
  export * from "./documentation-templates";
42
42
  export const DOCUMENTATION_TEMPLATES_COLLECTION = "documentation-templates";
43
43
  export const FILLED_DOCUMENTS_COLLECTION = "filled-documents";
44
+
45
+ // Calendar
46
+ export * from "./calendar";
47
+ export const CALENDAR_COLLECTION = "calendar";
@@ -35,6 +35,25 @@ export interface PractitionerCertification {
35
35
  verificationStatus: "pending" | "verified" | "rejected";
36
36
  }
37
37
 
38
+ /**
39
+ * Interfejs za radno vreme zdravstvenog radnika u klinici
40
+ */
41
+ export interface PractitionerClinicWorkingHours {
42
+ clinicId: string;
43
+ workingHours: {
44
+ monday: { start: string; end: string } | null;
45
+ tuesday: { start: string; end: string } | null;
46
+ wednesday: { start: string; end: string } | null;
47
+ thursday: { start: string; end: string } | null;
48
+ friday: { start: string; end: string } | null;
49
+ saturday: { start: string; end: string } | null;
50
+ sunday: { start: string; end: string } | null;
51
+ };
52
+ isActive: boolean;
53
+ createdAt: Timestamp;
54
+ updatedAt: Timestamp;
55
+ }
56
+
38
57
  /**
39
58
  * Interfejs za zdravstvenog radnika
40
59
  */
@@ -44,6 +63,7 @@ export interface Practitioner {
44
63
  basicInfo: PractitionerBasicInfo;
45
64
  certification: PractitionerCertification;
46
65
  clinics: string[]; // Reference na klinike gde radi
66
+ clinicWorkingHours: PractitionerClinicWorkingHours[]; // Radno vreme za svaku kliniku
47
67
  isActive: boolean;
48
68
  isVerified: boolean;
49
69
  createdAt: Timestamp;
@@ -58,6 +78,7 @@ export interface CreatePractitionerData {
58
78
  basicInfo: PractitionerBasicInfo;
59
79
  certification: PractitionerCertification;
60
80
  clinics?: string[];
81
+ clinicWorkingHours?: PractitionerClinicWorkingHours[];
61
82
  isActive: boolean;
62
83
  isVerified: boolean;
63
84
  }
@@ -0,0 +1,39 @@
1
+ import { Timestamp } from "firebase-admin/firestore";
2
+ import { ClinicLocation, ClinicContactInfo } from "../clinic";
3
+ import { PractitionerCertification } from "../practitioner";
4
+ import { Gender } from "../patient";
5
+ /**
6
+ * Interface for clinic profile information
7
+ */
8
+ export interface ClinicInfo {
9
+ id: string;
10
+ featuredPhoto: string;
11
+ name: string;
12
+ description: string;
13
+ location: ClinicLocation;
14
+ contactInfo: ClinicContactInfo;
15
+ }
16
+
17
+ /**
18
+ * Interface for practitioner profile information
19
+ */
20
+ export interface PractitionerProfileInfo {
21
+ id: string;
22
+ practitionerPhoto: string | null;
23
+ name: string;
24
+ email: string;
25
+ phone: string | null;
26
+ certification: PractitionerCertification;
27
+ }
28
+
29
+ /**
30
+ * Interface for patient profile information
31
+ */
32
+ export interface PatientProfileInfo {
33
+ id: string;
34
+ fullName: string;
35
+ email: string;
36
+ phone: string | null;
37
+ dateOfBirth: Timestamp;
38
+ gender: Gender;
39
+ }
@@ -0,0 +1,223 @@
1
+ import { z } from "zod";
2
+ import { Timestamp, FieldValue } from "firebase/firestore";
3
+ import {
4
+ CalendarEventStatus,
5
+ CalendarEventType,
6
+ CalendarSyncStatus,
7
+ } from "../types/calendar";
8
+ import { SyncedCalendarProvider } from "../types/calendar/synced-calendar.types";
9
+ import {
10
+ Currency,
11
+ PricingMeasure,
12
+ } from "../backoffice/types/static/pricing.types";
13
+ import { clinicLocationSchema } from "./clinic.schema";
14
+ import {
15
+ clinicInfoSchema,
16
+ patientProfileInfoSchema,
17
+ practitionerProfileInfoSchema,
18
+ } from "./profile-info.schema";
19
+
20
+ /**
21
+ * Minimum appointment duration in minutes
22
+ */
23
+ const MIN_APPOINTMENT_DURATION = 15;
24
+
25
+ /**
26
+ * Validation schema for calendar event time
27
+ */
28
+ export const calendarEventTimeSchema = z
29
+ .object({
30
+ start: z.instanceof(Date).or(z.instanceof(Timestamp)),
31
+ end: z.instanceof(Date).or(z.instanceof(Timestamp)),
32
+ })
33
+ .refine(
34
+ (data) => {
35
+ // Convert to Date objects for comparison if they are Timestamps
36
+ const startDate =
37
+ data.start instanceof Timestamp ? data.start.toDate() : data.start;
38
+ const endDate =
39
+ data.end instanceof Timestamp ? data.end.toDate() : data.end;
40
+
41
+ // Ensure end time is after start time
42
+ return startDate < endDate;
43
+ },
44
+ {
45
+ message: "End time must be after start time",
46
+ path: ["end"],
47
+ }
48
+ )
49
+ .refine(
50
+ (data) => {
51
+ // Convert to Date objects for comparison if they are Timestamps
52
+ const startDate =
53
+ data.start instanceof Timestamp ? data.start.toDate() : data.start;
54
+
55
+ // Ensure start time is in the future
56
+ return startDate > new Date();
57
+ },
58
+ {
59
+ message: "Appointment must be scheduled in the future",
60
+ path: ["start"],
61
+ }
62
+ );
63
+
64
+ /**
65
+ * Validation schema for time slot
66
+ */
67
+ export const timeSlotSchema = z
68
+ .object({
69
+ start: z.date(),
70
+ end: z.date(),
71
+ isAvailable: z.boolean(),
72
+ })
73
+ .refine((data) => data.start < data.end, {
74
+ message: "End time must be after start time",
75
+ path: ["end"],
76
+ });
77
+
78
+ /**
79
+ * Validation schema for synced calendar event
80
+ */
81
+ export const syncedCalendarEventSchema = z.object({
82
+ eventId: z.string(),
83
+ syncedCalendarProvider: z.nativeEnum(SyncedCalendarProvider),
84
+ syncedAt: z.instanceof(Date).or(z.instanceof(Timestamp)),
85
+ });
86
+
87
+ /**
88
+ * Validation schema for procedure info
89
+ */
90
+ export const procedureInfoSchema = z.object({
91
+ name: z.string(),
92
+ description: z.string(),
93
+ duration: z.number().min(MIN_APPOINTMENT_DURATION),
94
+ price: z.number().min(0),
95
+ currency: z.nativeEnum(Currency),
96
+ });
97
+
98
+ /**
99
+ * Validation schema for procedure categorization
100
+ */
101
+ export const procedureCategorizationSchema = z.object({
102
+ procedureFamily: z.string(), // Replace with proper enum when available
103
+ procedureCategory: z.string(), // Replace with proper enum when available
104
+ procedureSubcategory: z.string(), // Replace with proper enum when available
105
+ procedureTechnology: z.string(), // Replace with proper enum when available
106
+ procedureProduct: z.string(), // Replace with proper enum when available
107
+ });
108
+
109
+ /**
110
+ * Validation schema for creating an appointment
111
+ */
112
+ export const createAppointmentSchema = z
113
+ .object({
114
+ clinicId: z.string().min(1, "Clinic ID is required"),
115
+ doctorId: z.string().min(1, "Doctor ID is required"),
116
+ patientId: z.string().min(1, "Patient ID is required"),
117
+ procedureId: z.string().min(1, "Procedure ID is required"),
118
+ eventLocation: clinicLocationSchema,
119
+ eventTime: calendarEventTimeSchema,
120
+ description: z.string().optional(),
121
+ })
122
+ .refine(
123
+ (data) => {
124
+ // If we have access to the actual data, we could validate:
125
+ // - Doctor works at the clinic
126
+ // - Procedure is available at the clinic
127
+ // - Patient is eligible for the procedure
128
+ // These validations will be handled in the service layer
129
+ return true;
130
+ },
131
+ {
132
+ message: "Invalid appointment parameters",
133
+ }
134
+ );
135
+
136
+ /**
137
+ * Validation schema for updating an appointment
138
+ */
139
+ export const updateAppointmentSchema = z.object({
140
+ appointmentId: z.string().min(1, "Appointment ID is required"),
141
+ clinicId: z.string().min(1, "Clinic ID is required"),
142
+ doctorId: z.string().min(1, "Doctor ID is required"),
143
+ patientId: z.string().min(1, "Patient ID is required"),
144
+ eventTime: calendarEventTimeSchema.optional(),
145
+ description: z.string().optional(),
146
+ status: z.nativeEnum(CalendarEventStatus).optional(),
147
+ });
148
+
149
+ /**
150
+ * Validation schema for creating a calendar event
151
+ */
152
+ export const createCalendarEventSchema = z.object({
153
+ id: z.string(),
154
+ clinicBranchId: z.string().nullable().optional(),
155
+ clinicBranchInfo: z.any().nullable().optional(),
156
+ practitionerProfileId: z.string().nullable().optional(),
157
+ practitionerProfileInfo: practitionerProfileInfoSchema.nullable().optional(),
158
+ patientProfileId: z.string().nullable().optional(),
159
+ patientProfileInfo: patientProfileInfoSchema.nullable().optional(),
160
+ procedureId: z.string().nullable().optional(),
161
+ appointmentId: z.string().nullable().optional(),
162
+ syncedCalendarEventId: z
163
+ .array(syncedCalendarEventSchema)
164
+ .nullable()
165
+ .optional(),
166
+ eventName: z.string().min(1, "Event name is required"),
167
+ eventLocation: clinicLocationSchema.optional(),
168
+ eventTime: calendarEventTimeSchema,
169
+ description: z.string().optional(),
170
+ status: z.nativeEnum(CalendarEventStatus),
171
+ syncStatus: z.nativeEnum(CalendarSyncStatus),
172
+ eventType: z.nativeEnum(CalendarEventType),
173
+ createdAt: z.any(), // FieldValue for server timestamp
174
+ updatedAt: z.any(), // FieldValue for server timestamp
175
+ });
176
+
177
+ /**
178
+ * Validation schema for updating a calendar event
179
+ */
180
+ export const updateCalendarEventSchema = z.object({
181
+ syncedCalendarEventId: z
182
+ .array(syncedCalendarEventSchema)
183
+ .nullable()
184
+ .optional(),
185
+ appointmentId: z.string().nullable().optional(),
186
+ eventName: z.string().optional(),
187
+ eventTime: calendarEventTimeSchema.optional(),
188
+ description: z.string().optional(),
189
+ status: z.nativeEnum(CalendarEventStatus).optional(),
190
+ syncStatus: z.nativeEnum(CalendarSyncStatus).optional(),
191
+ eventType: z.nativeEnum(CalendarEventType).optional(),
192
+ updatedAt: z.any(), // FieldValue for server timestamp
193
+ });
194
+
195
+ /**
196
+ * Validation schema for calendar event
197
+ */
198
+ export const calendarEventSchema = z.object({
199
+ id: z.string(),
200
+ clinicBranchId: z.string().nullable().optional(),
201
+ clinicBranchInfo: z.any().nullable().optional(), // Will be replaced with proper clinic info schema
202
+ practitionerProfileId: z.string().nullable().optional(),
203
+ practitionerProfileInfo: practitionerProfileInfoSchema.nullable().optional(),
204
+ patientProfileId: z.string().nullable().optional(),
205
+ patientProfileInfo: patientProfileInfoSchema.nullable().optional(),
206
+ procedureId: z.string().nullable().optional(),
207
+ procedureInfo: procedureInfoSchema.nullable().optional(),
208
+ procedureCategorization: procedureCategorizationSchema.nullable().optional(),
209
+ appointmentId: z.string().nullable().optional(),
210
+ syncedCalendarEventId: z
211
+ .array(syncedCalendarEventSchema)
212
+ .nullable()
213
+ .optional(),
214
+ eventName: z.string(),
215
+ eventLocation: clinicLocationSchema.optional(),
216
+ eventTime: calendarEventTimeSchema,
217
+ description: z.string().optional(),
218
+ status: z.nativeEnum(CalendarEventStatus),
219
+ syncStatus: z.nativeEnum(CalendarSyncStatus),
220
+ eventType: z.nativeEnum(CalendarEventType),
221
+ createdAt: z.instanceof(Date).or(z.instanceof(Timestamp)),
222
+ updatedAt: z.instanceof(Date).or(z.instanceof(Timestamp)),
223
+ });
@@ -240,7 +240,7 @@ export const clinicSchema = z.object({
240
240
  workingHours: workingHoursSchema,
241
241
  tags: z.array(z.nativeEnum(ClinicTag)),
242
242
  featuredPhotos: z.array(z.string()),
243
- photos: z.array(z.string()),
243
+ coverPhoto: z.string().nullable(),
244
244
  photosWithTags: z
245
245
  .array(
246
246
  z.object({
@@ -316,7 +316,7 @@ export const createClinicSchema = z.object({
316
316
  contactInfo: clinicContactInfoSchema,
317
317
  workingHours: workingHoursSchema,
318
318
  tags: z.array(z.nativeEnum(ClinicTag)),
319
- photos: z.array(z.string()),
319
+ coverPhoto: z.string().nullable(),
320
320
  photosWithTags: z
321
321
  .array(
322
322
  z.object({
@@ -403,7 +403,7 @@ export const clinicBranchSetupSchema = z.object({
403
403
  workingHours: workingHoursSchema,
404
404
  tags: z.array(z.nativeEnum(ClinicTag)),
405
405
  logo: z.string().optional(),
406
- photos: z.array(z.string()),
406
+ coverPhoto: z.string().nullable(),
407
407
  photosWithTags: z
408
408
  .array(
409
409
  z.object({
@@ -60,6 +60,25 @@ export const practitionerWorkingHoursSchema = z.object({
60
60
  updatedAt: z.instanceof(Timestamp),
61
61
  });
62
62
 
63
+ /**
64
+ * Šema za validaciju radnog vremena u klinici
65
+ */
66
+ export const practitionerClinicWorkingHoursSchema = z.object({
67
+ clinicId: z.string().min(1),
68
+ workingHours: z.object({
69
+ monday: timeSlotSchema,
70
+ tuesday: timeSlotSchema,
71
+ wednesday: timeSlotSchema,
72
+ thursday: timeSlotSchema,
73
+ friday: timeSlotSchema,
74
+ saturday: timeSlotSchema,
75
+ sunday: timeSlotSchema,
76
+ }),
77
+ isActive: z.boolean(),
78
+ createdAt: z.instanceof(Timestamp),
79
+ updatedAt: z.instanceof(Timestamp),
80
+ });
81
+
63
82
  /**
64
83
  * Šema za validaciju recenzije zdravstvenog radnika
65
84
  */
@@ -96,6 +115,7 @@ export const practitionerSchema = z.object({
96
115
  basicInfo: practitionerBasicInfoSchema,
97
116
  certification: practitionerCertificationSchema,
98
117
  clinics: z.array(z.string()),
118
+ clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema),
99
119
  isActive: z.boolean(),
100
120
  isVerified: z.boolean(),
101
121
  createdAt: z.instanceof(Timestamp),
@@ -110,6 +130,7 @@ export const createPractitionerSchema = z.object({
110
130
  basicInfo: practitionerBasicInfoSchema,
111
131
  certification: practitionerCertificationSchema,
112
132
  clinics: z.array(z.string()).optional(),
133
+ clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
113
134
  isActive: z.boolean(),
114
135
  isVerified: z.boolean(),
115
136
  });