@blackcode_sa/metaestetics-api 1.4.7 → 1.4.9
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 +16 -16
- package/dist/index.d.ts +16 -16
- package/dist/index.js +768 -188
- package/dist/index.mjs +787 -200
- package/package.json +1 -1
- package/src/services/auth.service.ts +52 -8
- 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 +195 -52
- 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 +5 -5
- package/src/validations/clinic.schema.ts +3 -3
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
Firestore,
|
|
13
13
|
serverTimestamp,
|
|
14
14
|
} from "firebase/firestore";
|
|
15
|
+
import { getStorage, ref, uploadBytes, getDownloadURL } from "firebase/storage";
|
|
15
16
|
import {
|
|
16
17
|
ClinicGroup,
|
|
17
18
|
CreateClinicGroupData,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
AdminToken,
|
|
20
21
|
AdminTokenStatus,
|
|
21
22
|
CreateAdminTokenData,
|
|
23
|
+
SubscriptionModel,
|
|
22
24
|
} from "../../../types/clinic";
|
|
23
25
|
import { geohashForLocation } from "geofire-common";
|
|
24
26
|
import {
|
|
@@ -26,6 +28,8 @@ import {
|
|
|
26
28
|
createClinicGroupSchema,
|
|
27
29
|
} from "../../../validations/clinic.schema";
|
|
28
30
|
import { z } from "zod";
|
|
31
|
+
import { uploadPhoto } from "./photos.utils";
|
|
32
|
+
import { FirebaseApp } from "firebase/app";
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* Generates a unique ID for documents
|
|
@@ -50,6 +54,7 @@ function generateId(): string {
|
|
|
50
54
|
* @param ownerId - ID of the owner
|
|
51
55
|
* @param isDefault - Whether this is a default group
|
|
52
56
|
* @param clinicAdminService - Service for clinic admin operations
|
|
57
|
+
* @param app - Firebase app instance
|
|
53
58
|
* @returns The created clinic group
|
|
54
59
|
*/
|
|
55
60
|
export async function createClinicGroup(
|
|
@@ -57,7 +62,8 @@ export async function createClinicGroup(
|
|
|
57
62
|
data: CreateClinicGroupData,
|
|
58
63
|
ownerId: string,
|
|
59
64
|
isDefault: boolean = false,
|
|
60
|
-
clinicAdminService: any
|
|
65
|
+
clinicAdminService: any,
|
|
66
|
+
app: FirebaseApp
|
|
61
67
|
): Promise<ClinicGroup> {
|
|
62
68
|
console.log("[CLINIC_GROUP] Starting clinic group creation", {
|
|
63
69
|
ownerId,
|
|
@@ -79,14 +85,24 @@ export async function createClinicGroup(
|
|
|
79
85
|
// Proveravamo da li owner postoji i da li je clinic admin
|
|
80
86
|
try {
|
|
81
87
|
console.log("[CLINIC_GROUP] Checking if owner exists", { ownerId });
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
console.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
// Skip owner verification for default groups since the admin profile doesn't exist yet
|
|
89
|
+
if (isDefault) {
|
|
90
|
+
console.log(
|
|
91
|
+
"[CLINIC_GROUP] Skipping owner verification for default group creation"
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
const owner = await clinicAdminService.getClinicAdmin(ownerId);
|
|
95
|
+
if (!owner) {
|
|
96
|
+
console.error(
|
|
97
|
+
"[CLINIC_GROUP] Owner not found or is not a clinic admin",
|
|
98
|
+
{
|
|
99
|
+
ownerId,
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
throw new Error("Owner not found or is not a clinic admin");
|
|
103
|
+
}
|
|
104
|
+
console.log("[CLINIC_GROUP] Owner verified as clinic admin");
|
|
88
105
|
}
|
|
89
|
-
console.log("[CLINIC_GROUP] Owner verified as clinic admin");
|
|
90
106
|
} catch (ownerError) {
|
|
91
107
|
console.error("[CLINIC_GROUP] Error verifying owner:", ownerError);
|
|
92
108
|
throw ownerError;
|
|
@@ -112,22 +128,43 @@ export async function createClinicGroup(
|
|
|
112
128
|
const now = Timestamp.now();
|
|
113
129
|
console.log("[CLINIC_GROUP] Preparing clinic group data object");
|
|
114
130
|
|
|
131
|
+
// Generate a unique ID for the clinic group
|
|
132
|
+
const groupId = doc(collection(db, CLINIC_GROUPS_COLLECTION)).id;
|
|
133
|
+
|
|
115
134
|
// Log the logo value to debug null vs undefined issue
|
|
116
135
|
console.log("[CLINIC_GROUP] Logo value:", {
|
|
117
136
|
logoValue: validatedData.logo,
|
|
118
137
|
logoType: validatedData.logo === null ? "null" : typeof validatedData.logo,
|
|
119
138
|
});
|
|
120
139
|
|
|
140
|
+
// Handle logo upload if provided
|
|
141
|
+
let logoUrl = await uploadPhoto(
|
|
142
|
+
validatedData.logo || null,
|
|
143
|
+
"clinic-groups",
|
|
144
|
+
groupId,
|
|
145
|
+
"logo",
|
|
146
|
+
app
|
|
147
|
+
);
|
|
148
|
+
console.log("[CLINIC_GROUP] Logo processed", { logoUrl });
|
|
149
|
+
|
|
121
150
|
const groupData: ClinicGroup = {
|
|
122
151
|
...validatedData,
|
|
123
|
-
id:
|
|
152
|
+
id: groupId,
|
|
153
|
+
name: validatedData.name,
|
|
154
|
+
logo: logoUrl, // Use the uploaded logo URL or the original value
|
|
124
155
|
description: isDefault ? undefined : validatedData.description || undefined,
|
|
125
156
|
hqLocation: {
|
|
126
|
-
|
|
157
|
+
address: validatedData.hqLocation.address || "",
|
|
158
|
+
city: validatedData.hqLocation.city || "",
|
|
159
|
+
country: validatedData.hqLocation.country || "",
|
|
160
|
+
postalCode: validatedData.hqLocation.postalCode || "",
|
|
161
|
+
latitude: validatedData.hqLocation.latitude || 0,
|
|
162
|
+
longitude: validatedData.hqLocation.longitude || 0,
|
|
127
163
|
geohash: validatedData.hqLocation.geohash || undefined,
|
|
128
164
|
},
|
|
129
165
|
contactInfo: {
|
|
130
|
-
|
|
166
|
+
email: validatedData.contactInfo.email || "",
|
|
167
|
+
phoneNumber: validatedData.contactInfo.phoneNumber || "",
|
|
131
168
|
alternativePhoneNumber: isDefault
|
|
132
169
|
? undefined
|
|
133
170
|
: validatedData.contactInfo.alternativePhoneNumber || undefined,
|
|
@@ -135,6 +172,15 @@ export async function createClinicGroup(
|
|
|
135
172
|
? undefined
|
|
136
173
|
: validatedData.contactInfo.website || undefined,
|
|
137
174
|
},
|
|
175
|
+
contactPerson: {
|
|
176
|
+
firstName: validatedData.contactPerson?.firstName || "",
|
|
177
|
+
lastName: validatedData.contactPerson?.lastName || "",
|
|
178
|
+
email: validatedData.contactPerson?.email || "",
|
|
179
|
+
title: validatedData.contactPerson?.title || null,
|
|
180
|
+
phoneNumber: validatedData.contactPerson?.phoneNumber || null,
|
|
181
|
+
},
|
|
182
|
+
subscriptionModel:
|
|
183
|
+
validatedData.subscriptionModel || SubscriptionModel.NO_SUBSCRIPTION,
|
|
138
184
|
clinics: [],
|
|
139
185
|
clinicsInfo: [],
|
|
140
186
|
admins: [ownerId],
|
|
@@ -256,29 +302,64 @@ export async function getAllActiveGroups(
|
|
|
256
302
|
* @param db - Firestore database instance
|
|
257
303
|
* @param groupId - ID of the clinic group
|
|
258
304
|
* @param data - Data to update
|
|
305
|
+
* @param app - Firebase app instance
|
|
259
306
|
* @returns The updated clinic group
|
|
260
307
|
*/
|
|
261
308
|
export async function updateClinicGroup(
|
|
262
309
|
db: Firestore,
|
|
263
310
|
groupId: string,
|
|
264
|
-
data: Partial<ClinicGroup
|
|
311
|
+
data: Partial<ClinicGroup>,
|
|
312
|
+
app: FirebaseApp
|
|
265
313
|
): Promise<ClinicGroup> {
|
|
314
|
+
console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
|
|
315
|
+
|
|
266
316
|
const group = await getClinicGroup(db, groupId);
|
|
267
317
|
if (!group) {
|
|
318
|
+
console.error("[CLINIC_GROUP] Clinic group not found", { groupId });
|
|
268
319
|
throw new Error("Clinic group not found");
|
|
269
320
|
}
|
|
270
321
|
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
322
|
+
// Process logo if it's a data URL
|
|
323
|
+
let updatedData = { ...data };
|
|
324
|
+
|
|
325
|
+
if (
|
|
326
|
+
data.logo &&
|
|
327
|
+
typeof data.logo === "string" &&
|
|
328
|
+
data.logo.startsWith("data:")
|
|
329
|
+
) {
|
|
330
|
+
console.log("[CLINIC_GROUP] Processing logo for update");
|
|
331
|
+
try {
|
|
332
|
+
const logoUrl = await uploadPhoto(
|
|
333
|
+
data.logo,
|
|
334
|
+
"clinic-groups",
|
|
335
|
+
groupId,
|
|
336
|
+
"logo",
|
|
337
|
+
app
|
|
338
|
+
);
|
|
339
|
+
console.log("[CLINIC_GROUP] Logo processed for update", { logoUrl });
|
|
340
|
+
|
|
341
|
+
// Replace the data URL with the uploaded URL
|
|
342
|
+
updatedData.logo = logoUrl;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error("[CLINIC_GROUP] Error processing logo for update:", error);
|
|
345
|
+
// Continue with update even if logo upload fails
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Add timestamp
|
|
350
|
+
updatedData = {
|
|
351
|
+
...updatedData,
|
|
274
352
|
updatedAt: Timestamp.now(),
|
|
275
353
|
};
|
|
276
354
|
|
|
355
|
+
console.log("[CLINIC_GROUP] Updating clinic group in Firestore");
|
|
277
356
|
await updateDoc(doc(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
|
|
357
|
+
console.log("[CLINIC_GROUP] Clinic group updated successfully");
|
|
278
358
|
|
|
279
|
-
//
|
|
359
|
+
// Return updated data
|
|
280
360
|
const updatedGroup = await getClinicGroup(db, groupId);
|
|
281
361
|
if (!updatedGroup) {
|
|
362
|
+
console.error("[CLINIC_GROUP] Failed to retrieve updated clinic group");
|
|
282
363
|
throw new Error("Failed to retrieve updated clinic group");
|
|
283
364
|
}
|
|
284
365
|
|
|
@@ -289,25 +370,46 @@ export async function updateClinicGroup(
|
|
|
289
370
|
* Adds an admin to a clinic group
|
|
290
371
|
* @param db - Firestore database instance
|
|
291
372
|
* @param groupId - ID of the clinic group
|
|
292
|
-
* @param adminId - ID of the admin to add
|
|
373
|
+
* @param adminId - ID of the admin to add (this is the admin document ID, not the user UID)
|
|
374
|
+
* @param app - Firebase app instance
|
|
293
375
|
*/
|
|
294
376
|
export async function addAdminToGroup(
|
|
295
377
|
db: Firestore,
|
|
296
378
|
groupId: string,
|
|
297
|
-
adminId: string
|
|
379
|
+
adminId: string,
|
|
380
|
+
app: FirebaseApp
|
|
298
381
|
): Promise<void> {
|
|
299
|
-
|
|
300
|
-
if (!group) {
|
|
301
|
-
throw new Error("Clinic group not found");
|
|
302
|
-
}
|
|
382
|
+
console.log("[CLINIC_GROUP] Adding admin to group", { groupId, adminId });
|
|
303
383
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
384
|
+
try {
|
|
385
|
+
const group = await getClinicGroup(db, groupId);
|
|
386
|
+
if (!group) {
|
|
387
|
+
console.error("[CLINIC_GROUP] Clinic group not found", { groupId });
|
|
388
|
+
throw new Error("Clinic group not found");
|
|
389
|
+
}
|
|
307
390
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
391
|
+
if (group.admins.includes(adminId)) {
|
|
392
|
+
console.log("[CLINIC_GROUP] Admin is already in the group", {
|
|
393
|
+
adminId,
|
|
394
|
+
groupId,
|
|
395
|
+
});
|
|
396
|
+
return; // Admin is already in the group
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
console.log("[CLINIC_GROUP] Updating group with new admin");
|
|
400
|
+
await updateClinicGroup(
|
|
401
|
+
db,
|
|
402
|
+
groupId,
|
|
403
|
+
{
|
|
404
|
+
admins: [...group.admins, adminId],
|
|
405
|
+
},
|
|
406
|
+
app
|
|
407
|
+
);
|
|
408
|
+
console.log("[CLINIC_GROUP] Admin added to group successfully");
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error("[CLINIC_GROUP] Error adding admin to group:", error);
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
311
413
|
}
|
|
312
414
|
|
|
313
415
|
/**
|
|
@@ -315,11 +417,13 @@ export async function addAdminToGroup(
|
|
|
315
417
|
* @param db - Firestore database instance
|
|
316
418
|
* @param groupId - ID of the clinic group
|
|
317
419
|
* @param adminId - ID of the admin to remove
|
|
420
|
+
* @param app - Firebase app instance
|
|
318
421
|
*/
|
|
319
422
|
export async function removeAdminFromGroup(
|
|
320
423
|
db: Firestore,
|
|
321
424
|
groupId: string,
|
|
322
|
-
adminId: string
|
|
425
|
+
adminId: string,
|
|
426
|
+
app: FirebaseApp
|
|
323
427
|
): Promise<void> {
|
|
324
428
|
const group = await getClinicGroup(db, groupId);
|
|
325
429
|
if (!group) {
|
|
@@ -334,29 +438,40 @@ export async function removeAdminFromGroup(
|
|
|
334
438
|
return; // Admin is not in the group
|
|
335
439
|
}
|
|
336
440
|
|
|
337
|
-
await updateClinicGroup(
|
|
338
|
-
|
|
339
|
-
|
|
441
|
+
await updateClinicGroup(
|
|
442
|
+
db,
|
|
443
|
+
groupId,
|
|
444
|
+
{
|
|
445
|
+
admins: group.admins.filter((id) => id !== adminId),
|
|
446
|
+
},
|
|
447
|
+
app
|
|
448
|
+
);
|
|
340
449
|
}
|
|
341
450
|
|
|
342
451
|
/**
|
|
343
452
|
* Deactivates a clinic group
|
|
344
453
|
* @param db - Firestore database instance
|
|
345
454
|
* @param groupId - ID of the clinic group
|
|
455
|
+
* @param app - Firebase app instance
|
|
346
456
|
*/
|
|
347
457
|
export async function deactivateClinicGroup(
|
|
348
458
|
db: Firestore,
|
|
349
|
-
groupId: string
|
|
459
|
+
groupId: string,
|
|
460
|
+
app: FirebaseApp
|
|
350
461
|
): Promise<void> {
|
|
351
462
|
const group = await getClinicGroup(db, groupId);
|
|
352
463
|
if (!group) {
|
|
353
464
|
throw new Error("Clinic group not found");
|
|
354
465
|
}
|
|
355
466
|
|
|
356
|
-
await
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
467
|
+
await updateClinicGroup(
|
|
468
|
+
db,
|
|
469
|
+
groupId,
|
|
470
|
+
{
|
|
471
|
+
isActive: false,
|
|
472
|
+
},
|
|
473
|
+
app
|
|
474
|
+
);
|
|
360
475
|
}
|
|
361
476
|
|
|
362
477
|
/**
|
|
@@ -364,6 +479,7 @@ export async function deactivateClinicGroup(
|
|
|
364
479
|
* @param db - Firestore database instance
|
|
365
480
|
* @param groupId - ID of the clinic group
|
|
366
481
|
* @param creatorAdminId - ID of the admin creating the token
|
|
482
|
+
* @param app - Firebase app instance
|
|
367
483
|
* @param data - Token data
|
|
368
484
|
* @returns The created admin token
|
|
369
485
|
*/
|
|
@@ -371,6 +487,7 @@ export async function createAdminToken(
|
|
|
371
487
|
db: Firestore,
|
|
372
488
|
groupId: string,
|
|
373
489
|
creatorAdminId: string,
|
|
490
|
+
app: FirebaseApp,
|
|
374
491
|
data?: CreateAdminTokenData
|
|
375
492
|
): Promise<AdminToken> {
|
|
376
493
|
const group = await getClinicGroup(db, groupId);
|
|
@@ -399,9 +516,14 @@ export async function createAdminToken(
|
|
|
399
516
|
};
|
|
400
517
|
|
|
401
518
|
// Dodajemo token u grupu
|
|
402
|
-
await updateClinicGroup(
|
|
403
|
-
|
|
404
|
-
|
|
519
|
+
await updateClinicGroup(
|
|
520
|
+
db,
|
|
521
|
+
groupId,
|
|
522
|
+
{
|
|
523
|
+
adminTokens: [...group.adminTokens, token],
|
|
524
|
+
},
|
|
525
|
+
app
|
|
526
|
+
);
|
|
405
527
|
|
|
406
528
|
return token;
|
|
407
529
|
}
|
|
@@ -412,13 +534,15 @@ export async function createAdminToken(
|
|
|
412
534
|
* @param groupId - ID of the clinic group
|
|
413
535
|
* @param token - Token to verify
|
|
414
536
|
* @param userRef - User reference
|
|
537
|
+
* @param app - Firebase app instance
|
|
415
538
|
* @returns Whether the token was successfully used
|
|
416
539
|
*/
|
|
417
540
|
export async function verifyAndUseAdminToken(
|
|
418
541
|
db: Firestore,
|
|
419
542
|
groupId: string,
|
|
420
543
|
token: string,
|
|
421
|
-
userRef: string
|
|
544
|
+
userRef: string,
|
|
545
|
+
app: FirebaseApp
|
|
422
546
|
): Promise<boolean> {
|
|
423
547
|
const group = await getClinicGroup(db, groupId);
|
|
424
548
|
if (!group) {
|
|
@@ -441,9 +565,14 @@ export async function verifyAndUseAdminToken(
|
|
|
441
565
|
t.id === adminToken.id ? { ...t, status: AdminTokenStatus.EXPIRED } : t
|
|
442
566
|
);
|
|
443
567
|
|
|
444
|
-
await updateClinicGroup(
|
|
445
|
-
|
|
446
|
-
|
|
568
|
+
await updateClinicGroup(
|
|
569
|
+
db,
|
|
570
|
+
groupId,
|
|
571
|
+
{
|
|
572
|
+
adminTokens: updatedTokens,
|
|
573
|
+
},
|
|
574
|
+
app
|
|
575
|
+
);
|
|
447
576
|
|
|
448
577
|
throw new Error("Admin token has expired");
|
|
449
578
|
}
|
|
@@ -459,9 +588,14 @@ export async function verifyAndUseAdminToken(
|
|
|
459
588
|
: t
|
|
460
589
|
);
|
|
461
590
|
|
|
462
|
-
await updateClinicGroup(
|
|
463
|
-
|
|
464
|
-
|
|
591
|
+
await updateClinicGroup(
|
|
592
|
+
db,
|
|
593
|
+
groupId,
|
|
594
|
+
{
|
|
595
|
+
adminTokens: updatedTokens,
|
|
596
|
+
},
|
|
597
|
+
app
|
|
598
|
+
);
|
|
465
599
|
|
|
466
600
|
return true;
|
|
467
601
|
}
|
|
@@ -472,12 +606,14 @@ export async function verifyAndUseAdminToken(
|
|
|
472
606
|
* @param groupId - ID of the clinic group
|
|
473
607
|
* @param tokenId - ID of the token to delete
|
|
474
608
|
* @param adminId - ID of the admin making the deletion
|
|
609
|
+
* @param app - Firebase app instance
|
|
475
610
|
*/
|
|
476
611
|
export async function deleteAdminToken(
|
|
477
612
|
db: Firestore,
|
|
478
613
|
groupId: string,
|
|
479
614
|
tokenId: string,
|
|
480
|
-
adminId: string
|
|
615
|
+
adminId: string,
|
|
616
|
+
app: FirebaseApp
|
|
481
617
|
): Promise<void> {
|
|
482
618
|
const group = await getClinicGroup(db, groupId);
|
|
483
619
|
if (!group) {
|
|
@@ -492,9 +628,14 @@ export async function deleteAdminToken(
|
|
|
492
628
|
// Uklanjamo token
|
|
493
629
|
const updatedTokens = group.adminTokens.filter((t) => t.id !== tokenId);
|
|
494
630
|
|
|
495
|
-
await updateClinicGroup(
|
|
496
|
-
|
|
497
|
-
|
|
631
|
+
await updateClinicGroup(
|
|
632
|
+
db,
|
|
633
|
+
groupId,
|
|
634
|
+
{
|
|
635
|
+
adminTokens: updatedTokens,
|
|
636
|
+
},
|
|
637
|
+
app
|
|
638
|
+
);
|
|
498
639
|
}
|
|
499
640
|
|
|
500
641
|
/**
|
|
@@ -502,12 +643,14 @@ export async function deleteAdminToken(
|
|
|
502
643
|
* @param db - Firestore database instance
|
|
503
644
|
* @param groupId - ID of the clinic group
|
|
504
645
|
* @param adminId - ID of the admin requesting the tokens
|
|
646
|
+
* @param app - Firebase app instance (not used but included for consistency)
|
|
505
647
|
* @returns Array of active admin tokens
|
|
506
648
|
*/
|
|
507
649
|
export async function getActiveAdminTokens(
|
|
508
650
|
db: Firestore,
|
|
509
651
|
groupId: string,
|
|
510
|
-
adminId: string
|
|
652
|
+
adminId: string,
|
|
653
|
+
app: FirebaseApp
|
|
511
654
|
): Promise<AdminToken[]> {
|
|
512
655
|
const group = await getClinicGroup(db, groupId);
|
|
513
656
|
if (!group) {
|