@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.
@@ -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
- const owner = await clinicAdminService.getClinicAdmin(ownerId);
83
- if (!owner) {
84
- console.error("[CLINIC_GROUP] Owner not found or is not a clinic admin", {
85
- ownerId,
86
- });
87
- throw new Error("Owner not found or is not a clinic admin");
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: doc(collection(db, CLINIC_GROUPS_COLLECTION)).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
- ...validatedData.hqLocation,
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
- ...validatedData.contactInfo,
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
- // Ažuriramo podatke
272
- const updatedData = {
273
- ...data,
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
- // Vraćamo ažurirane podatke
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
- const group = await getClinicGroup(db, groupId);
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
- if (group.admins.includes(adminId)) {
305
- return; // Admin is already in the group
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
- await updateClinicGroup(db, groupId, {
309
- admins: [...group.admins, adminId],
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(db, groupId, {
338
- admins: group.admins.filter((id) => id !== adminId),
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 updateDoc(doc(db, CLINIC_GROUPS_COLLECTION, groupId), {
357
- isActive: false,
358
- updatedAt: Timestamp.now(),
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(db, groupId, {
403
- adminTokens: [...group.adminTokens, token],
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(db, groupId, {
445
- adminTokens: updatedTokens,
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(db, groupId, {
463
- adminTokens: updatedTokens,
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(db, groupId, {
496
- adminTokens: updatedTokens,
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) {