@blackcode_sa/metaestetics-api 1.4.8 → 1.4.10
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/index.d.mts +31 -16
- package/dist/index.d.ts +31 -16
- package/dist/index.js +742 -187
- package/dist/index.mjs +761 -199
- package/package.json +1 -1
- package/src/services/auth.service.ts +33 -11
- package/src/services/clinic/clinic-group.service.ts +63 -9
- package/src/services/clinic/clinic.service.ts +32 -9
- package/src/services/clinic/utils/admin.utils.ts +6 -51
- package/src/services/clinic/utils/clinic-group.utils.ts +178 -45
- package/src/services/clinic/utils/clinic.utils.ts +482 -39
- package/src/services/clinic/utils/photos.utils.ts +188 -0
- package/src/services/clinic/utils/review.utils.ts +24 -17
- package/src/services/clinic/utils/tag.utils.ts +33 -8
- package/src/types/clinic/index.ts +8 -5
- package/src/validations/clinic.schema.ts +6 -3
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getStorage,
|
|
3
|
+
ref,
|
|
4
|
+
uploadBytes,
|
|
5
|
+
getDownloadURL,
|
|
6
|
+
deleteObject,
|
|
7
|
+
} from "firebase/storage";
|
|
8
|
+
import { FirebaseApp } from "firebase/app";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Uploads a photo to Firebase Storage
|
|
12
|
+
* @param photo - The photo data URL or URL
|
|
13
|
+
* @param entityType - The type of entity (clinic-groups, clinics, etc.)
|
|
14
|
+
* @param entityId - The ID of the entity
|
|
15
|
+
* @param photoType - The type of photo (logo, featured, etc.)
|
|
16
|
+
* @param app - Firebase app instance
|
|
17
|
+
* @param fileName - Optional custom file name
|
|
18
|
+
* @returns The URL of the uploaded photo or null if upload failed
|
|
19
|
+
*/
|
|
20
|
+
export async function uploadPhoto(
|
|
21
|
+
photo: string | null,
|
|
22
|
+
entityType: string,
|
|
23
|
+
entityId: string,
|
|
24
|
+
photoType: string,
|
|
25
|
+
app: FirebaseApp,
|
|
26
|
+
fileName?: string
|
|
27
|
+
): Promise<string | null> {
|
|
28
|
+
// If photo is null or not a data URL, return it as is
|
|
29
|
+
if (!photo || typeof photo !== "string" || !photo.startsWith("data:")) {
|
|
30
|
+
return photo;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
console.log(
|
|
35
|
+
`[PHOTO_UTILS] Uploading ${photoType} for ${entityType}/${entityId}`
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Get Firebase Storage instance
|
|
39
|
+
const storage = getStorage(app);
|
|
40
|
+
|
|
41
|
+
// Create a reference to the storage location
|
|
42
|
+
const storageFileName = fileName || `${photoType}-${Date.now()}`;
|
|
43
|
+
const storageRef = ref(
|
|
44
|
+
storage,
|
|
45
|
+
`${entityType}/${entityId}/${storageFileName}`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Extract base64 data from data URL
|
|
49
|
+
const base64Data = photo.split(",")[1];
|
|
50
|
+
const contentType = photo.split(";")[0].split(":")[1];
|
|
51
|
+
|
|
52
|
+
// Convert base64 to Blob
|
|
53
|
+
const byteCharacters = atob(base64Data);
|
|
54
|
+
const byteArrays = [];
|
|
55
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
56
|
+
byteArrays.push(byteCharacters.charCodeAt(i));
|
|
57
|
+
}
|
|
58
|
+
const blob = new Blob([new Uint8Array(byteArrays)], { type: contentType });
|
|
59
|
+
|
|
60
|
+
// Upload the file
|
|
61
|
+
await uploadBytes(storageRef, blob, { contentType });
|
|
62
|
+
|
|
63
|
+
// Get the download URL
|
|
64
|
+
const downloadUrl = await getDownloadURL(storageRef);
|
|
65
|
+
|
|
66
|
+
console.log(`[PHOTO_UTILS] ${photoType} uploaded successfully`, {
|
|
67
|
+
downloadUrl,
|
|
68
|
+
});
|
|
69
|
+
return downloadUrl;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`[PHOTO_UTILS] Error uploading ${photoType}:`, error);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Deletes a photo from Firebase Storage
|
|
78
|
+
* @param photoUrl - The URL of the photo to delete
|
|
79
|
+
* @param app - Firebase app instance
|
|
80
|
+
* @returns Whether the deletion was successful
|
|
81
|
+
*/
|
|
82
|
+
export async function deletePhoto(
|
|
83
|
+
photoUrl: string,
|
|
84
|
+
app: FirebaseApp
|
|
85
|
+
): Promise<boolean> {
|
|
86
|
+
if (!photoUrl || !photoUrl.includes("firebasestorage.googleapis.com")) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
console.log(`[PHOTO_UTILS] Deleting photo`, { photoUrl });
|
|
92
|
+
|
|
93
|
+
// Get Firebase Storage instance
|
|
94
|
+
const storage = getStorage(app);
|
|
95
|
+
|
|
96
|
+
// Extract the storage path from the URL
|
|
97
|
+
const storageRef = ref(storage, photoUrl);
|
|
98
|
+
|
|
99
|
+
// Delete the file
|
|
100
|
+
await deleteObject(storageRef);
|
|
101
|
+
|
|
102
|
+
console.log(`[PHOTO_UTILS] Photo deleted successfully`);
|
|
103
|
+
return true;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(`[PHOTO_UTILS] Error deleting photo:`, error);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Uploads multiple photos to Firebase Storage
|
|
112
|
+
* @param photos - Array of photo data URLs or URLs
|
|
113
|
+
* @param entityType - The type of entity (clinic-groups, clinics, etc.)
|
|
114
|
+
* @param entityId - The ID of the entity
|
|
115
|
+
* @param photoType - The type of photos (featured, gallery, etc.)
|
|
116
|
+
* @param app - Firebase app instance
|
|
117
|
+
* @returns Array of URLs of the uploaded photos
|
|
118
|
+
*/
|
|
119
|
+
export async function uploadMultiplePhotos(
|
|
120
|
+
photos: string[],
|
|
121
|
+
entityType: string,
|
|
122
|
+
entityId: string,
|
|
123
|
+
photoType: string,
|
|
124
|
+
app: FirebaseApp
|
|
125
|
+
): Promise<string[]> {
|
|
126
|
+
if (!photos || !Array.isArray(photos) || photos.length === 0) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const uploadPromises = photos.map((photo, index) =>
|
|
131
|
+
uploadPhoto(photo, entityType, entityId, `${photoType}-${index}`, app)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const results = await Promise.all(uploadPromises);
|
|
136
|
+
return results.filter((url): url is string => url !== null);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error(`[PHOTO_UTILS] Error uploading multiple photos:`, error);
|
|
139
|
+
return photos.filter((photo) => !photo.startsWith("data:"));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Processes photos in an entity object, uploading any data URLs
|
|
145
|
+
* @param entity - The entity object containing photo fields
|
|
146
|
+
* @param entityType - The type of entity (clinic-groups, clinics, etc.)
|
|
147
|
+
* @param entityId - The ID of the entity
|
|
148
|
+
* @param photoFields - Array of field names that contain photos
|
|
149
|
+
* @param app - Firebase app instance
|
|
150
|
+
* @returns The entity with updated photo URLs
|
|
151
|
+
*/
|
|
152
|
+
export async function processEntityPhotos<T extends Record<string, any>>(
|
|
153
|
+
entity: T,
|
|
154
|
+
entityType: string,
|
|
155
|
+
entityId: string,
|
|
156
|
+
photoFields: { field: keyof T; type: string }[],
|
|
157
|
+
app: FirebaseApp
|
|
158
|
+
): Promise<T> {
|
|
159
|
+
const updatedEntity = { ...entity };
|
|
160
|
+
|
|
161
|
+
for (const { field, type } of photoFields) {
|
|
162
|
+
const value = entity[field];
|
|
163
|
+
|
|
164
|
+
if (!value) continue;
|
|
165
|
+
|
|
166
|
+
if (Array.isArray(value)) {
|
|
167
|
+
// Handle array of photos
|
|
168
|
+
updatedEntity[field] = (await uploadMultiplePhotos(
|
|
169
|
+
value,
|
|
170
|
+
entityType,
|
|
171
|
+
entityId,
|
|
172
|
+
type,
|
|
173
|
+
app
|
|
174
|
+
)) as any;
|
|
175
|
+
} else if (typeof value === "string") {
|
|
176
|
+
// Handle single photo
|
|
177
|
+
updatedEntity[field] = (await uploadPhoto(
|
|
178
|
+
value,
|
|
179
|
+
entityType,
|
|
180
|
+
entityId,
|
|
181
|
+
type,
|
|
182
|
+
app
|
|
183
|
+
)) as any;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return updatedEntity;
|
|
188
|
+
}
|
|
@@ -4,16 +4,20 @@ import {
|
|
|
4
4
|
setDoc,
|
|
5
5
|
Timestamp,
|
|
6
6
|
Firestore,
|
|
7
|
+
getDoc,
|
|
8
|
+
addDoc,
|
|
7
9
|
} from "firebase/firestore";
|
|
8
10
|
import { Clinic, ClinicReview } from "../../../types/clinic";
|
|
9
11
|
import { clinicReviewSchema } from "../../../validations/clinic.schema";
|
|
10
12
|
import { getClinic, updateClinic } from "./clinic.utils";
|
|
13
|
+
import { FirebaseApp } from "firebase/app";
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Adds a review to a clinic
|
|
14
17
|
* @param db - Firestore database instance
|
|
15
|
-
* @param clinicId - ID of the clinic to review
|
|
18
|
+
* @param clinicId - ID of the clinic to add the review to
|
|
16
19
|
* @param review - Review data
|
|
20
|
+
* @param app - Firebase app instance
|
|
17
21
|
* @returns The created review
|
|
18
22
|
*/
|
|
19
23
|
export async function addReview(
|
|
@@ -22,20 +26,25 @@ export async function addReview(
|
|
|
22
26
|
review: Omit<
|
|
23
27
|
ClinicReview,
|
|
24
28
|
"id" | "clinicId" | "createdAt" | "updatedAt" | "isVerified"
|
|
25
|
-
|
|
29
|
+
>,
|
|
30
|
+
app: FirebaseApp
|
|
26
31
|
): Promise<ClinicReview> {
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
// Proveravamo da li klinika postoji
|
|
33
|
+
const clinicRef = doc(db, "clinics", clinicId);
|
|
34
|
+
const clinicSnap = await getDoc(clinicRef);
|
|
35
|
+
|
|
36
|
+
if (!clinicSnap.exists()) {
|
|
29
37
|
throw new Error("Clinic not found");
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
const clinic = clinicSnap.data();
|
|
41
|
+
|
|
42
|
+
// Kreiramo recenziju
|
|
32
43
|
const now = Timestamp.now();
|
|
33
44
|
const reviewData: ClinicReview = {
|
|
45
|
+
...review,
|
|
34
46
|
id: doc(collection(db, "clinic_reviews")).id,
|
|
35
47
|
clinicId,
|
|
36
|
-
patientId: review.patientId,
|
|
37
|
-
rating: review.rating,
|
|
38
|
-
comment: review.comment,
|
|
39
48
|
createdAt: now,
|
|
40
49
|
updatedAt: now,
|
|
41
50
|
isVerified: false,
|
|
@@ -45,7 +54,7 @@ export async function addReview(
|
|
|
45
54
|
clinicReviewSchema.parse(reviewData);
|
|
46
55
|
|
|
47
56
|
// Čuvamo recenziju
|
|
48
|
-
await
|
|
57
|
+
await addDoc(collection(db, "clinic_reviews"), reviewData);
|
|
49
58
|
|
|
50
59
|
// Ažuriramo prosečnu ocenu klinike
|
|
51
60
|
const newRating = clinic.rating
|
|
@@ -68,18 +77,16 @@ export async function addReview(
|
|
|
68
77
|
...clinic.reviewsInfo,
|
|
69
78
|
{
|
|
70
79
|
id: reviewData.id,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
patientPhoto: "", // This should be fetched from patient service
|
|
76
|
-
createdAt: now,
|
|
77
|
-
updatedAt: now,
|
|
80
|
+
patientId: reviewData.patientId,
|
|
81
|
+
rating: reviewData.rating,
|
|
82
|
+
comment: reviewData.comment,
|
|
83
|
+
createdAt: reviewData.createdAt,
|
|
78
84
|
},
|
|
79
85
|
],
|
|
80
86
|
},
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
"system", // System update, no admin ID needed
|
|
88
|
+
null, // No clinic admin service needed for system updates
|
|
89
|
+
app
|
|
83
90
|
);
|
|
84
91
|
|
|
85
92
|
return reviewData;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Firestore } from "firebase/firestore";
|
|
2
2
|
import { Clinic, ClinicTag } from "../../../types/clinic";
|
|
3
3
|
import { getClinic, updateClinic } from "./clinic.utils";
|
|
4
|
+
import { FirebaseApp } from "firebase/app";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Adds tags to a clinic
|
|
@@ -9,6 +10,7 @@ import { getClinic, updateClinic } from "./clinic.utils";
|
|
|
9
10
|
* @param adminId - ID of the admin making the change
|
|
10
11
|
* @param newTags - Tags to add
|
|
11
12
|
* @param clinicAdminService - Service for clinic admin operations
|
|
13
|
+
* @param app - Firebase app instance
|
|
12
14
|
* @returns The updated clinic
|
|
13
15
|
*/
|
|
14
16
|
export async function addTags(
|
|
@@ -18,7 +20,8 @@ export async function addTags(
|
|
|
18
20
|
newTags: {
|
|
19
21
|
tags?: ClinicTag[];
|
|
20
22
|
},
|
|
21
|
-
clinicAdminService: any
|
|
23
|
+
clinicAdminService: any,
|
|
24
|
+
app: FirebaseApp
|
|
22
25
|
): Promise<Clinic> {
|
|
23
26
|
const clinic = await getClinic(db, clinicId);
|
|
24
27
|
if (!clinic) {
|
|
@@ -31,7 +34,16 @@ export async function addTags(
|
|
|
31
34
|
throw new Error("Admin not found");
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
if
|
|
37
|
+
// Check if admin is either:
|
|
38
|
+
// 1. The owner of the clinic group that this clinic belongs to, OR
|
|
39
|
+
// 2. Has this clinic in their managed clinics list AND is listed in the clinic's admins array
|
|
40
|
+
const hasPermission =
|
|
41
|
+
(admin.isGroupOwner && admin.clinicGroupId === clinic.clinicGroupId) ||
|
|
42
|
+
(admin.clinicsManaged.includes(clinicId) &&
|
|
43
|
+
clinic.admins &&
|
|
44
|
+
clinic.admins.includes(adminId));
|
|
45
|
+
|
|
46
|
+
if (!hasPermission) {
|
|
35
47
|
throw new Error("Admin does not have permission to update this clinic");
|
|
36
48
|
}
|
|
37
49
|
|
|
@@ -45,7 +57,8 @@ export async function addTags(
|
|
|
45
57
|
tags: updatedTags,
|
|
46
58
|
},
|
|
47
59
|
adminId,
|
|
48
|
-
clinicAdminService
|
|
60
|
+
clinicAdminService,
|
|
61
|
+
app
|
|
49
62
|
);
|
|
50
63
|
}
|
|
51
64
|
|
|
@@ -56,6 +69,7 @@ export async function addTags(
|
|
|
56
69
|
* @param adminId - ID of the admin making the change
|
|
57
70
|
* @param tagsToRemove - Tags to remove
|
|
58
71
|
* @param clinicAdminService - Service for clinic admin operations
|
|
72
|
+
* @param app - Firebase app instance
|
|
59
73
|
* @returns The updated clinic
|
|
60
74
|
*/
|
|
61
75
|
export async function removeTags(
|
|
@@ -65,7 +79,8 @@ export async function removeTags(
|
|
|
65
79
|
tagsToRemove: {
|
|
66
80
|
tags?: ClinicTag[];
|
|
67
81
|
},
|
|
68
|
-
clinicAdminService: any
|
|
82
|
+
clinicAdminService: any,
|
|
83
|
+
app: FirebaseApp
|
|
69
84
|
): Promise<Clinic> {
|
|
70
85
|
const clinic = await getClinic(db, clinicId);
|
|
71
86
|
if (!clinic) {
|
|
@@ -78,13 +93,22 @@ export async function removeTags(
|
|
|
78
93
|
throw new Error("Admin not found");
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
if
|
|
96
|
+
// Check if admin is either:
|
|
97
|
+
// 1. The owner of the clinic group that this clinic belongs to, OR
|
|
98
|
+
// 2. Has this clinic in their managed clinics list AND is listed in the clinic's admins array
|
|
99
|
+
const hasPermission =
|
|
100
|
+
(admin.isGroupOwner && admin.clinicGroupId === clinic.clinicGroupId) ||
|
|
101
|
+
(admin.clinicsManaged.includes(clinicId) &&
|
|
102
|
+
clinic.admins &&
|
|
103
|
+
clinic.admins.includes(adminId));
|
|
104
|
+
|
|
105
|
+
if (!hasPermission) {
|
|
82
106
|
throw new Error("Admin does not have permission to update this clinic");
|
|
83
107
|
}
|
|
84
108
|
|
|
85
|
-
//
|
|
109
|
+
// Remove specified tags
|
|
86
110
|
const updatedTags = clinic.tags.filter(
|
|
87
|
-
(tag) => !tagsToRemove.tags
|
|
111
|
+
(tag) => !tagsToRemove.tags || !tagsToRemove.tags.includes(tag)
|
|
88
112
|
);
|
|
89
113
|
|
|
90
114
|
return updateClinic(
|
|
@@ -94,6 +118,7 @@ export async function removeTags(
|
|
|
94
118
|
tags: updatedTags,
|
|
95
119
|
},
|
|
96
120
|
adminId,
|
|
97
|
-
clinicAdminService
|
|
121
|
+
clinicAdminService,
|
|
122
|
+
app
|
|
98
123
|
);
|
|
99
124
|
}
|
|
@@ -107,7 +107,7 @@ export interface ClinicInfo {
|
|
|
107
107
|
export interface ClinicAdmin {
|
|
108
108
|
id: string;
|
|
109
109
|
userRef: string;
|
|
110
|
-
clinicGroupId: string;
|
|
110
|
+
clinicGroupId: string; // Can be empty string initially for owners
|
|
111
111
|
isGroupOwner: boolean;
|
|
112
112
|
clinicsManaged: string[];
|
|
113
113
|
clinicsManagedInfo: ClinicInfo[];
|
|
@@ -195,7 +195,7 @@ export interface ClinicGroup {
|
|
|
195
195
|
admins: string[];
|
|
196
196
|
adminsInfo: AdminInfo[];
|
|
197
197
|
adminTokens: AdminToken[];
|
|
198
|
-
ownerId: string;
|
|
198
|
+
ownerId: string | null;
|
|
199
199
|
createdAt: Timestamp;
|
|
200
200
|
updatedAt: Timestamp;
|
|
201
201
|
isActive: boolean;
|
|
@@ -205,6 +205,7 @@ export interface ClinicGroup {
|
|
|
205
205
|
subscriptionModel: SubscriptionModel;
|
|
206
206
|
calendarSyncEnabled?: boolean;
|
|
207
207
|
autoConfirmAppointments?: boolean;
|
|
208
|
+
businessIdentificationNumber?: string | null;
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
/**
|
|
@@ -216,7 +217,7 @@ export interface CreateClinicGroupData {
|
|
|
216
217
|
hqLocation: ClinicLocation;
|
|
217
218
|
contactInfo: ClinicContactInfo;
|
|
218
219
|
contactPerson: ContactPerson;
|
|
219
|
-
ownerId: string;
|
|
220
|
+
ownerId: string | null;
|
|
220
221
|
isActive: boolean;
|
|
221
222
|
logo?: string | null;
|
|
222
223
|
practiceType?: PracticeType;
|
|
@@ -224,6 +225,7 @@ export interface CreateClinicGroupData {
|
|
|
224
225
|
subscriptionModel?: SubscriptionModel;
|
|
225
226
|
calendarSyncEnabled?: boolean;
|
|
226
227
|
autoConfirmAppointments?: boolean;
|
|
228
|
+
businessIdentificationNumber?: string | null;
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
/**
|
|
@@ -355,12 +357,12 @@ export interface UpdateClinicData extends Partial<CreateClinicData> {
|
|
|
355
357
|
*/
|
|
356
358
|
export interface CreateDefaultClinicGroupData {
|
|
357
359
|
name: string;
|
|
358
|
-
ownerId: string;
|
|
360
|
+
ownerId: string | null;
|
|
359
361
|
contactPerson: ContactPerson;
|
|
360
362
|
contactInfo: ClinicContactInfo;
|
|
361
363
|
hqLocation: ClinicLocation;
|
|
362
364
|
isActive: boolean;
|
|
363
|
-
logo?: string;
|
|
365
|
+
logo?: string | null;
|
|
364
366
|
practiceType?: PracticeType;
|
|
365
367
|
languages?: Language[];
|
|
366
368
|
subscriptionModel?: SubscriptionModel;
|
|
@@ -397,6 +399,7 @@ export interface ClinicGroupSetupData {
|
|
|
397
399
|
logo: string;
|
|
398
400
|
calendarSyncEnabled: boolean;
|
|
399
401
|
autoConfirmAppointments: boolean;
|
|
402
|
+
businessIdentificationNumber: string | null;
|
|
400
403
|
}
|
|
401
404
|
|
|
402
405
|
/**
|
|
@@ -200,7 +200,7 @@ export const clinicGroupSchema = z.object({
|
|
|
200
200
|
admins: z.array(z.string()),
|
|
201
201
|
adminsInfo: z.array(adminInfoSchema),
|
|
202
202
|
adminTokens: z.array(adminTokenSchema),
|
|
203
|
-
ownerId: z.string(),
|
|
203
|
+
ownerId: z.string().nullable(),
|
|
204
204
|
createdAt: z.instanceof(Date).or(z.instanceof(Timestamp)), // Timestamp
|
|
205
205
|
updatedAt: z.instanceof(Date).or(z.instanceof(Timestamp)), // Timestamp
|
|
206
206
|
isActive: z.boolean(),
|
|
@@ -210,6 +210,7 @@ export const clinicGroupSchema = z.object({
|
|
|
210
210
|
subscriptionModel: z.nativeEnum(SubscriptionModel),
|
|
211
211
|
calendarSyncEnabled: z.boolean().optional(),
|
|
212
212
|
autoConfirmAppointments: z.boolean().optional(),
|
|
213
|
+
businessIdentificationNumber: z.string().optional().nullable(),
|
|
213
214
|
});
|
|
214
215
|
|
|
215
216
|
/**
|
|
@@ -290,7 +291,7 @@ export const createClinicGroupSchema = z.object({
|
|
|
290
291
|
hqLocation: clinicLocationSchema,
|
|
291
292
|
contactInfo: clinicContactInfoSchema,
|
|
292
293
|
contactPerson: contactPersonSchema,
|
|
293
|
-
ownerId: z.string(),
|
|
294
|
+
ownerId: z.string().nullable(),
|
|
294
295
|
isActive: z.boolean(),
|
|
295
296
|
logo: z.string().optional().nullable(),
|
|
296
297
|
practiceType: z.nativeEnum(PracticeType).optional(),
|
|
@@ -301,6 +302,7 @@ export const createClinicGroupSchema = z.object({
|
|
|
301
302
|
.default(SubscriptionModel.NO_SUBSCRIPTION),
|
|
302
303
|
calendarSyncEnabled: z.boolean().optional(),
|
|
303
304
|
autoConfirmAppointments: z.boolean().optional(),
|
|
305
|
+
businessIdentificationNumber: z.string().optional().nullable(),
|
|
304
306
|
});
|
|
305
307
|
|
|
306
308
|
/**
|
|
@@ -337,7 +339,7 @@ export const createClinicSchema = z.object({
|
|
|
337
339
|
*/
|
|
338
340
|
export const createDefaultClinicGroupSchema = z.object({
|
|
339
341
|
name: z.string(),
|
|
340
|
-
ownerId: z.string(),
|
|
342
|
+
ownerId: z.string().nullable(),
|
|
341
343
|
contactPerson: contactPersonSchema,
|
|
342
344
|
contactInfo: clinicContactInfoSchema,
|
|
343
345
|
hqLocation: clinicLocationSchema,
|
|
@@ -387,6 +389,7 @@ export const clinicGroupSetupSchema = z.object({
|
|
|
387
389
|
logo: z.string(),
|
|
388
390
|
calendarSyncEnabled: z.boolean(),
|
|
389
391
|
autoConfirmAppointments: z.boolean(),
|
|
392
|
+
businessIdentificationNumber: z.string().optional().nullable(),
|
|
390
393
|
});
|
|
391
394
|
|
|
392
395
|
/**
|