@blackcode_sa/metaestetics-api 1.4.18 → 1.5.1

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.
@@ -17,11 +17,20 @@ import {
17
17
  CreatePractitionerData,
18
18
  UpdatePractitionerData,
19
19
  PRACTITIONERS_COLLECTION,
20
+ REGISTER_TOKENS_COLLECTION,
21
+ PractitionerStatus,
22
+ CreateDraftPractitionerData,
23
+ PractitionerToken,
24
+ CreatePractitionerTokenData,
25
+ PractitionerTokenStatus,
20
26
  } from "../../types/practitioner";
21
27
  import { ClinicService } from "../clinic/clinic.service";
22
28
  import {
23
29
  practitionerSchema,
24
30
  createPractitionerSchema,
31
+ createDraftPractitionerSchema,
32
+ practitionerTokenSchema,
33
+ createPractitionerTokenSchema,
25
34
  } from "../../validations/practitioner.schema";
26
35
  import { z } from "zod";
27
36
  import { Auth } from "firebase/auth";
@@ -92,8 +101,10 @@ export class PractitionerService extends BaseService {
92
101
  basicInfo: validatedData.basicInfo,
93
102
  certification: validatedData.certification,
94
103
  clinics: validatedData.clinics || [],
104
+ clinicWorkingHours: validatedData.clinicWorkingHours || [],
95
105
  isActive: validatedData.isActive,
96
106
  isVerified: validatedData.isVerified,
107
+ status: validatedData.status || PractitionerStatus.ACTIVE,
97
108
  createdAt: serverTimestamp(),
98
109
  updatedAt: serverTimestamp(),
99
110
  };
@@ -125,6 +136,278 @@ export class PractitionerService extends BaseService {
125
136
  }
126
137
  }
127
138
 
