@blackcode_sa/metaestetics-api 1.4.2 → 1.4.4
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 +2842 -407
- package/dist/index.d.ts +2842 -407
- package/dist/index.js +1285 -969
- package/dist/index.mjs +1207 -893
- package/package.json +1 -1
- package/src/services/auth.service.ts +177 -0
- package/src/services/clinic/clinic-group.service.ts +34 -0
- package/src/services/clinic/clinic.service.ts +53 -0
- package/src/services/user.service.ts +8 -0
- package/src/types/clinic/index.ts +165 -100
- package/src/types/clinic/preferences.types.ts +101 -0
- package/src/validations/clinic.schema.ts +162 -34
package/package.json
CHANGED
|
@@ -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,168 @@ 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(
|
|
123
|
+
firebaseUser,
|
|
124
|
+
[UserRole.CLINIC_ADMIN],
|
|
125
|
+
{
|
|
126
|
+
skipProfileCreation: true,
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Create contact person object
|
|
131
|
+
const contactPerson: ContactPerson = {
|
|
132
|
+
firstName: data.firstName,
|
|
133
|
+
lastName: data.lastName,
|
|
134
|
+
title: data.title,
|
|
135
|
+
email: data.email,
|
|
136
|
+
phoneNumber: data.phoneNumber,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Initialize services
|
|
140
|
+
const clinicAdminService = new ClinicAdminService(
|
|
141
|
+
this.db,
|
|
142
|
+
this.auth,
|
|
143
|
+
this.app
|
|
144
|
+
);
|
|
145
|
+
const clinicGroupService = new ClinicGroupService(
|
|
146
|
+
this.db,
|
|
147
|
+
this.auth,
|
|
148
|
+
this.app,
|
|
149
|
+
clinicAdminService
|
|
150
|
+
);
|
|
151
|
+
const clinicService = new ClinicService(
|
|
152
|
+
this.db,
|
|
153
|
+
this.auth,
|
|
154
|
+
this.app,
|
|
155
|
+
clinicGroupService,
|
|
156
|
+
clinicAdminService
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Set services to resolve circular dependencies
|
|
160
|
+
clinicAdminService.setServices(clinicGroupService, clinicService);
|
|
161
|
+
|
|
162
|
+
if (data.isCreatingNewGroup) {
|
|
163
|
+
// Create new clinic group
|
|
164
|
+
if (!data.clinicGroupData) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
"Clinic group data is required when creating a new group"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Create clinic group
|
|
171
|
+
const createClinicGroupData: CreateClinicGroupData = {
|
|
172
|
+
name: data.clinicGroupData.name,
|
|
173
|
+
hqLocation: data.clinicGroupData.hqLocation,
|
|
174
|
+
contactInfo: data.clinicGroupData.contactInfo,
|
|
175
|
+
contactPerson: contactPerson,
|
|
176
|
+
ownerId: firebaseUser.uid,
|
|
177
|
+
isActive: true,
|
|
178
|
+
logo: data.clinicGroupData.logo,
|
|
179
|
+
subscriptionModel:
|
|
180
|
+
data.clinicGroupData.subscriptionModel ||
|
|
181
|
+
SubscriptionModel.NO_SUBSCRIPTION,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Create clinic group
|
|
185
|
+
await clinicGroupService.createClinicGroup(
|
|
186
|
+
createClinicGroupData,
|
|
187
|
+
firebaseUser.uid,
|
|
188
|
+
true
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
// Join existing clinic group with token
|
|
192
|
+
if (!data.inviteToken) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
"Invite token is required when joining an existing group"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Find the token in the database
|
|
199
|
+
const groupsRef = collection(this.db, CLINIC_GROUPS_COLLECTION);
|
|
200
|
+
const q = query(groupsRef);
|
|
201
|
+
const querySnapshot = await getDocs(q);
|
|
202
|
+
|
|
203
|
+
let foundGroup: ClinicGroup | null = null;
|
|
204
|
+
let foundToken: AdminToken | null = null;
|
|
205
|
+
|
|
206
|
+
for (const docSnapshot of querySnapshot.docs) {
|
|
207
|
+
const group = docSnapshot.data() as ClinicGroup;
|
|
208
|
+
|
|
209
|
+
// Find the token in the group's tokens
|
|
210
|
+
const token = group.adminTokens.find(
|
|
211
|
+
(t) =>
|
|
212
|
+
t.token === data.inviteToken &&
|
|
213
|
+
t.status === AdminTokenStatus.ACTIVE &&
|
|
214
|
+
new Date(t.expiresAt.toDate()) > new Date()
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (token) {
|
|
218
|
+
foundGroup = group;
|
|
219
|
+
foundToken = token;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!foundGroup || !foundToken) {
|
|
225
|
+
throw new Error("Invalid or expired invite token");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Create clinic admin
|
|
229
|
+
const createClinicAdminData: CreateClinicAdminData = {
|
|
230
|
+
userRef: firebaseUser.uid,
|
|
231
|
+
clinicGroupId: foundGroup.id,
|
|
232
|
+
isGroupOwner: false,
|
|
233
|
+
clinicsManaged: [],
|
|
234
|
+
contactInfo: contactPerson,
|
|
235
|
+
roleTitle: data.title,
|
|
236
|
+
isActive: true,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
await clinicAdminService.createClinicAdmin(createClinicAdminData);
|
|
240
|
+
|
|
241
|
+
// Mark token as used
|
|
242
|
+
await clinicGroupService.verifyAndUseAdminToken(
|
|
243
|
+
foundGroup.id,
|
|
244
|
+
data.inviteToken,
|
|
245
|
+
firebaseUser.uid
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return user;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof z.ZodError) {
|
|
252
|
+
throw AUTH_ERRORS.VALIDATION_ERROR;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const firebaseError = error as FirebaseError;
|
|
256
|
+
if (firebaseError.code === FirebaseErrorCode.EMAIL_ALREADY_IN_USE) {
|
|
257
|
+
throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
87
264
|
/**
|
|
88
265
|
* Prijavljuje korisnika sa email-om i lozinkom
|
|
89
266
|
*/
|
|
@@ -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
|
}
|
|
@@ -86,6 +86,7 @@ export class UserService extends BaseService {
|
|
|
86
86
|
groupToken?: string;
|
|
87
87
|
groupId?: string;
|
|
88
88
|
};
|
|
89
|
+
skipProfileCreation?: boolean;
|
|
89
90
|
}
|
|
90
91
|
): Promise<User> {
|
|
91
92
|
const userData: CreateUserData = {
|
|
@@ -142,6 +143,7 @@ export class UserService extends BaseService {
|
|
|
142
143
|
groupToken?: string;
|
|
143
144
|
groupId?: string;
|
|
144
145
|
};
|
|
146
|
+
skipProfileCreation?: boolean;
|
|
145
147
|
}
|
|
146
148
|
): Promise<{
|
|
147
149
|
patientProfile?: string;
|
|
@@ -172,6 +174,12 @@ export class UserService extends BaseService {
|
|
|
172
174
|
profiles.patientProfile = patientProfile.id;
|
|
173
175
|
break;
|
|
174
176
|
case UserRole.CLINIC_ADMIN:
|
|
177
|
+
// Skip profile creation if explicitly requested
|
|
178
|
+
// This is used when we know the profile will be created elsewhere (e.g. in signUpClinicAdmin)
|
|
179
|
+
if (options?.skipProfileCreation) {
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
175
183
|
// Ako imamo token, verifikujemo ga i dodajemo admina u postojeću grupu
|
|
176
184
|
if (
|
|
177
185
|
options?.clinicAdminData?.groupToken &&
|