@blackcode_sa/metaestetics-api 1.4.2 → 1.4.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.4.2",
4
+ "version": "1.4.3",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -45,6 +45,21 @@ import { FirebaseError } from "../errors/firebase.errors";
45
45
  import { BaseService } from "./base.service";
46
46
  import { UserService } from "./user.service";
47
47
  import { throws } from "assert";
48
+ import {
49
+ ClinicGroup,
50
+ AdminToken,
51
+ AdminTokenStatus,
52
+ CreateClinicGroupData,
53
+ CreateClinicAdminData,
54
+ ContactPerson,
55
+ ClinicAdminSignupData,
56
+ SubscriptionModel,
57
+ CLINIC_GROUPS_COLLECTION,
58
+ } from "../types/clinic";
59
+ import { clinicAdminSignupSchema } from "../validations/clinic.schema";
60
+ import { ClinicGroupService } from "./clinic/clinic-group.service";
61
+ import { ClinicAdminService } from "./clinic/clinic-admin.service";
62
+ import { ClinicService } from "./clinic/clinic.service";
48
63
 
49
64
  export class AuthService extends BaseService {
50
65
  private googleProvider = new GoogleAuthProvider();
@@ -84,6 +99,164 @@ export class AuthService extends BaseService {
84
99
  return this.userService.createUser(firebaseUser, [initialRole]);
85
100
  }
86
101
 
102
+ /**
103
+ * Registers a new clinic admin user with email and password
104
+ * Can either create a new clinic group or join an existing one with a token
105
+ *
106
+ * @param data - Clinic admin signup data
107
+ * @returns The created user
108
+ */
109
+ async signUpClinicAdmin(data: ClinicAdminSignupData): Promise<User> {
110
+ try {
111
+ // Validate data
112
+ await clinicAdminSignupSchema.parseAsync(data);
113
+
114
+ // Create Firebase user
115
+ const { user: firebaseUser } = await createUserWithEmailAndPassword(
116
+ this.auth,
117
+ data.email,
118
+ data.password
119
+ );
120
+
121
+ // Create user with CLINIC_ADMIN role
122
+ const user = await this.userService.createUser(firebaseUser, [
123
+ UserRole.CLINIC_ADMIN,
124
+ ]);
125
+
126
+ // Create contact person object
127
+ const contactPerson: ContactPerson = {
128
+ firstName: data.firstName,
129
+ lastName: data.lastName,
130
+ title: data.title,
131
+ email: data.email,
132
+ phoneNumber: data.phoneNumber,
133
+ };
134
+
135
+ // Initialize services
136
+ const clinicAdminService = new ClinicAdminService(
137
+ this.db,
138
+ this.auth,
139
+ this.app
140
+ );
141
+ const clinicGroupService = new ClinicGroupService(
142
+ this.db,
143
+ this.auth,
144
+ this.app,
145
+ clinicAdminService
146
+ );
147
+ const clinicService = new ClinicService(
148
+ this.db,
149
+ this.auth,
150
+ this.app,
151
+ clinicGroupService,
152
+ clinicAdminService
153
+ );
154
+
155
+ // Set services to resolve circular dependencies
156
+ clinicAdminService.setServices(clinicGroupService, clinicService);
157
+
158
+ if (data.isCreatingNewGroup) {
159
+ // Create new clinic group
160
+ if (!data.clinicGroupData) {
161
+ throw new Error(
162
+ "Clinic group data is required when creating a new group"
163
+ );
164
+ }
165
+
166
+ // Create clinic group
167
+ const createClinicGroupData: CreateClinicGroupData = {
168
+ name: data.clinicGroupData.name,
169
+ hqLocation: data.clinicGroupData.hqLocation,
170
+ contactInfo: data.clinicGroupData.contactInfo,
171
+ contactPerson: contactPerson,
172
+ ownerId: firebaseUser.uid,
173
+ isActive: true,
174
+ logo: data.clinicGroupData.logo,
175
+ subscriptionModel:
176
+ data.clinicGroupData.subscriptionModel ||
177
+ SubscriptionModel.NO_SUBSCRIPTION,
178
+ };
179
+
180
+ // Create clinic group
181
+ await clinicGroupService.createClinicGroup(
182
+ createClinicGroupData,
183
+ firebaseUser.uid,
184
+ true
185
+ );
186
+ } else {
187
+ // Join existing clinic group with token
188
+ if (!data.inviteToken) {
189
+ throw new Error(
190
+ "Invite token is required when joining an existing group"
191
+ );
192
+ }
193
+
194
+ // Find the token in the database
195
+ const groupsRef = collection(this.db, CLINIC_GROUPS_COLLECTION);
196
+ const q = query(groupsRef);
197
+ const querySnapshot = await getDocs(q);
198
+
199
+ let foundGroup: ClinicGroup | null = null;
200
+ let foundToken: AdminToken | null = null;
201
+
202
+ for (const docSnapshot of querySnapshot.docs) {
203
+ const group = docSnapshot.data() as ClinicGroup;
204
+
205
+ // Find the token in the group's tokens
206
+ const token = group.adminTokens.find(
207
+ (t) =>
208
+ t.token === data.inviteToken &&
209
+ t.status === AdminTokenStatus.ACTIVE &&
210
+ new Date(t.expiresAt.toDate()) > new Date()
211
+ );
212
+
213
+ if (token) {
214
+ foundGroup = group;
215
+ foundToken = token;
216
+ break;
217
+ }
218
+ }
219
+
220
+ if (!foundGroup || !foundToken) {
221
+ throw new Error("Invalid or expired invite token");
222
+ }
223
+
224
+ // Create clinic admin
225
+ const createClinicAdminData: CreateClinicAdminData = {
226
+ userRef: firebaseUser.uid,
227
+ clinicGroupId: foundGroup.id,
228
+ isGroupOwner: false,
229
+ clinicsManaged: [],
230
+ contactInfo: contactPerson,
231
+ roleTitle: data.title,
232
+ isActive: true,
233
+ };
234
+
235
+ await clinicAdminService.createClinicAdmin(createClinicAdminData);
236
+
237
+ // Mark token as used
238
+ await clinicGroupService.verifyAndUseAdminToken(
239
+ foundGroup.id,
240
+ data.inviteToken,
241
+ firebaseUser.uid
242
+ );
243
+ }
244
+
245
+ return user;
246
+ } catch (error) {
247
+ if (error instanceof z.ZodError) {
248
+ throw AUTH_ERRORS.VALIDATION_ERROR;
249
+ }
250
+
251
+ const firebaseError = error as FirebaseError;
252
+ if (firebaseError.code === FirebaseErrorCode.EMAIL_ALREADY_IN_USE) {
253
+ throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
254
+ }
255
+
256
+ throw error;
257
+ }
258
+ }
259
+
87
260
  /**
88
261
  * Prijavljuje korisnika sa email-om i lozinkom
89
262
  */
@@ -10,6 +10,7 @@ import {
10
10
  deleteDoc,
11
11
  Timestamp,
12
12
  serverTimestamp,
13
+ FieldValue,
13
14
  } from "firebase/firestore";
14
15
  import { BaseService } from "../base.service";
15
16
  import {
@@ -19,6 +20,8 @@ import {
19
20
  AdminToken,
20
21
  AdminTokenStatus,
21
22
  CreateAdminTokenData,
23
+ ClinicGroupSetupData,
24
+ UpdateClinicGroupData,
22
25
  } from "../../types/clinic";
23
26
  import { ClinicAdminService } from "./clinic-admin.service";
24
27
  import { geohashForLocation } from "geofire-common";
@@ -107,6 +110,37 @@ export class ClinicGroupService extends BaseService {
107
110
  return ClinicGroupUtils.deactivateClinicGroup(this.db, groupId);
108
111
  }
109
112
 
113
+ /**
114
+ * Sets up additional clinic group information after initial creation
115
+ *
116
+ * @param groupId - The ID of the clinic group to set up
117
+ * @param setupData - The setup data for the clinic group
118
+ * @returns The updated clinic group
119
+ */
120
+ async setupClinicGroup(
121
+ groupId: string,
122
+ setupData: ClinicGroupSetupData
123
+ ): Promise<ClinicGroup> {
124
+ // Get the clinic group
125
+ const clinicGroup = await this.getClinicGroup(groupId);
126
+ if (!clinicGroup) {
127
+ throw new Error(`Clinic group with ID ${groupId} not found`);
128
+ }
129
+
130
+ // Update the clinic group with the setup data
131
+ const updateData = {
132
+ languages: setupData.languages,
133
+ practiceType: setupData.practiceType,
134
+ description: setupData.description,
135
+ logo: setupData.logo,
136
+ calendarSyncEnabled: setupData.calendarSyncEnabled,
137
+ autoConfirmAppointments: setupData.autoConfirmAppointments,
138
+ };
139
+
140
+ // Update the clinic group
141
+ return this.updateClinicGroup(groupId, updateData);
142
+ }
143
+
110
144
  /**
111
145
  * Kreira admin token za grupaciju
112
146
  */
@@ -12,6 +12,7 @@ import {
12
12
  serverTimestamp,
13
13
  GeoPoint,
14
14
  QueryConstraint,
15
+ FieldValue,
15
16
  } from "firebase/firestore";
16
17
  import { BaseService } from "../base.service";
17
18
  import {
@@ -22,6 +23,8 @@ import {
22
23
  ClinicTag,
23
24
  ClinicTags,
24
25
  ClinicGroup,
26
+ ClinicBranchSetupData,
27
+ CLINIC_ADMINS_COLLECTION,
25
28
  } from "../../types/clinic";
26
29
  import { ClinicGroupService } from "./clinic-group.service";
27
30
  import { ClinicAdminService } from "./clinic-admin.service";
@@ -43,6 +46,7 @@ import * as ClinicUtils from "./utils/clinic.utils";
43
46
  import * as ReviewUtils from "./utils/review.utils";
44
47
  import * as TagUtils from "./utils/tag.utils";
45
48
  import * as SearchUtils from "./utils/search.utils";
49
+ import * as AdminUtils from "./utils/admin.utils";
46
50
 
47
51
  export class ClinicService extends BaseService {
48
52
  private clinicGroupService: ClinicGroupService;
@@ -219,4 +223,53 @@ export class ClinicService extends BaseService {
219
223
  this.clinicGroupService
220
224
  );
221
225
  }
226
+
227
+ /**
228
+ * Creates a new clinic branch for a clinic group
229
+ *
230
+ * @param clinicGroupId - The ID of the clinic group
231
+ * @param setupData - The setup data for the clinic branch
232
+ * @param adminId - The ID of the admin creating the branch
233
+ * @returns The created clinic
234
+ */
235
+ async createClinicBranch(
236
+ clinicGroupId: string,
237
+ setupData: ClinicBranchSetupData,
238
+ adminId: string
239
+ ): Promise<Clinic> {
240
+ // Validate that the clinic group exists
241
+ const clinicGroup = await this.clinicGroupService.getClinicGroup(
242
+ clinicGroupId
243
+ );
244
+ if (!clinicGroup) {
245
+ throw new Error(`Clinic group with ID ${clinicGroupId} not found`);
246
+ }
247
+
248
+ // Create the clinic data
249
+ const createClinicData: CreateClinicData = {
250
+ clinicGroupId,
251
+ name: setupData.name,
252
+ description: setupData.description,
253
+ location: setupData.location,
254
+ contactInfo: setupData.contactInfo,
255
+ workingHours: setupData.workingHours,
256
+ tags: setupData.tags,
257
+ photos: setupData.photos,
258
+ photosWithTags: setupData.photosWithTags,
259
+ doctors: [],
260
+ services: [],
261
+ admins: [adminId],
262
+ isActive: true,
263
+ isVerified: false,
264
+ logo: setupData.logo,
265
+ featuredPhotos: setupData.featuredPhotos || [],
266
+ };
267
+
268
+ // Create the clinic
269
+ const clinic = await this.createClinic(createClinicData, adminId);
270
+
271
+ // Note: The createClinic method already adds the clinic to the admin's managed clinics
272
+
273
+ return clinic;
274
+ }
222
275
  }