139
+ /**
140
+ * Kreira novi draft profil zdravstvenog radnika bez povezanog korisnika
141
+ * Koristi se od strane administratora klinike za kreiranje profila i kasnije pozivanje
142
+ * @param data Podaci za kreiranje draft profila
143
+ * @param createdBy ID administratora koji kreira profil
144
+ * @param clinicId ID klinike za koju se kreira profil
145
+ * @returns Objekt koji sadrži kreirani draft profil i token za registraciju
146
+ */
147
+ async createDraftPractitioner(
148
+ data: CreateDraftPractitionerData,
149
+ createdBy: string,
150
+ clinicId: string
151
+ ): Promise<{ practitioner: Practitioner; token: PractitionerToken }> {
152
+ try {
153
+ // Validacija ulaznih podataka
154
+ const validatedData = createDraftPractitionerSchema.parse(data);
155
+
156
+ // Provera da li klinika postoji
157
+ const clinic = await this.getClinicService().getClinic(clinicId);
158
+ if (!clinic) {
159
+ throw new Error(`Clinic ${clinicId} not found`);
160
+ }
161
+
162
+ // Priprema podataka za kreiranje profila
163
+ const clinics = data.clinics || [clinicId];
164
+
165
+ // Provera da li sve dodatno navedene klinike postoje
166
+ if (data.clinics) {
167
+ for (const cId of data.clinics) {
168
+ if (cId !== clinicId) {
169
+ const otherClinic = await this.getClinicService().getClinic(cId);
170
+ if (!otherClinic) {
171
+ throw new Error(`Clinic ${cId} not found`);
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ const practitionerId = this.generateId();
178
+ const practitionerData: Omit<Practitioner, "createdAt" | "updatedAt"> & {
179
+ createdAt: ReturnType<typeof serverTimestamp>;
180
+ updatedAt: ReturnType<typeof serverTimestamp>;
181
+ } = {
182
+ id: practitionerId,
183
+ userRef: "", // Prazno - biće popunjeno kada korisnik kreira nalog
184
+ basicInfo: validatedData.basicInfo,
185
+ certification: validatedData.certification,
186
+ clinics: clinics,
187
+ clinicWorkingHours: validatedData.clinicWorkingHours || [],
188
+ isActive:
189
+ validatedData.isActive !== undefined ? validatedData.isActive : false,
190
+ isVerified:
191
+ validatedData.isVerified !== undefined
192
+ ? validatedData.isVerified
193
+ : false,
194
+ status: PractitionerStatus.DRAFT,
195
+ createdAt: serverTimestamp(),
196
+ updatedAt: serverTimestamp(),
197
+ };
198
+
199
+ // Validacija kompletnog objekta
200
+ // Koristimo privremeni userRef za validaciju, biće prazan u bazi
201
+ practitionerSchema.parse({
202
+ ...practitionerData,
203
+ userRef: "temp-for-validation",
204
+ createdAt: Timestamp.now(),
205
+ updatedAt: Timestamp.now(),
206
+ });
207
+
208
+ // Čuvamo u Firestore
209
+ await setDoc(
210
+ doc(this.db, PRACTITIONERS_COLLECTION, practitionerData.id),
211
+ practitionerData
212
+ );
213
+
214
+ const savedPractitioner = await this.getPractitioner(practitionerData.id);
215
+ if (!savedPractitioner) {
216
+ throw new Error("Failed to create draft practitioner profile");
217
+ }
218
+
219
+ // Automatski kreiramo token za registraciju
220
+ const tokenString = this.generateId().slice(0, 6).toUpperCase();
221
+
222
+ // Default expiration is 7 days from now
223
+ const expiration = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
224
+
225
+ const token: PractitionerToken = {
226
+ id: this.generateId(),
227
+ token: tokenString,
228
+ practitionerId: practitionerId,
229
+ email: practitionerData.basicInfo.email,
230
+ clinicId: clinicId,
231
+ status: PractitionerTokenStatus.ACTIVE,
232
+ createdBy: createdBy,
233
+ createdAt: Timestamp.now(),
234
+ expiresAt: Timestamp.fromDate(expiration),
235
+ };
236
+
237
+ // Validate token object
238
+ practitionerTokenSchema.parse(token);
239
+
240
+ // Store the token in the practitioner document's register_tokens subcollection
241
+ const tokenPath = `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
242
+ await setDoc(doc(this.db, tokenPath), token);
243
+
244
+ // Ovde bi bilo slanje emaila sa tokenom, ali to ćemo implementirati kasnije
245
+ // TODO: Implement email sending with Cloud Functions
246
+
247
+ return { practitioner: savedPractitioner, token };
248
+ } catch (error) {
249
+ if (error instanceof z.ZodError) {
250
+ throw new Error("Invalid practitioner data: " + error.message);
251
+ }
252
+ throw error;
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Creates a token for inviting practitioner to claim their profile
258
+ * @param data Data for creating token
259
+ * @param createdBy ID of the user creating the token
260
+ * @returns Created token
261
+ */
262
+ async createPractitionerToken(
263
+ data: CreatePractitionerTokenData,
264
+ createdBy: string
265
+ ): Promise<PractitionerToken> {
266
+ try {
267
+ // Validate data
268
+ const validatedData = createPractitionerTokenSchema.parse(data);
269
+
270
+ // Check if practitioner exists and is in DRAFT status
271
+ const practitioner = await this.getPractitioner(
272
+ validatedData.practitionerId
273
+ );
274
+ if (!practitioner) {
275
+ throw new Error("Practitioner not found");
276
+ }
277
+
278
+ if (practitioner.status !== PractitionerStatus.DRAFT) {
279
+ throw new Error(
280
+ "Can only create tokens for practitioners in DRAFT status"
281
+ );
282
+ }
283
+
284
+ // Check if clinic exists and practitioner belongs to it
285
+ const clinic = await this.getClinicService().getClinic(
286
+ validatedData.clinicId
287
+ );
288
+ if (!clinic) {
289
+ throw new Error(`Clinic ${validatedData.clinicId} not found`);
290
+ }
291
+
292
+ if (!practitioner.clinics.includes(validatedData.clinicId)) {
293
+ throw new Error("Practitioner is not associated with this clinic");
294
+ }
295
+
296
+ // Default expiration is 7 days from now if not specified
297
+ const expiration =
298
+ validatedData.expiresAt ||
299
+ new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
300
+
301
+ // Generate a token (6 characters) using generateId from BaseService
302
+ const tokenString = this.generateId().slice(0, 6).toUpperCase();
303
+
304
+ const token: PractitionerToken = {
305
+ id: this.generateId(),
306
+ token: tokenString,
307
+ practitionerId: validatedData.practitionerId,
308
+ email: validatedData.email,
309
+ clinicId: validatedData.clinicId,
310
+ status: PractitionerTokenStatus.ACTIVE,
311
+ createdBy: createdBy,
312
+ createdAt: Timestamp.now(),
313
+ expiresAt: Timestamp.fromDate(expiration),
314
+ };
315
+
316
+ // Validate token object
317
+ practitionerTokenSchema.parse(token);
318
+
319
+ // Store the token in the practitioner document's register_tokens subcollection
320
+ const tokenPath = `${PRACTITIONERS_COLLECTION}/${validatedData.practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
321
+ await setDoc(doc(this.db, tokenPath), token);
322
+
323
+ return token;
324
+ } catch (error) {
325
+ if (error instanceof z.ZodError) {
326
+ throw new Error("Invalid token data: " + error.message);
327
+ }
328
+ throw error;
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Gets active tokens for a practitioner
334
+ * @param practitionerId ID of the practitioner
335
+ * @returns Array of active tokens
336
+ */
337
+ async getPractitionerActiveTokens(
338
+ practitionerId: string
339
+ ): Promise<PractitionerToken[]> {
340
+ const tokensRef = collection(
341
+ this.db,
342
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
343
+ );
344
+
345
+ const q = query(
346
+ tokensRef,
347
+ where("status", "==", PractitionerTokenStatus.ACTIVE),
348
+ where("expiresAt", ">", Timestamp.now())
349
+ );
350
+
351
+ const querySnapshot = await getDocs(q);
352
+ return querySnapshot.docs.map((doc) => doc.data() as PractitionerToken);
353
+ }
354
+
355
+ /**
356
+ * Gets a token by its string value and validates it
357
+ * @param tokenString The token string to find
358
+ * @returns The token if found and valid, null otherwise
359
+ */
360
+ async validateToken(tokenString: string): Promise<PractitionerToken | null> {
361
+ // We need to search through all practitioners' register_tokens subcollections
362
+ const practitionersRef = collection(this.db, PRACTITIONERS_COLLECTION);
363
+ const practitionersSnapshot = await getDocs(practitionersRef);
364
+
365
+ for (const practitionerDoc of practitionersSnapshot.docs) {
366
+ const practitionerId = practitionerDoc.id;
367
+ const tokensRef = collection(
368
+ this.db,
369
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
370
+ );
371
+
372
+ const q = query(
373
+ tokensRef,
374
+ where("token", "==", tokenString),
375
+ where("status", "==", PractitionerTokenStatus.ACTIVE),
376
+ where("expiresAt", ">", Timestamp.now())
377
+ );
378
+
379
+ const tokenSnapshot = await getDocs(q);
380
+ if (!tokenSnapshot.empty) {
381
+ return tokenSnapshot.docs[0].data() as PractitionerToken;
382
+ }
383
+ }
384
+
385
+ return null;
386
+ }
387
+
388
+ /**
389
+ * Marks a token as used
390
+ * @param tokenId ID of the token
391
+ * @param practitionerId ID of the practitioner
392
+ * @param userId ID of the user using the token
393
+ */
394
+ async markTokenAsUsed(
395
+ tokenId: string,
396
+ practitionerId: string,
397
+ userId: string
398
+ ): Promise<void> {
399
+ const tokenRef = doc(
400
+ this.db,
401
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${tokenId}`
402
+ );
403
+
404
+ await updateDoc(tokenRef, {
405
+ status: PractitionerTokenStatus.USED,
406
+ usedBy: userId,
407
+ usedAt: Timestamp.now(),
408
+ });
409
+ }
410
+
128
411
  /**
129
412
  * Dohvata zdravstvenog radnika po ID-u
130
413
  */
@@ -166,7 +449,24 @@ export class PractitionerService extends BaseService {
166
449
  const q = query(
167
450
  collection(this.db, PRACTITIONERS_COLLECTION),
168
451
  where("clinics", "array-contains", clinicId),
169
- where("isActive", "==", true)
452
+ where("isActive", "==", true),
453
+ where("status", "==", PractitionerStatus.ACTIVE)
454
+ );
455
+
456
+ const querySnapshot = await getDocs(q);
457
+ return querySnapshot.docs.map((doc) => doc.data() as Practitioner);
458
+ }
459
+
460
+ /**
461
+ * Dohvata sve draft zdravstvene radnike za određenu kliniku
462
+ */
463
+ async getDraftPractitionersByClinic(
464
+ clinicId: string
465
+ ): Promise<Practitioner[]> {
466
+ const q = query(
467
+ collection(this.db, PRACTITIONERS_COLLECTION),
468
+ where("clinics", "array-contains", clinicId),
469
+ where("status", "==", PractitionerStatus.DRAFT)
170
470
  );
171
471
 
172
472
  const querySnapshot = await getDocs(q);
@@ -302,4 +602,49 @@ export class PractitionerService extends BaseService {
302
602
 
303
603
  await deleteDoc(doc(this.db, PRACTITIONERS_COLLECTION, practitionerId));
304
604
  }
605
+
606
+ /**
607
+ * Validates a registration token and claims the associated draft practitioner profile
608
+ * @param tokenString The token provided by the practitioner
609
+ * @param userId The ID of the user claiming the profile
610
+ * @returns The claimed practitioner profile or null if token is invalid
611
+ */
612
+ async validateTokenAndClaimProfile(
613
+ tokenString: string,
614
+ userId: string
615
+ ): Promise<Practitioner | null> {
616
+ // Find the token
617
+ const token = await this.validateToken(tokenString);
618
+ if (!token) {
619
+ return null; // Token not found or not valid
620
+ }
621
+
622
+ // Get the practitioner profile
623
+ const practitioner = await this.getPractitioner(token.practitionerId);
624
+ if (!practitioner) {
625
+ return null; // Practitioner not found
626
+ }
627
+
628
+ // Ensure practitioner is in DRAFT status
629
+ if (practitioner.status !== PractitionerStatus.DRAFT) {
630
+ throw new Error("This practitioner profile has already been claimed");
631
+ }
632
+
633
+ // Check if user already has a practitioner profile
634
+ const existingPractitioner = await this.getPractitionerByUserRef(userId);
635
+ if (existingPractitioner) {
636
+ throw new Error("User already has a practitioner profile");
637
+ }
638
+
639
+ // Claim the profile by linking it to the user
640
+ const updatedPractitioner = await this.updatePractitioner(practitioner.id, {
641
+ userRef: userId,
642
+ status: PractitionerStatus.ACTIVE,
643
+ });
644
+
645
+ // Mark the token as used
646
+ await this.markTokenAsUsed(token.id, token.practitionerId, userId);
647
+
648
+ return updatedPractitioner;
649
+ }
305
650
  }
@@ -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
  */
@@ -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";