@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.mjs CHANGED
@@ -2375,7 +2375,7 @@ var clinicGroupSchema = z9.object({
2375
2375
  admins: z9.array(z9.string()),
2376
2376
  adminsInfo: z9.array(adminInfoSchema),
2377
2377
  adminTokens: z9.array(adminTokenSchema),
2378
- ownerId: z9.string(),
2378
+ ownerId: z9.string().nullable(),
2379
2379
  createdAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
2380
2380
  // Timestamp
2381
2381
  updatedAt: z9.instanceof(Date).or(z9.instanceof(Timestamp5)),
@@ -2451,7 +2451,7 @@ var createClinicGroupSchema = z9.object({
2451
2451
  hqLocation: clinicLocationSchema,
2452
2452
  contactInfo: clinicContactInfoSchema,
2453
2453
  contactPerson: contactPersonSchema,
2454
- ownerId: z9.string(),
2454
+ ownerId: z9.string().nullable(),
2455
2455
  isActive: z9.boolean(),
2456
2456
  logo: z9.string().optional().nullable(),
2457
2457
  practiceType: z9.nativeEnum(PracticeType).optional(),
@@ -2485,7 +2485,7 @@ var createClinicSchema = z9.object({
2485
2485
  });
2486
2486
  var createDefaultClinicGroupSchema = z9.object({
2487
2487
  name: z9.string(),
2488
- ownerId: z9.string(),
2488
+ ownerId: z9.string().nullable(),
2489
2489
  contactPerson: contactPersonSchema,
2490
2490
  contactInfo: clinicContactInfoSchema,
2491
2491
  hqLocation: clinicLocationSchema,
@@ -2583,45 +2583,7 @@ async function createClinicAdmin(db, data, clinicGroupService) {
2583
2583
  hasGroupId: !!clinicGroupId
2584
2584
  });
2585
2585
  if (validatedData.isGroupOwner && !clinicGroupId) {
2586
- console.log("[CLINIC_ADMIN] Creating default group for owner");
2587
- const defaultGroup = {
2588
- name: `${validatedData.contactInfo.firstName}'s Group`,
2589
- ownerId: validatedData.userRef,
2590
- contactPerson: validatedData.contactInfo,
2591
- contactInfo: {
2592
- email: validatedData.contactInfo.email,
2593
- phoneNumber: validatedData.contactInfo.phoneNumber || ""
2594
- },
2595
- hqLocation: {
2596
- address: "",
2597
- city: "",
2598
- country: "",
2599
- postalCode: "",
2600
- latitude: 0,
2601
- longitude: 0
2602
- },
2603
- isActive: true
2604
- };
2605
- console.log("[CLINIC_ADMIN] Default group data prepared", {
2606
- groupName: defaultGroup.name
2607
- });
2608
- try {
2609
- const clinicGroup = await clinicGroupService.createClinicGroup(
2610
- defaultGroup,
2611
- validatedData.userRef,
2612
- true
2613
- );
2614
- clinicGroupId = clinicGroup.id;
2615
- console.log("[CLINIC_ADMIN] Default group created successfully", {
2616
- groupId: clinicGroupId
2617
- });
2618
- } catch (groupCreationError) {
2619
- console.error(
2620
- "[CLINIC_ADMIN] Error creating default group:",
2621
- groupCreationError
2622
- );
2623
- throw groupCreationError;
2624
- }
2586
+ console.log("[CLINIC_ADMIN] Owner will be assigned to group later");
2625
2587
  } else if (!validatedData.isGroupOwner && !clinicGroupId) {
2626
2588
  console.error("[CLINIC_ADMIN] Missing clinic group ID for non-owner admin");
2627
2589
  throw new Error("Clinic group ID is required for non-owner admins");
@@ -2651,16 +2613,12 @@ async function createClinicAdmin(db, data, clinicGroupService) {
2651
2613
  }
2652
2614
  }
2653
2615
  console.log("[CLINIC_ADMIN] Preparing admin data object");
2654
- if (!clinicGroupId) {
2655
- console.error(
2656
- "[CLINIC_ADMIN] clinicGroupId is undefined, which should not happen at this point"
2657
- );
2658
- throw new Error("clinicGroupId is required but was undefined");
2659
- }
2660
2616
  const adminData = {
2661
- id: validatedData.userRef,
2617
+ id: doc4(collection2(db, CLINIC_ADMINS_COLLECTION)).id,
2618
+ // Generate a new ID for the admin document
2662
2619
  userRef: validatedData.userRef,
2663
- clinicGroupId,
2620
+ clinicGroupId: clinicGroupId || "",
2621
+ // Empty string for now if no group ID
2664
2622
  isGroupOwner: validatedData.isGroupOwner,
2665
2623
  clinicsManaged: [],
2666
2624
  // Uvek krećemo od prazne liste
@@ -3706,6 +3664,65 @@ import {
3706
3664
  } from "firebase/firestore";
3707
3665
  import { geohashForLocation as geohashForLocation2 } from "geofire-common";
3708
3666
  import { z as z13 } from "zod";
3667
+
3668
+ // src/services/clinic/utils/photos.utils.ts
3669
+ import {
3670
+ getStorage as getStorage2,
3671
+ ref as ref2,
3672
+ uploadBytes as uploadBytes2,
3673
+ getDownloadURL as getDownloadURL2,
3674
+ deleteObject as deleteObject2
3675
+ } from "firebase/storage";
3676
+ async function uploadPhoto(photo, entityType, entityId, photoType, app, fileName) {
3677
+ if (!photo || typeof photo !== "string" || !photo.startsWith("data:")) {
3678
+ return photo;
3679
+ }
3680
+ try {
3681
+ console.log(
3682
+ `[PHOTO_UTILS] Uploading ${photoType} for ${entityType}/${entityId}`
3683
+ );
3684
+ const storage = getStorage2(app);
3685
+ const storageFileName = fileName || `${photoType}-${Date.now()}`;
3686
+ const storageRef = ref2(
3687
+ storage,
3688
+ `${entityType}/${entityId}/${storageFileName}`
3689
+ );
3690
+ const base64Data = photo.split(",")[1];
3691
+ const contentType = photo.split(";")[0].split(":")[1];
3692
+ const byteCharacters = atob(base64Data);
3693
+ const byteArrays = [];
3694
+ for (let i = 0; i < byteCharacters.length; i++) {
3695
+ byteArrays.push(byteCharacters.charCodeAt(i));
3696
+ }
3697
+ const blob = new Blob([new Uint8Array(byteArrays)], { type: contentType });
3698
+ await uploadBytes2(storageRef, blob, { contentType });
3699
+ const downloadUrl = await getDownloadURL2(storageRef);
3700
+ console.log(`[PHOTO_UTILS] ${photoType} uploaded successfully`, {
3701
+ downloadUrl
3702
+ });
3703
+ return downloadUrl;
3704
+ } catch (error) {
3705
+ console.error(`[PHOTO_UTILS] Error uploading ${photoType}:`, error);
3706
+ return null;
3707
+ }
3708
+ }
3709
+ async function uploadMultiplePhotos(photos, entityType, entityId, photoType, app) {
3710
+ if (!photos || !Array.isArray(photos) || photos.length === 0) {
3711
+ return [];
3712
+ }
3713
+ const uploadPromises = photos.map(
3714
+ (photo, index) => uploadPhoto(photo, entityType, entityId, `${photoType}-${index}`, app)
3715
+ );
3716
+ try {
3717
+ const results = await Promise.all(uploadPromises);
3718
+ return results.filter((url) => url !== null);
3719
+ } catch (error) {
3720
+ console.error(`[PHOTO_UTILS] Error uploading multiple photos:`, error);
3721
+ return photos.filter((photo) => !photo.startsWith("data:"));
3722
+ }
3723
+ }
3724
+
3725
+ // src/services/clinic/utils/clinic-group.utils.ts
3709
3726
  function generateId() {
3710
3727
  const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3711
3728
  const timestamp = Date.now().toString(36);
@@ -3715,7 +3732,8 @@ function generateId() {
3715
3732
  ).join("");
3716
3733
  return `${randomPart}-${timestamp}`;
3717
3734
  }
3718
- async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService) {
3735
+ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService, app) {
3736
+ var _a, _b, _c, _d, _e;
3719
3737
  console.log("[CLINIC_GROUP] Starting clinic group creation", {
3720
3738
  ownerId,
3721
3739
  isDefault
@@ -3731,14 +3749,23 @@ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdm
3731
3749
  const validatedData = createClinicGroupSchema.parse(data);
3732
3750
  try {
3733
3751
  console.log("[CLINIC_GROUP] Checking if owner exists", { ownerId });
3734
- const owner = await clinicAdminService.getClinicAdmin(ownerId);
3735
- if (!owner) {
3736
- console.error("[CLINIC_GROUP] Owner not found or is not a clinic admin", {
3737
- ownerId
3738
- });
3739
- throw new Error("Owner not found or is not a clinic admin");
3752
+ if (isDefault) {
3753
+ console.log(
3754
+ "[CLINIC_GROUP] Skipping owner verification for default group creation"
3755
+ );
3756
+ } else {
3757
+ const owner = await clinicAdminService.getClinicAdmin(ownerId);
3758
+ if (!owner) {
3759
+ console.error(
3760
+ "[CLINIC_GROUP] Owner not found or is not a clinic admin",
3761
+ {
3762
+ ownerId
3763
+ }
3764
+ );
3765
+ throw new Error("Owner not found or is not a clinic admin");
3766
+ }
3767
+ console.log("[CLINIC_GROUP] Owner verified as clinic admin");
3740
3768
  }
3741
- console.log("[CLINIC_GROUP] Owner verified as clinic admin");
3742
3769
  } catch (ownerError) {
3743
3770
  console.error("[CLINIC_GROUP] Error verifying owner:", ownerError);
3744
3771
  throw ownerError;
@@ -3760,23 +3787,49 @@ async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdm
3760
3787
  }
3761
3788
  const now = Timestamp10.now();
3762
3789
  console.log("[CLINIC_GROUP] Preparing clinic group data object");
3790
+ const groupId = doc7(collection5(db, CLINIC_GROUPS_COLLECTION)).id;
3763
3791
  console.log("[CLINIC_GROUP] Logo value:", {
3764
3792
  logoValue: validatedData.logo,
3765
3793
  logoType: validatedData.logo === null ? "null" : typeof validatedData.logo
3766
3794
  });
3795
+ let logoUrl = await uploadPhoto(
3796
+ validatedData.logo || null,
3797
+ "clinic-groups",
3798
+ groupId,
3799
+ "logo",
3800
+ app
3801
+ );
3802
+ console.log("[CLINIC_GROUP] Logo processed", { logoUrl });
3767
3803
  const groupData = {
3768
3804
  ...validatedData,
3769
- id: doc7(collection5(db, CLINIC_GROUPS_COLLECTION)).id,
3805
+ id: groupId,
3806
+ name: validatedData.name,
3807
+ logo: logoUrl,
3808
+ // Use the uploaded logo URL or the original value
3770
3809
  description: isDefault ? void 0 : validatedData.description || void 0,
3771
3810
  hqLocation: {
3772
- ...validatedData.hqLocation,
3811
+ address: validatedData.hqLocation.address || "",
3812
+ city: validatedData.hqLocation.city || "",
3813
+ country: validatedData.hqLocation.country || "",
3814
+ postalCode: validatedData.hqLocation.postalCode || "",
3815
+ latitude: validatedData.hqLocation.latitude || 0,
3816
+ longitude: validatedData.hqLocation.longitude || 0,
3773
3817
  geohash: validatedData.hqLocation.geohash || void 0
3774
3818
  },
3775
3819
  contactInfo: {
3776
- ...validatedData.contactInfo,
3820
+ email: validatedData.contactInfo.email || "",
3821
+ phoneNumber: validatedData.contactInfo.phoneNumber || "",
3777
3822
  alternativePhoneNumber: isDefault ? void 0 : validatedData.contactInfo.alternativePhoneNumber || void 0,
3778
3823
  website: isDefault ? void 0 : validatedData.contactInfo.website || void 0
3779
3824
  },
3825
+ contactPerson: {
3826
+ firstName: ((_a = validatedData.contactPerson) == null ? void 0 : _a.firstName) || "",
3827
+ lastName: ((_b = validatedData.contactPerson) == null ? void 0 : _b.lastName) || "",
3828
+ email: ((_c = validatedData.contactPerson) == null ? void 0 : _c.email) || "",
3829
+ title: ((_d = validatedData.contactPerson) == null ? void 0 : _d.title) || null,
3830
+ phoneNumber: ((_e = validatedData.contactPerson) == null ? void 0 : _e.phoneNumber) || null
3831
+ },
3832
+ subscriptionModel: validatedData.subscriptionModel || "no_subscription" /* NO_SUBSCRIPTION */,
3780
3833
  clinics: [],
3781
3834
  clinicsInfo: [],
3782
3835
  admins: [ownerId],
@@ -3864,35 +3917,75 @@ async function getAllActiveGroups(db) {
3864
3917
  const querySnapshot = await getDocs5(q);
3865
3918
  return querySnapshot.docs.map((doc14) => doc14.data());
3866
3919
  }
3867
- async function updateClinicGroup(db, groupId, data) {
3920
+ async function updateClinicGroup(db, groupId, data, app) {
3921
+ console.log("[CLINIC_GROUP] Updating clinic group", { groupId });
3868
3922
  const group = await getClinicGroup(db, groupId);
3869
3923
  if (!group) {
3924
+ console.error("[CLINIC_GROUP] Clinic group not found", { groupId });
3870
3925
  throw new Error("Clinic group not found");
3871
3926
  }
3872
- const updatedData = {
3873
- ...data,
3927
+ let updatedData = { ...data };
3928
+ if (data.logo && typeof data.logo === "string" && data.logo.startsWith("data:")) {
3929
+ console.log("[CLINIC_GROUP] Processing logo for update");
3930
+ try {
3931
+ const logoUrl = await uploadPhoto(
3932
+ data.logo,
3933
+ "clinic-groups",
3934
+ groupId,
3935
+ "logo",
3936
+ app
3937
+ );
3938
+ console.log("[CLINIC_GROUP] Logo processed for update", { logoUrl });
3939
+ updatedData.logo = logoUrl;
3940
+ } catch (error) {
3941
+ console.error("[CLINIC_GROUP] Error processing logo for update:", error);
3942
+ }
3943
+ }
3944
+ updatedData = {
3945
+ ...updatedData,
3874
3946
  updatedAt: Timestamp10.now()
3875
3947
  };
3948
+ console.log("[CLINIC_GROUP] Updating clinic group in Firestore");
3876
3949
  await updateDoc10(doc7(db, CLINIC_GROUPS_COLLECTION, groupId), updatedData);
3950
+ console.log("[CLINIC_GROUP] Clinic group updated successfully");
3877
3951
  const updatedGroup = await getClinicGroup(db, groupId);
3878
3952
  if (!updatedGroup) {
3953
+ console.error("[CLINIC_GROUP] Failed to retrieve updated clinic group");
3879
3954
  throw new Error("Failed to retrieve updated clinic group");
3880
3955
  }
3881
3956
  return updatedGroup;
3882
3957
  }
3883
- async function addAdminToGroup(db, groupId, adminId) {
3884
- const group = await getClinicGroup(db, groupId);
3885
- if (!group) {
3886
- throw new Error("Clinic group not found");
3887
- }
3888
- if (group.admins.includes(adminId)) {
3889
- return;
3958
+ async function addAdminToGroup(db, groupId, adminId, app) {
3959
+ console.log("[CLINIC_GROUP] Adding admin to group", { groupId, adminId });
3960
+ try {
3961
+ const group = await getClinicGroup(db, groupId);
3962
+ if (!group) {
3963
+ console.error("[CLINIC_GROUP] Clinic group not found", { groupId });
3964
+ throw new Error("Clinic group not found");
3965
+ }
3966
+ if (group.admins.includes(adminId)) {
3967
+ console.log("[CLINIC_GROUP] Admin is already in the group", {
3968
+ adminId,
3969
+ groupId
3970
+ });
3971
+ return;
3972
+ }
3973
+ console.log("[CLINIC_GROUP] Updating group with new admin");
3974
+ await updateClinicGroup(
3975
+ db,
3976
+ groupId,
3977
+ {
3978
+ admins: [...group.admins, adminId]
3979
+ },
3980
+ app
3981
+ );
3982
+ console.log("[CLINIC_GROUP] Admin added to group successfully");
3983
+ } catch (error) {
3984
+ console.error("[CLINIC_GROUP] Error adding admin to group:", error);
3985
+ throw error;
3890
3986
  }
3891
- await updateClinicGroup(db, groupId, {
3892
- admins: [...group.admins, adminId]
3893
- });
3894
3987
  }
3895
- async function removeAdminFromGroup(db, groupId, adminId) {
3988
+ async function removeAdminFromGroup(db, groupId, adminId, app) {
3896
3989
  const group = await getClinicGroup(db, groupId);
3897
3990
  if (!group) {
3898
3991
  throw new Error("Clinic group not found");
@@ -3903,21 +3996,30 @@ async function removeAdminFromGroup(db, groupId, adminId) {
3903
3996
  if (!group.admins.includes(adminId)) {
3904
3997
  return;
3905
3998
  }
3906
- await updateClinicGroup(db, groupId, {
3907
- admins: group.admins.filter((id) => id !== adminId)
3908
- });
3999
+ await updateClinicGroup(
4000
+ db,
4001
+ groupId,
4002
+ {
4003
+ admins: group.admins.filter((id) => id !== adminId)
4004
+ },
4005
+ app
4006
+ );
3909
4007
  }
3910
- async function deactivateClinicGroup(db, groupId) {
4008
+ async function deactivateClinicGroup(db, groupId, app) {
3911
4009
  const group = await getClinicGroup(db, groupId);
3912
4010
  if (!group) {
3913
4011
  throw new Error("Clinic group not found");
3914
4012
  }
3915
- await updateDoc10(doc7(db, CLINIC_GROUPS_COLLECTION, groupId), {
3916
- isActive: false,
3917
- updatedAt: Timestamp10.now()
3918
- });
4013
+ await updateClinicGroup(
4014
+ db,
4015
+ groupId,
4016
+ {
4017
+ isActive: false
4018
+ },
4019
+ app
4020
+ );
3919
4021
  }
3920
- async function createAdminToken(db, groupId, creatorAdminId, data) {
4022
+ async function createAdminToken(db, groupId, creatorAdminId, app, data) {
3921
4023
  const group = await getClinicGroup(db, groupId);
3922
4024
  if (!group) {
3923
4025
  throw new Error("Clinic group not found");
@@ -3938,12 +4040,17 @@ async function createAdminToken(db, groupId, creatorAdminId, data) {
3938
4040
  createdAt: now,
3939
4041
  expiresAt
3940
4042
  };
3941
- await updateClinicGroup(db, groupId, {
3942
- adminTokens: [...group.adminTokens, token]
3943
- });
4043
+ await updateClinicGroup(
4044
+ db,
4045
+ groupId,
4046
+ {
4047
+ adminTokens: [...group.adminTokens, token]
4048
+ },
4049
+ app
4050
+ );
3944
4051
  return token;
3945
4052
  }
3946
- async function verifyAndUseAdminToken(db, groupId, token, userRef) {
4053
+ async function verifyAndUseAdminToken(db, groupId, token, userRef, app) {
3947
4054
  const group = await getClinicGroup(db, groupId);
3948
4055
  if (!group) {
3949
4056
  throw new Error("Clinic group not found");
@@ -3960,9 +4067,14 @@ async function verifyAndUseAdminToken(db, groupId, token, userRef) {
3960
4067
  const updatedTokens2 = group.adminTokens.map(
3961
4068
  (t) => t.id === adminToken.id ? { ...t, status: "expired" /* EXPIRED */ } : t
3962
4069
  );
3963
- await updateClinicGroup(db, groupId, {
3964
- adminTokens: updatedTokens2
3965
- });
4070
+ await updateClinicGroup(
4071
+ db,
4072
+ groupId,
4073
+ {
4074
+ adminTokens: updatedTokens2
4075
+ },
4076
+ app
4077
+ );
3966
4078
  throw new Error("Admin token has expired");
3967
4079
  }
3968
4080
  const updatedTokens = group.adminTokens.map(
@@ -3972,12 +4084,17 @@ async function verifyAndUseAdminToken(db, groupId, token, userRef) {
3972
4084
  usedByUserRef: userRef
3973
4085
  } : t
3974
4086
  );
3975
- await updateClinicGroup(db, groupId, {
3976
- adminTokens: updatedTokens
3977
- });
4087
+ await updateClinicGroup(
4088
+ db,
4089
+ groupId,
4090
+ {
4091
+ adminTokens: updatedTokens
4092
+ },
4093
+ app
4094
+ );
3978
4095
  return true;
3979
4096
  }
3980
- async function deleteAdminToken(db, groupId, tokenId, adminId) {
4097
+ async function deleteAdminToken(db, groupId, tokenId, adminId, app) {
3981
4098
  const group = await getClinicGroup(db, groupId);
3982
4099
  if (!group) {
3983
4100
  throw new Error("Clinic group not found");
@@ -3986,11 +4103,16 @@ async function deleteAdminToken(db, groupId, tokenId, adminId) {
3986
4103
  throw new Error("Admin does not belong to this clinic group");
3987
4104
  }
3988
4105
  const updatedTokens = group.adminTokens.filter((t) => t.id !== tokenId);
3989
- await updateClinicGroup(db, groupId, {
3990
- adminTokens: updatedTokens
3991
- });
4106
+ await updateClinicGroup(
4107
+ db,
4108
+ groupId,
4109
+ {
4110
+ adminTokens: updatedTokens
4111
+ },
4112
+ app
4113
+ );
3992
4114
  }
3993
- async function getActiveAdminTokens(db, groupId, adminId) {
4115
+ async function getActiveAdminTokens(db, groupId, adminId, app) {
3994
4116
  const group = await getClinicGroup(db, groupId);
3995
4117
  if (!group) {
3996
4118
  throw new Error("Clinic group not found");
@@ -4016,7 +4138,8 @@ var ClinicGroupService = class extends BaseService {
4016
4138
  data,
4017
4139
  ownerId,
4018
4140
  isDefault,
4019
- this.clinicAdminService
4141
+ this.clinicAdminService,
4142
+ this.app
4020
4143
  );
4021
4144
  }
4022
4145
  /**
@@ -4035,25 +4158,35 @@ var ClinicGroupService = class extends BaseService {
4035
4158
  * Ažurira grupaciju klinika
4036
4159
  */
4037
4160
  async updateClinicGroup(groupId, data) {
4038
- return updateClinicGroup(this.db, groupId, data);
4161
+ return updateClinicGroup(this.db, groupId, data, this.app);
4039
4162
  }
4040
4163
  /**
4041
4164
  * Dodaje admina u grupaciju
4042
4165
  */
4043
4166
  async addAdminToGroup(groupId, adminId) {
4044
- return addAdminToGroup(this.db, groupId, adminId);
4167
+ return addAdminToGroup(
4168
+ this.db,
4169
+ groupId,
4170
+ adminId,
4171
+ this.app
4172
+ );
4045
4173
  }
4046
4174
  /**
4047
4175
  * Uklanja admina iz grupacije
4048
4176
  */
4049
4177
  async removeAdminFromGroup(groupId, adminId) {
4050
- return removeAdminFromGroup(this.db, groupId, adminId);
4178
+ return removeAdminFromGroup(
4179
+ this.db,
4180
+ groupId,
4181
+ adminId,
4182
+ this.app
4183
+ );
4051
4184
  }
4052
4185
  /**
4053
4186
  * Deaktivira grupaciju klinika
4054
4187
  */
4055
4188
  async deactivateClinicGroup(groupId) {
4056
- return deactivateClinicGroup(this.db, groupId);
4189
+ return deactivateClinicGroup(this.db, groupId, this.app);
4057
4190
  }
4058
4191
  /**
4059
4192
  * Sets up additional clinic group information after initial creation
@@ -4063,18 +4196,45 @@ var ClinicGroupService = class extends BaseService {
4063
4196
  * @returns The updated clinic group
4064
4197
  */
4065
4198
  async setupClinicGroup(groupId, setupData) {
4199
+ console.log("[CLINIC_GROUP] Setting up clinic group", { groupId });
4066
4200
  const clinicGroup = await this.getClinicGroup(groupId);
4067
4201
  if (!clinicGroup) {
4202
+ console.error("[CLINIC_GROUP] Clinic group not found", { groupId });
4068
4203
  throw new Error(`Clinic group with ID ${groupId} not found`);
4069
4204
  }
4205
+ let logoUrl = setupData.logo;
4206
+ if (logoUrl && typeof logoUrl === "string" && logoUrl.startsWith("data:")) {
4207
+ console.log("[CLINIC_GROUP] Processing logo in setupClinicGroup");
4208
+ try {
4209
+ const uploadedLogoUrl = await uploadPhoto(
4210
+ logoUrl,
4211
+ "clinic-groups",
4212
+ groupId,
4213
+ "logo",
4214
+ this.app
4215
+ );
4216
+ console.log("[CLINIC_GROUP] Logo processed in setupClinicGroup", {
4217
+ uploadedLogoUrl
4218
+ });
4219
+ if (uploadedLogoUrl !== null) {
4220
+ logoUrl = uploadedLogoUrl;
4221
+ }
4222
+ } catch (error) {
4223
+ console.error(
4224
+ "[CLINIC_GROUP] Error processing logo in setupClinicGroup:",
4225
+ error
4226
+ );
4227
+ }
4228
+ }
4070
4229
  const updateData = {
4071
4230
  languages: setupData.languages,
4072
4231
  practiceType: setupData.practiceType,
4073
4232
  description: setupData.description,
4074
- logo: setupData.logo,
4233
+ logo: logoUrl,
4075
4234
  calendarSyncEnabled: setupData.calendarSyncEnabled,
4076
4235
  autoConfirmAppointments: setupData.autoConfirmAppointments
4077
4236
  };
4237
+ console.log("[CLINIC_GROUP] Updating clinic group with setup data");
4078
4238
  return this.updateClinicGroup(groupId, updateData);
4079
4239
  }
4080
4240
  /**
@@ -4085,6 +4245,7 @@ var ClinicGroupService = class extends BaseService {
4085
4245
  this.db,
4086
4246
  groupId,
4087
4247
  creatorAdminId,
4248
+ this.app,
4088
4249
  data
4089
4250
  );
4090
4251
  }
@@ -4096,7 +4257,8 @@ var ClinicGroupService = class extends BaseService {
4096
4257
  this.db,
4097
4258
  groupId,
4098
4259
  token,
4099
- userRef
4260
+ userRef,
4261
+ this.app
4100
4262
  );
4101
4263
  }
4102
4264
  /**
@@ -4107,14 +4269,20 @@ var ClinicGroupService = class extends BaseService {
4107
4269
  this.db,
4108
4270
  groupId,
4109
4271
  tokenId,
4110
- adminId
4272
+ adminId,
4273
+ this.app
4111
4274
  );
4112
4275
  }
4113
4276
  /**
4114
4277
  * Dohvata aktivne admin tokene
4115
4278
  */
4116
4279
  async getActiveAdminTokens(groupId, adminId) {
4117
- return getActiveAdminTokens(this.db, groupId, adminId);
4280
+ return getActiveAdminTokens(
4281
+ this.db,
4282
+ groupId,
4283
+ adminId,
4284
+ this.app
4285
+ );
4118
4286
  }
4119
4287
  };
4120
4288
 
@@ -4132,44 +4300,193 @@ import {
4132
4300
  } from "firebase/firestore";
4133
4301
  import { geohashForLocation as geohashForLocation3 } from "geofire-common";
4134
4302
  import { z as z14 } from "zod";
4135
- async function createClinic(db, data, creatorAdminId, clinicGroupService, clinicAdminService) {
4136
- const validatedData = createClinicSchema.parse(data);
4137
- const admin = await clinicAdminService.getClinicAdmin(creatorAdminId);
4138
- if (!admin) {
4139
- throw new Error("Admin not found");
4303
+ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinicAdminService, app) {
4304
+ var _a;
4305
+ console.log("[CLINIC] Starting clinic creation", { creatorAdminId });
4306
+ console.log("[CLINIC] Input data:", JSON.stringify(data, null, 2));
4307
+ try {
4308
+ const validatedData2 = createClinicSchema.parse(data);
4309
+ console.log("[CLINIC] Data validation passed");
4310
+ } catch (validationError) {
4311
+ console.error("[CLINIC] Data validation failed:", validationError);
4312
+ throw validationError;
4140
4313
  }
4141
- if (admin.clinicGroupId !== validatedData.clinicGroupId) {
4142
- throw new Error("Admin does not belong to this clinic group");
4314
+ const validatedData = createClinicSchema.parse(data);
4315
+ try {
4316
+ console.log("[CLINIC] Checking if admin exists and belongs to group");
4317
+ const admin = await clinicAdminService.getClinicAdmin(creatorAdminId);
4318
+ if (!admin) {
4319
+ console.error("[CLINIC] Admin not found", { creatorAdminId });
4320
+ throw new Error("Admin not found");
4321
+ }
4322
+ if (admin.clinicGroupId !== validatedData.clinicGroupId) {
4323
+ console.error("[CLINIC] Admin does not belong to this clinic group", {
4324
+ adminGroupId: admin.clinicGroupId,
4325
+ requestedGroupId: validatedData.clinicGroupId
4326
+ });
4327
+ throw new Error("Admin does not belong to this clinic group");
4328
+ }
4329
+ console.log("[CLINIC] Admin verified");
4330
+ } catch (adminError) {
4331
+ console.error("[CLINIC] Error verifying admin:", adminError);
4332
+ throw adminError;
4143
4333
  }
4144
- const group = await clinicGroupService.getClinicGroup(
4145
- validatedData.clinicGroupId
4146
- );
4147
- if (!group) {
4148
- throw new Error("Clinic group not found");
4334
+ try {
4335
+ console.log("[CLINIC] Checking if clinic group exists");
4336
+ const group = await clinicGroupService.getClinicGroup(
4337
+ validatedData.clinicGroupId
4338
+ );
4339
+ if (!group) {
4340
+ console.error("[CLINIC] Clinic group not found", {
4341
+ groupId: validatedData.clinicGroupId
4342
+ });
4343
+ throw new Error("Clinic group not found");
4344
+ }
4345
+ console.log("[CLINIC] Clinic group verified");
4346
+ } catch (groupError) {
4347
+ console.error("[CLINIC] Error verifying clinic group:", groupError);
4348
+ throw groupError;
4149
4349
  }
4350
+ console.log("[CLINIC] Generating geohash for location");
4150
4351
  if (validatedData.location) {
4151
- validatedData.location.geohash = geohashForLocation3([
4152
- validatedData.location.latitude,
4153
- validatedData.location.longitude
4154
- ]);
4352
+ try {
4353
+ validatedData.location.geohash = geohashForLocation3([
4354
+ validatedData.location.latitude,
4355
+ validatedData.location.longitude
4356
+ ]);
4357
+ console.log("[CLINIC] Geohash generated successfully", {
4358
+ geohash: validatedData.location.geohash
4359
+ });
4360
+ } catch (geohashError) {
4361
+ console.error("[CLINIC] Error generating geohash:", geohashError);
4362
+ throw geohashError;
4363
+ }
4364
+ }
4365
+ const clinicId = doc8(collection6(db, CLINICS_COLLECTION)).id;
4366
+ console.log("[CLINIC] Generated clinic ID:", clinicId);
4367
+ console.log("[CLINIC] Processing photos");
4368
+ let logoUrl = null;
4369
+ if (validatedData.logo) {
4370
+ console.log("[CLINIC] Processing logo");
4371
+ try {
4372
+ logoUrl = await uploadPhoto(
4373
+ validatedData.logo,
4374
+ "clinics",
4375
+ clinicId,
4376
+ "logo",
4377
+ app
4378
+ );
4379
+ console.log("[CLINIC] Logo processed", { logoUrl });
4380
+ } catch (logoError) {
4381
+ console.error("[CLINIC] Error processing logo:", logoError);
4382
+ }
4383
+ }
4384
+ let processedPhotos = [];
4385
+ if (validatedData.photos && validatedData.photos.length > 0) {
4386
+ console.log("[CLINIC] Processing regular photos");
4387
+ try {
4388
+ processedPhotos = await uploadMultiplePhotos(
4389
+ validatedData.photos,
4390
+ "clinics",
4391
+ clinicId,
4392
+ "photo",
4393
+ app
4394
+ );
4395
+ console.log("[CLINIC] Regular photos processed", {
4396
+ count: processedPhotos.length
4397
+ });
4398
+ } catch (photosError) {
4399
+ console.error("[CLINIC] Error processing regular photos:", photosError);
4400
+ processedPhotos = validatedData.photos.filter(
4401
+ (photo) => !photo.startsWith("data:")
4402
+ );
4403
+ }
4404
+ }
4405
+ let processedFeaturedPhotos = [];
4406
+ if (validatedData.featuredPhotos && validatedData.featuredPhotos.length > 0) {
4407
+ console.log("[CLINIC] Processing featured photos");
4408
+ try {
4409
+ processedFeaturedPhotos = await uploadMultiplePhotos(
4410
+ validatedData.featuredPhotos,
4411
+ "clinics",
4412
+ clinicId,
4413
+ "featured",
4414
+ app
4415
+ );
4416
+ console.log("[CLINIC] Featured photos processed", {
4417
+ count: processedFeaturedPhotos.length
4418
+ });
4419
+ } catch (featuredError) {
4420
+ console.error(
4421
+ "[CLINIC] Error processing featured photos:",
4422
+ featuredError
4423
+ );
4424
+ processedFeaturedPhotos = validatedData.featuredPhotos.filter(
4425
+ (photo) => !photo.startsWith("data:")
4426
+ );
4427
+ }
4428
+ }
4429
+ let processedPhotosWithTags = validatedData.photosWithTags || [];
4430
+ if (processedPhotosWithTags.length > 0) {
4431
+ console.log("[CLINIC] Processing photos with tags");
4432
+ try {
4433
+ const updatedPhotosWithTags = [];
4434
+ for (const photoWithTag of processedPhotosWithTags) {
4435
+ if (photoWithTag.url && photoWithTag.url.startsWith("data:")) {
4436
+ const uploadedUrl = await uploadPhoto(
4437
+ photoWithTag.url,
4438
+ "clinics",
4439
+ clinicId,
4440
+ `tagged-${photoWithTag.tag}`,
4441
+ app
4442
+ );
4443
+ if (uploadedUrl) {
4444
+ updatedPhotosWithTags.push({
4445
+ url: uploadedUrl,
4446
+ tag: photoWithTag.tag
4447
+ });
4448
+ }
4449
+ } else {
4450
+ updatedPhotosWithTags.push(photoWithTag);
4451
+ }
4452
+ }
4453
+ processedPhotosWithTags = updatedPhotosWithTags;
4454
+ console.log("[CLINIC] Photos with tags processed", {
4455
+ count: processedPhotosWithTags.length
4456
+ });
4457
+ } catch (tagsError) {
4458
+ console.error("[CLINIC] Error processing photos with tags:", tagsError);
4459
+ processedPhotosWithTags = ((_a = validatedData.photosWithTags) == null ? void 0 : _a.filter(
4460
+ (photo) => !photo.url.startsWith("data:")
4461
+ )) || [];
4462
+ }
4155
4463
  }
4156
4464
  const now = Timestamp11.now();
4465
+ console.log("[CLINIC] Preparing clinic data object");
4157
4466
  const clinicData = {
4158
4467
  ...validatedData,
4159
- id: doc8(collection6(db, CLINICS_COLLECTION)).id,
4468
+ id: clinicId,
4160
4469
  description: validatedData.description || void 0,
4161
4470
  location: {
4162
- ...validatedData.location,
4471
+ address: validatedData.location.address || "",
4472
+ city: validatedData.location.city || "",
4473
+ country: validatedData.location.country || "",
4474
+ postalCode: validatedData.location.postalCode || "",
4475
+ latitude: validatedData.location.latitude || 0,
4476
+ longitude: validatedData.location.longitude || 0,
4163
4477
  geohash: validatedData.location.geohash || void 0
4164
4478
  },
4165
4479
  contactInfo: {
4166
- ...validatedData.contactInfo,
4480
+ email: validatedData.contactInfo.email || "",
4481
+ phoneNumber: validatedData.contactInfo.phoneNumber || "",
4167
4482
  alternativePhoneNumber: validatedData.contactInfo.alternativePhoneNumber || void 0,
4168
4483
  website: validatedData.contactInfo.website || void 0
4169
4484
  },
4485
+ logo: logoUrl || void 0,
4170
4486
  tags: validatedData.tags || [],
4171
- featuredPhotos: [],
4172
- photos: validatedData.photos || [],
4487
+ featuredPhotos: processedFeaturedPhotos || [],
4488
+ photos: processedPhotos || [],
4489
+ photosWithTags: processedPhotosWithTags,
4173
4490
  doctors: [],
4174
4491
  doctorsInfo: [],
4175
4492
  services: [],
@@ -4184,17 +4501,73 @@ async function createClinic(db, data, creatorAdminId, clinicGroupService, clinic
4184
4501
  isVerified: false
4185
4502
  };
4186
4503
  try {
4187
- clinicSchema.parse(clinicData);
4188
- await setDoc10(doc8(db, CLINICS_COLLECTION, clinicData.id), clinicData);
4189
- await clinicGroupService.updateClinicGroup(validatedData.clinicGroupId, {
4190
- clinics: [...group.clinics, clinicData.id]
4504
+ console.log("[CLINIC] Validating complete clinic object");
4505
+ try {
4506
+ clinicSchema.parse(clinicData);
4507
+ console.log("[CLINIC] Clinic validation passed");
4508
+ } catch (schemaError) {
4509
+ console.error(
4510
+ "[CLINIC] Clinic validation failed:",
4511
+ JSON.stringify(schemaError, null, 2)
4512
+ );
4513
+ throw schemaError;
4514
+ }
4515
+ console.log("[CLINIC] Saving clinic to Firestore", {
4516
+ clinicId: clinicData.id
4517
+ });
4518
+ try {
4519
+ await setDoc10(doc8(db, CLINICS_COLLECTION, clinicData.id), clinicData);
4520
+ console.log("[CLINIC] Clinic saved successfully");
4521
+ } catch (firestoreError) {
4522
+ console.error("[CLINIC] Error saving to Firestore:", firestoreError);
4523
+ throw firestoreError;
4524
+ }
4525
+ console.log("[CLINIC] Adding clinic to clinic group");
4526
+ try {
4527
+ const group = await clinicGroupService.getClinicGroup(
4528
+ validatedData.clinicGroupId
4529
+ );
4530
+ if (group) {
4531
+ await clinicGroupService.updateClinicGroup(
4532
+ validatedData.clinicGroupId,
4533
+ {
4534
+ clinics: [...group.clinics, clinicData.id]
4535
+ }
4536
+ );
4537
+ console.log("[CLINIC] Clinic added to group successfully");
4538
+ }
4539
+ } catch (groupUpdateError) {
4540
+ console.error("[CLINIC] Error adding clinic to group:", groupUpdateError);
4541
+ }
4542
+ console.log("[CLINIC] Adding clinic to admin's managed clinics");
4543
+ try {
4544
+ await clinicAdminService.addClinicToManaged(
4545
+ creatorAdminId,
4546
+ clinicData.id
4547
+ );
4548
+ console.log(
4549
+ "[CLINIC] Clinic added to admin's managed clinics successfully"
4550
+ );
4551
+ } catch (adminUpdateError) {
4552
+ console.error(
4553
+ "[CLINIC] Error adding clinic to admin's managed clinics:",
4554
+ adminUpdateError
4555
+ );
4556
+ }
4557
+ console.log("[CLINIC] Clinic creation completed successfully", {
4558
+ clinicId: clinicData.id,
4559
+ clinicName: clinicData.name
4191
4560
  });
4192
- await clinicAdminService.addClinicToManaged(creatorAdminId, clinicData.id);
4193
4561
  return clinicData;
4194
4562
  } catch (error) {
4195
4563
  if (error instanceof z14.ZodError) {
4564
+ console.error(
4565
+ "[CLINIC] Zod validation error:",
4566
+ JSON.stringify(error.errors, null, 2)
4567
+ );
4196
4568
  throw new Error("Invalid clinic data: " + error.message);
4197
4569
  }
4570
+ console.error("[CLINIC] Unhandled error in createClinic:", error);
4198
4571
  throw error;
4199
4572
  }
4200
4573
  }
@@ -4215,27 +4588,177 @@ async function getClinicsByGroup(db, groupId) {
4215
4588
  const querySnapshot = await getDocs6(q);
4216
4589
  return querySnapshot.docs.map((doc14) => doc14.data());
4217
4590
  }
4218
- async function updateClinic(db, clinicId, data, adminId, clinicAdminService) {
4591
+ async function updateClinic(db, clinicId, data, adminId, clinicAdminService, app) {
4592
+ console.log("[CLINIC] Starting clinic update", { clinicId, adminId });
4219
4593
  const clinic = await getClinic(db, clinicId);
4220
4594
  if (!clinic) {
4595
+ console.error("[CLINIC] Clinic not found", { clinicId });
4221
4596
  throw new Error("Clinic not found");
4222
4597
  }
4223
- const admin = await clinicAdminService.getClinicAdmin(adminId);
4224
- if (!admin) {
4225
- throw new Error("Admin not found");
4598
+ try {
4599
+ console.log("[CLINIC] Checking admin permissions");
4600
+ const admin = await clinicAdminService.getClinicAdmin(adminId);
4601
+ if (!admin) {
4602
+ console.error("[CLINIC] Admin not found", { adminId });
4603
+ throw new Error("Admin not found");
4604
+ }
4605
+ const hasPermission = admin.isGroupOwner && admin.clinicGroupId === clinic.clinicGroupId || admin.clinicsManaged.includes(clinicId) && clinic.admins && clinic.admins.includes(adminId);
4606
+ if (!hasPermission) {
4607
+ console.error(
4608
+ "[CLINIC] Admin does not have permission to update this clinic",
4609
+ {
4610
+ adminId,
4611
+ clinicId,
4612
+ isGroupOwner: admin.isGroupOwner,
4613
+ clinicsManaged: admin.clinicsManaged,
4614
+ isClinicAdmin: clinic.admins && clinic.admins.includes(adminId)
4615
+ }
4616
+ );
4617
+ throw new Error("Admin does not have permission to update this clinic");
4618
+ }
4619
+ console.log("[CLINIC] Admin permissions verified");
4620
+ } catch (adminError) {
4621
+ console.error("[CLINIC] Error verifying admin permissions:", adminError);
4622
+ throw adminError;
4623
+ }
4624
+ let updatedData = { ...data };
4625
+ if (data.logo && typeof data.logo === "string" && data.logo.startsWith("data:")) {
4626
+ console.log("[CLINIC] Processing logo update");
4627
+ try {
4628
+ const logoUrl = await uploadPhoto(
4629
+ data.logo,
4630
+ "clinics",
4631
+ clinicId,
4632
+ "logo",
4633
+ app
4634
+ );
4635
+ console.log("[CLINIC] Logo update processed", { logoUrl });
4636
+ if (logoUrl !== null) {
4637
+ updatedData.logo = logoUrl;
4638
+ }
4639
+ } catch (logoError) {
4640
+ console.error("[CLINIC] Error processing logo update:", logoError);
4641
+ }
4226
4642
  }
4227
- if (!admin.isGroupOwner && !admin.clinicsManaged.includes(clinicId)) {
4228
- throw new Error("Admin does not have permission to update this clinic");
4643
+ if (data.photos && data.photos.length > 0) {
4644
+ console.log("[CLINIC] Processing regular photos update");
4645
+ try {
4646
+ const dataUrlPhotos = data.photos.filter(
4647
+ (photo) => typeof photo === "string" && photo.startsWith("data:")
4648
+ );
4649
+ const existingPhotos = data.photos.filter(
4650
+ (photo) => typeof photo === "string" && !photo.startsWith("data:")
4651
+ );
4652
+ if (dataUrlPhotos.length > 0) {
4653
+ const uploadedPhotos = await uploadMultiplePhotos(
4654
+ dataUrlPhotos,
4655
+ "clinics",
4656
+ clinicId,
4657
+ "photo",
4658
+ app
4659
+ );
4660
+ console.log("[CLINIC] Regular photos update processed", {
4661
+ count: uploadedPhotos.length
4662
+ });
4663
+ updatedData.photos = [...existingPhotos, ...uploadedPhotos];
4664
+ }
4665
+ } catch (photosError) {
4666
+ console.error(
4667
+ "[CLINIC] Error processing regular photos update:",
4668
+ photosError
4669
+ );
4670
+ updatedData.photos = data.photos.filter(
4671
+ (photo) => typeof photo === "string" && !photo.startsWith("data:")
4672
+ );
4673
+ }
4229
4674
  }
4230
- const updatedData = {
4231
- ...data,
4675
+ if (data.featuredPhotos && data.featuredPhotos.length > 0) {
4676
+ console.log("[CLINIC] Processing featured photos update");
4677
+ try {
4678
+ const dataUrlPhotos = data.featuredPhotos.filter(
4679
+ (photo) => typeof photo === "string" && photo.startsWith("data:")
4680
+ );
4681
+ const existingPhotos = data.featuredPhotos.filter(
4682
+ (photo) => typeof photo === "string" && !photo.startsWith("data:")
4683
+ );
4684
+ if (dataUrlPhotos.length > 0) {
4685
+ const uploadedPhotos = await uploadMultiplePhotos(
4686
+ dataUrlPhotos,
4687
+ "clinics",
4688
+ clinicId,
4689
+ "featured",
4690
+ app
4691
+ );
4692
+ console.log("[CLINIC] Featured photos update processed", {
4693
+ count: uploadedPhotos.length
4694
+ });
4695
+ updatedData.featuredPhotos = [...existingPhotos, ...uploadedPhotos];
4696
+ }
4697
+ } catch (featuredError) {
4698
+ console.error(
4699
+ "[CLINIC] Error processing featured photos update:",
4700
+ featuredError
4701
+ );
4702
+ updatedData.featuredPhotos = data.featuredPhotos.filter(
4703
+ (photo) => typeof photo === "string" && !photo.startsWith("data:")
4704
+ );
4705
+ }
4706
+ }
4707
+ if (data.photosWithTags && data.photosWithTags.length > 0) {
4708
+ console.log("[CLINIC] Processing photos with tags update");
4709
+ try {
4710
+ const updatedPhotosWithTags = [];
4711
+ for (const photoWithTag of data.photosWithTags) {
4712
+ if (photoWithTag.url && photoWithTag.url.startsWith("data:")) {
4713
+ const uploadedUrl = await uploadPhoto(
4714
+ photoWithTag.url,
4715
+ "clinics",
4716
+ clinicId,
4717
+ `tagged-${photoWithTag.tag}`,
4718
+ app
4719
+ );
4720
+ if (uploadedUrl) {
4721
+ updatedPhotosWithTags.push({
4722
+ url: uploadedUrl,
4723
+ tag: photoWithTag.tag
4724
+ });
4725
+ }
4726
+ } else {
4727
+ updatedPhotosWithTags.push(photoWithTag);
4728
+ }
4729
+ }
4730
+ updatedData.photosWithTags = updatedPhotosWithTags;
4731
+ console.log("[CLINIC] Photos with tags update processed", {
4732
+ count: updatedPhotosWithTags.length
4733
+ });
4734
+ } catch (tagsError) {
4735
+ console.error(
4736
+ "[CLINIC] Error processing photos with tags update:",
4737
+ tagsError
4738
+ );
4739
+ updatedData.photosWithTags = data.photosWithTags.filter(
4740
+ (photo) => !photo.url.startsWith("data:")
4741
+ );
4742
+ }
4743
+ }
4744
+ updatedData = {
4745
+ ...updatedData,
4232
4746
  updatedAt: Timestamp11.now()
4233
4747
  };
4234
- await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), updatedData);
4748
+ console.log("[CLINIC] Updating clinic in Firestore");
4749
+ try {
4750
+ await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), updatedData);
4751
+ console.log("[CLINIC] Clinic updated successfully");
4752
+ } catch (updateError) {
4753
+ console.error("[CLINIC] Error updating clinic in Firestore:", updateError);
4754
+ throw updateError;
4755
+ }
4235
4756
  const updatedClinic = await getClinic(db, clinicId);
4236
4757
  if (!updatedClinic) {
4758
+ console.error("[CLINIC] Failed to retrieve updated clinic");
4237
4759
  throw new Error("Failed to retrieve updated clinic");
4238
4760
  }
4761
+ console.log("[CLINIC] Clinic update completed successfully");
4239
4762
  return updatedClinic;
4240
4763
  }
4241
4764
  async function deactivateClinic(db, clinicId, adminId, clinicAdminService) {
@@ -4247,7 +4770,8 @@ async function deactivateClinic(db, clinicId, adminId, clinicAdminService) {
4247
4770
  if (!admin) {
4248
4771
  throw new Error("Admin not found");
4249
4772
  }
4250
- if (!admin.isGroupOwner && !admin.clinicsManaged.includes(clinicId)) {
4773
+ const hasPermission = admin.isGroupOwner && admin.clinicGroupId === clinic.clinicGroupId || admin.clinicsManaged.includes(clinicId) && clinic.admins && clinic.admins.includes(adminId);
4774
+ if (!hasPermission) {
4251
4775
  throw new Error("Admin does not have permission to deactivate this clinic");
4252
4776
  }
4253
4777
  await updateDoc11(doc8(db, CLINICS_COLLECTION, clinicId), {
@@ -4292,27 +4816,28 @@ async function getActiveClinicsByAdmin(db, adminId, clinicAdminService, clinicGr
4292
4816
  import {
4293
4817
  collection as collection7,
4294
4818
  doc as doc9,
4295
- setDoc as setDoc11,
4296
- Timestamp as Timestamp12
4819
+ Timestamp as Timestamp12,
4820
+ getDoc as getDoc13,
4821
+ addDoc as addDoc2
4297
4822
  } from "firebase/firestore";
4298
- async function addReview(db, clinicId, review) {
4299
- const clinic = await getClinic(db, clinicId);
4300
- if (!clinic) {
4823
+ async function addReview(db, clinicId, review, app) {
4824
+ const clinicRef = doc9(db, "clinics", clinicId);
4825
+ const clinicSnap = await getDoc13(clinicRef);
4826
+ if (!clinicSnap.exists()) {
4301
4827
  throw new Error("Clinic not found");
4302
4828
  }
4829
+ const clinic = clinicSnap.data();
4303
4830
  const now = Timestamp12.now();
4304
4831
  const reviewData = {
4832
+ ...review,
4305
4833
  id: doc9(collection7(db, "clinic_reviews")).id,
4306
4834
  clinicId,
4307
- patientId: review.patientId,
4308
- rating: review.rating,
4309
- comment: review.comment,
4310
4835
  createdAt: now,
4311
4836
  updatedAt: now,
4312
4837
  isVerified: false
4313
4838
  };
4314
4839
  clinicReviewSchema.parse(reviewData);
4315
- await setDoc11(doc9(db, "clinic_reviews", reviewData.id), reviewData);
4840
+ await addDoc2(collection7(db, "clinic_reviews"), reviewData);
4316
4841
  const newRating = clinic.rating ? {
4317
4842
  average: (clinic.rating.average * clinic.rating.count + review.rating) / (clinic.rating.count + 1),
4318
4843
  count: clinic.rating.count + 1
@@ -4328,28 +4853,24 @@ async function addReview(db, clinicId, review) {
4328
4853
  ...clinic.reviewsInfo,
4329
4854
  {
4330
4855
  id: reviewData.id,
4331
- rating: review.rating,
4332
- text: review.comment,
4333
- patientId: review.patientId,
4334
- patientName: "Patient",
4335
- // This should be fetched from patient service
4336
- patientPhoto: "",
4337
- // This should be fetched from patient service
4338
- createdAt: now,
4339
- updatedAt: now
4856
+ patientId: reviewData.patientId,
4857
+ rating: reviewData.rating,
4858
+ comment: reviewData.comment,
4859
+ createdAt: reviewData.createdAt
4340
4860
  }
4341
4861
  ]
4342
4862
  },
4343
- clinic.admins[0],
4344
- // Using the first admin for the update
4345
- { getClinicAdmin: async (id) => ({ isGroupOwner: true }) }
4346
- // Mock admin service
4863
+ "system",
4864
+ // System update, no admin ID needed
4865
+ null,
4866
+ // No clinic admin service needed for system updates
4867
+ app
4347
4868
  );
4348
4869
  return reviewData;
4349
4870
  }
4350
4871
 
4351
4872
  // src/services/clinic/utils/tag.utils.ts
4352
- async function addTags(db, clinicId, adminId, newTags, clinicAdminService) {
4873
+ async function addTags(db, clinicId, adminId, newTags, clinicAdminService, app) {
4353
4874
  const clinic = await getClinic(db, clinicId);
4354
4875
  if (!clinic) {
4355
4876
  throw new Error("Clinic not found");
@@ -4358,7 +4879,8 @@ async function addTags(db, clinicId, adminId, newTags, clinicAdminService) {
4358
4879
  if (!admin) {
4359
4880
  throw new Error("Admin not found");
4360
4881
  }
4361
- if (!admin.isGroupOwner && !admin.clinicsManaged.includes(clinicId)) {
4882
+ const hasPermission = admin.isGroupOwner && admin.clinicGroupId === clinic.clinicGroupId || admin.clinicsManaged.includes(clinicId) && clinic.admins && clinic.admins.includes(adminId);
4883
+ if (!hasPermission) {
4362
4884
  throw new Error("Admin does not have permission to update this clinic");
4363
4885
  }
4364
4886
  const updatedTags = [.../* @__PURE__ */ new Set([...clinic.tags, ...newTags.tags || []])];
@@ -4369,10 +4891,11 @@ async function addTags(db, clinicId, adminId, newTags, clinicAdminService) {
4369
4891
  tags: updatedTags
4370
4892
  },
4371
4893
  adminId,
4372
- clinicAdminService
4894
+ clinicAdminService,
4895
+ app
4373
4896
  );
4374
4897
  }
4375
- async function removeTags(db, clinicId, adminId, tagsToRemove, clinicAdminService) {
4898
+ async function removeTags(db, clinicId, adminId, tagsToRemove, clinicAdminService, app) {
4376
4899
  const clinic = await getClinic(db, clinicId);
4377
4900
  if (!clinic) {
4378
4901
  throw new Error("Clinic not found");
@@ -4381,14 +4904,12 @@ async function removeTags(db, clinicId, adminId, tagsToRemove, clinicAdminServic
4381
4904
  if (!admin) {
4382
4905
  throw new Error("Admin not found");
4383
4906
  }
4384
- if (!admin.isGroupOwner && !admin.clinicsManaged.includes(clinicId)) {
4907
+ const hasPermission = admin.isGroupOwner && admin.clinicGroupId === clinic.clinicGroupId || admin.clinicsManaged.includes(clinicId) && clinic.admins && clinic.admins.includes(adminId);
4908
+ if (!hasPermission) {
4385
4909
  throw new Error("Admin does not have permission to update this clinic");
4386
4910
  }
4387
4911
  const updatedTags = clinic.tags.filter(
4388
- (tag) => {
4389
- var _a;
4390
- return !((_a = tagsToRemove.tags) == null ? void 0 : _a.includes(tag));
4391
- }
4912
+ (tag) => !tagsToRemove.tags || !tagsToRemove.tags.includes(tag)
4392
4913
  );
4393
4914
  return updateClinic(
4394
4915
  db,
@@ -4397,7 +4918,8 @@ async function removeTags(db, clinicId, adminId, tagsToRemove, clinicAdminServic
4397
4918
  tags: updatedTags
4398
4919
  },
4399
4920
  adminId,
4400
- clinicAdminService
4921
+ clinicAdminService,
4922
+ app
4401
4923
  );
4402
4924
  }
4403
4925
 
@@ -4472,7 +4994,8 @@ var ClinicService = class extends BaseService {
4472
4994
  data,
4473
4995
  creatorAdminId,
4474
4996
  this.clinicGroupService,
4475
- this.clinicAdminService
4997
+ this.clinicAdminService,
4998
+ this.app
4476
4999
  );
4477
5000
  }
4478
5001
  /**
@@ -4507,14 +5030,15 @@ var ClinicService = class extends BaseService {
4507
5030
  clinicId,
4508
5031
  data,
4509
5032
  adminId,
4510
- this.clinicAdminService
5033
+ this.clinicAdminService,
5034
+ this.app
4511
5035
  );
4512
5036
  }
4513
5037
  /**
4514
5038
  * Dodaje recenziju klinici
4515
5039
  */
4516
5040
  async addReview(clinicId, review) {
4517
- return addReview(this.db, clinicId, review);
5041
+ return addReview(this.db, clinicId, review, this.app);
4518
5042
  }
4519
5043
  /**
4520
5044
  * Deaktivira kliniku
@@ -4536,7 +5060,8 @@ var ClinicService = class extends BaseService {
4536
5060
  clinicId,
4537
5061
  adminId,
4538
5062
  newTags,
4539
- this.clinicAdminService
5063
+ this.clinicAdminService,
5064
+ this.app
4540
5065
  );
4541
5066
  }
4542
5067
  /**
@@ -4548,7 +5073,8 @@ var ClinicService = class extends BaseService {
4548
5073
  clinicId,
4549
5074
  adminId,
4550
5075
  tagsToRemove,
4551
- this.clinicAdminService
5076
+ this.clinicAdminService,
5077
+ this.app
4552
5078
  );
4553
5079
  }
4554
5080
  /**
@@ -4583,12 +5109,21 @@ var ClinicService = class extends BaseService {
4583
5109
  * @returns The created clinic
4584
5110
  */
4585
5111
  async createClinicBranch(clinicGroupId, setupData, adminId) {
5112
+ var _a, _b;
5113
+ console.log("[CLINIC_SERVICE] Starting clinic branch creation", {
5114
+ clinicGroupId,
5115
+ adminId
5116
+ });
4586
5117
  const clinicGroup = await this.clinicGroupService.getClinicGroup(
4587
5118
  clinicGroupId
4588
5119
  );
4589
5120
  if (!clinicGroup) {
5121
+ console.error("[CLINIC_SERVICE] Clinic group not found", {
5122
+ clinicGroupId
5123
+ });
4590
5124
  throw new Error(`Clinic group with ID ${clinicGroupId} not found`);
4591
5125
  }
5126
+ console.log("[CLINIC_SERVICE] Clinic group verified");
4592
5127
  const createClinicData = {
4593
5128
  clinicGroupId,
4594
5129
  name: setupData.name,
@@ -4597,8 +5132,8 @@ var ClinicService = class extends BaseService {
4597
5132
  contactInfo: setupData.contactInfo,
4598
5133
  workingHours: setupData.workingHours,
4599
5134
  tags: setupData.tags,
4600
- photos: setupData.photos,
4601
- photosWithTags: setupData.photosWithTags,
5135
+ photos: setupData.photos || [],
5136
+ photosWithTags: setupData.photosWithTags || [],
4602
5137
  doctors: [],
4603
5138
  services: [],
4604
5139
  admins: [adminId],
@@ -4607,7 +5142,17 @@ var ClinicService = class extends BaseService {
4607
5142
  logo: setupData.logo,
4608
5143
  featuredPhotos: setupData.featuredPhotos || []
4609
5144
  };
5145
+ console.log("[CLINIC_SERVICE] Creating clinic branch with data", {
5146
+ name: createClinicData.name,
5147
+ hasLogo: !!createClinicData.logo,
5148
+ photosCount: createClinicData.photos.length,
5149
+ featuredPhotosCount: ((_a = createClinicData.featuredPhotos) == null ? void 0 : _a.length) || 0,
5150
+ photosWithTagsCount: ((_b = createClinicData.photosWithTags) == null ? void 0 : _b.length) || 0
5151
+ });
4610
5152
  const clinic = await this.createClinic(createClinicData, adminId);
5153
+ console.log("[CLINIC_SERVICE] Clinic branch created successfully", {
5154
+ clinicId: clinic.id
5155
+ });
4611
5156
  return clinic;
4612
5157
  }
4613
5158
  };
@@ -4729,12 +5274,38 @@ var AuthService = class extends BaseService {
4729
5274
  "Clinic group data is required when creating a new group"
4730
5275
  );
4731
5276
  }
5277
+ console.log("[AUTH] Creating clinic admin first (without group)");
5278
+ const createClinicAdminData = {
5279
+ userRef: firebaseUser.uid,
5280
+ isGroupOwner: true,
5281
+ clinicsManaged: [],
5282
+ contactInfo: contactPerson,
5283
+ roleTitle: data.title,
5284
+ isActive: true
5285
+ // No clinicGroupId yet
5286
+ };
5287
+ let adminProfile;
5288
+ try {
5289
+ adminProfile = await clinicAdminService.createClinicAdmin(
5290
+ createClinicAdminData
5291
+ );
5292
+ console.log("[AUTH] Clinic admin created successfully", {
5293
+ adminId: adminProfile.id
5294
+ });
5295
+ } catch (adminCreationError) {
5296
+ console.error(
5297
+ "[AUTH] Clinic admin creation failed:",
5298
+ adminCreationError
5299
+ );
5300
+ throw adminCreationError;
5301
+ }
4732
5302
  const createClinicGroupData = {
4733
5303
  name: data.clinicGroupData.name,
4734
5304
  hqLocation: data.clinicGroupData.hqLocation,
4735
5305
  contactInfo: data.clinicGroupData.contactInfo,
4736
5306
  contactPerson,
4737
- ownerId: firebaseUser.uid,
5307
+ ownerId: adminProfile.id,
5308
+ // Use admin profile ID, not user UID
4738
5309
  isActive: true,
4739
5310
  logo: data.clinicGroupData.logo || null,
4740
5311
  subscriptionModel: data.clinicGroupData.subscriptionModel || "no_subscription" /* NO_SUBSCRIPTION */
@@ -4742,13 +5313,24 @@ var AuthService = class extends BaseService {
4742
5313
  console.log("[AUTH] Clinic group data prepared", {
4743
5314
  groupName: createClinicGroupData.name
4744
5315
  });
5316
+ let clinicGroup;
4745
5317
  try {
4746
- await clinicGroupService.createClinicGroup(
5318
+ clinicGroup = await clinicGroupService.createClinicGroup(
4747
5319
  createClinicGroupData,
4748
- firebaseUser.uid,
4749
- true
5320
+ adminProfile.id,
5321
+ // Use admin profile ID, not user UID
5322
+ false
5323
+ // This is not a default group since we're providing complete data
4750
5324
  );
4751
- console.log("[AUTH] Clinic group created successfully");
5325
+ console.log("[AUTH] Clinic group created successfully", {
5326
+ groupId: clinicGroup.id
5327
+ });
5328
+ console.log("[AUTH] Updating admin with clinic group ID");
5329
+ await clinicAdminService.updateClinicAdmin(adminProfile.id, {
5330
+ // Use admin profile ID, not user UID
5331
+ clinicGroupId: clinicGroup.id
5332
+ });
5333
+ console.log("[AUTH] Admin updated with clinic group ID successfully");
4752
5334
  } catch (groupCreationError) {
4753
5335
  console.error(
4754
5336
  "[AUTH] Clinic group creation failed:",
@@ -4821,9 +5403,14 @@ var AuthService = class extends BaseService {
4821
5403
  roleTitle: data.title,
4822
5404
  isActive: true
4823
5405
  };
5406
+ let adminProfile;
4824
5407
  try {
4825
- await clinicAdminService.createClinicAdmin(createClinicAdminData);
4826
- console.log("[AUTH] Clinic admin created successfully");
5408
+ adminProfile = await clinicAdminService.createClinicAdmin(
5409
+ createClinicAdminData
5410
+ );
5411
+ console.log("[AUTH] Clinic admin created successfully", {
5412
+ adminId: adminProfile.id
5413
+ });
4827
5414
  } catch (adminCreationError) {
4828
5415
  console.error(
4829
5416
  "[AUTH] Clinic admin creation failed:",
@@ -5132,7 +5719,7 @@ var AuthService = class extends BaseService {
5132
5719
  import {
5133
5720
  collection as collection10,
5134
5721
  doc as doc11,
5135
- getDoc as getDoc14,
5722
+ getDoc as getDoc15,
5136
5723
  getDocs as getDocs9,
5137
5724
  query as query9,
5138
5725
  where as where9,
@@ -5140,8 +5727,8 @@ import {
5140
5727
  deleteDoc as deleteDoc6,
5141
5728
  orderBy,
5142
5729
  Timestamp as Timestamp14,
5143
- addDoc,
5144
- writeBatch as writeBatch2
5730
+ addDoc as addDoc3,
5731
+ writeBatch as writeBatch3
5145
5732
  } from "firebase/firestore";
5146
5733
 
5147
5734
  // src/types/notifications/index.ts
@@ -5178,7 +5765,7 @@ var NotificationService = class extends BaseService {
5178
5765
  isRead: false,
5179
5766
  userRole: notification.userRole || "patient" /* PATIENT */
5180
5767
  };
5181
- const docRef = await addDoc(notificationsRef, notificationData);
5768
+ const docRef = await addDoc3(notificationsRef, notificationData);
5182
5769
  return {
5183
5770
  ...notificationData,
5184
5771
  id: docRef.id
@@ -5193,7 +5780,7 @@ var NotificationService = class extends BaseService {
5193
5780
  NOTIFICATIONS_COLLECTION,
5194
5781
  notificationId
5195
5782
  );
5196
- const notificationDoc = await getDoc14(notificationRef);
5783
+ const notificationDoc = await getDoc15(notificationRef);
5197
5784
  if (!notificationDoc.exists()) {
5198
5785
  return null;
5199
5786
  }
@@ -5252,7 +5839,7 @@ var NotificationService = class extends BaseService {
5252
5839
  */
5253
5840
  async markAllAsRead(userId) {
5254
5841
  const notifications = await this.getUnreadNotifications(userId);
5255
- const batch = writeBatch2(this.db);
5842
+ const batch = writeBatch3(this.db);
5256
5843
  notifications.forEach((notification) => {
5257
5844
  const notificationRef = doc11(
5258
5845
  this.db,
@@ -5328,7 +5915,7 @@ var NotificationService = class extends BaseService {
5328
5915
  import {
5329
5916
  collection as collection11,
5330
5917
  doc as doc12,
5331
- getDoc as getDoc15,
5918
+ getDoc as getDoc16,
5332
5919
  getDocs as getDocs10,
5333
5920
  setDoc as setDoc13,
5334
5921
  updateDoc as updateDoc14,
@@ -5384,7 +5971,7 @@ var DocumentationTemplateService = class extends BaseService {
5384
5971
  */
5385
5972
  async getTemplateById(templateId) {
5386
5973
  const docRef = doc12(this.collectionRef, templateId);
5387
- const docSnap = await getDoc15(docRef);
5974
+ const docSnap = await getDoc16(docRef);
5388
5975
  if (!docSnap.exists()) {
5389
5976
  return null;
5390
5977
  }
@@ -5523,7 +6110,7 @@ var DocumentationTemplateService = class extends BaseService {
5523
6110
  import {
5524
6111
  collection as collection12,
5525
6112
  doc as doc13,
5526
- getDoc as getDoc16,
6113
+ getDoc as getDoc17,
5527
6114
  getDocs as getDocs11,
5528
6115
  setDoc as setDoc14,
5529
6116
  updateDoc as updateDoc15,
@@ -5580,7 +6167,7 @@ var FilledDocumentService = class extends BaseService {
5580
6167
  */
5581
6168
  async getFilledDocumentById(documentId) {
5582
6169
  const docRef = doc13(this.collectionRef, documentId);
5583
- const docSnap = await getDoc16(docRef);
6170
+ const docSnap = await getDoc17(docRef);
5584
6171
  if (!docSnap.exists()) {
5585
6172
  return null;
5586
6173
  }