@blackcode_sa/metaestetics-api 1.7.43 → 1.7.45
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 +21 -12
- package/dist/index.d.ts +21 -12
- package/dist/index.js +147 -7
- package/dist/index.mjs +150 -8
- package/package.json +1 -1
- package/src/services/practitioner/practitioner.service.ts +4 -3
- package/src/services/procedure/procedure.service.ts +181 -0
- package/src/services/user.service.ts +3 -3
- package/src/types/index.ts +3 -3
- package/src/validations/schemas.ts +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -5936,9 +5936,9 @@ interface User {
|
|
|
5936
5936
|
email: string | null;
|
|
5937
5937
|
roles: UserRole[];
|
|
5938
5938
|
isAnonymous: boolean;
|
|
5939
|
-
createdAt:
|
|
5940
|
-
updatedAt:
|
|
5941
|
-
lastLoginAt:
|
|
5939
|
+
createdAt: any;
|
|
5940
|
+
updatedAt: any;
|
|
5941
|
+
lastLoginAt: any;
|
|
5942
5942
|
patientProfile?: string;
|
|
5943
5943
|
practitionerProfile?: string;
|
|
5944
5944
|
adminProfile?: string;
|
|
@@ -6391,6 +6391,15 @@ declare class ProcedureService extends BaseService {
|
|
|
6391
6391
|
* @returns The created procedure
|
|
6392
6392
|
*/
|
|
6393
6393
|
createProcedure(data: CreateProcedureData): Promise<Procedure>;
|
|
6394
|
+
/**
|
|
6395
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
6396
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
6397
|
+
*
|
|
6398
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
6399
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
6400
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
6401
|
+
*/
|
|
6402
|
+
bulkCreateProcedures(baseData: Omit<CreateProcedureData, "practitionerId">, practitionerIds: string[]): Promise<Procedure[]>;
|
|
6394
6403
|
/**
|
|
6395
6404
|
* Gets a procedure by ID
|
|
6396
6405
|
* @param id - The ID of the procedure to get
|
|
@@ -8627,31 +8636,31 @@ declare const userSchema: z.ZodObject<{
|
|
|
8627
8636
|
email: z.ZodNullable<z.ZodString>;
|
|
8628
8637
|
roles: z.ZodArray<z.ZodNativeEnum<typeof UserRole>, "many">;
|
|
8629
8638
|
isAnonymous: z.ZodBoolean;
|
|
8630
|
-
createdAt: z.
|
|
8631
|
-
updatedAt: z.
|
|
8632
|
-
lastLoginAt: z.
|
|
8639
|
+
createdAt: z.ZodAny;
|
|
8640
|
+
updatedAt: z.ZodAny;
|
|
8641
|
+
lastLoginAt: z.ZodAny;
|
|
8633
8642
|
patientProfile: z.ZodOptional<z.ZodString>;
|
|
8634
8643
|
practitionerProfile: z.ZodOptional<z.ZodString>;
|
|
8635
8644
|
adminProfile: z.ZodOptional<z.ZodString>;
|
|
8636
8645
|
}, "strip", z.ZodTypeAny, {
|
|
8637
|
-
createdAt: Date | Timestamp | FieldValue;
|
|
8638
|
-
updatedAt: Date | Timestamp | FieldValue;
|
|
8639
8646
|
email: string | null;
|
|
8640
8647
|
uid: string;
|
|
8641
8648
|
roles: UserRole[];
|
|
8642
8649
|
isAnonymous: boolean;
|
|
8643
|
-
|
|
8650
|
+
createdAt?: any;
|
|
8651
|
+
updatedAt?: any;
|
|
8652
|
+
lastLoginAt?: any;
|
|
8644
8653
|
patientProfile?: string | undefined;
|
|
8645
8654
|
practitionerProfile?: string | undefined;
|
|
8646
8655
|
adminProfile?: string | undefined;
|
|
8647
8656
|
}, {
|
|
8648
|
-
createdAt: Date | Timestamp | FieldValue;
|
|
8649
|
-
updatedAt: Date | Timestamp | FieldValue;
|
|
8650
8657
|
email: string | null;
|
|
8651
8658
|
uid: string;
|
|
8652
8659
|
roles: UserRole[];
|
|
8653
8660
|
isAnonymous: boolean;
|
|
8654
|
-
|
|
8661
|
+
createdAt?: any;
|
|
8662
|
+
updatedAt?: any;
|
|
8663
|
+
lastLoginAt?: any;
|
|
8655
8664
|
patientProfile?: string | undefined;
|
|
8656
8665
|
practitionerProfile?: string | undefined;
|
|
8657
8666
|
adminProfile?: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -5936,9 +5936,9 @@ interface User {
|
|
|
5936
5936
|
email: string | null;
|
|
5937
5937
|
roles: UserRole[];
|
|
5938
5938
|
isAnonymous: boolean;
|
|
5939
|
-
createdAt:
|
|
5940
|
-
updatedAt:
|
|
5941
|
-
lastLoginAt:
|
|
5939
|
+
createdAt: any;
|
|
5940
|
+
updatedAt: any;
|
|
5941
|
+
lastLoginAt: any;
|
|
5942
5942
|
patientProfile?: string;
|
|
5943
5943
|
practitionerProfile?: string;
|
|
5944
5944
|
adminProfile?: string;
|
|
@@ -6391,6 +6391,15 @@ declare class ProcedureService extends BaseService {
|
|
|
6391
6391
|
* @returns The created procedure
|
|
6392
6392
|
*/
|
|
6393
6393
|
createProcedure(data: CreateProcedureData): Promise<Procedure>;
|
|
6394
|
+
/**
|
|
6395
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
6396
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
6397
|
+
*
|
|
6398
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
6399
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
6400
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
6401
|
+
*/
|
|
6402
|
+
bulkCreateProcedures(baseData: Omit<CreateProcedureData, "practitionerId">, practitionerIds: string[]): Promise<Procedure[]>;
|
|
6394
6403
|
/**
|
|
6395
6404
|
* Gets a procedure by ID
|
|
6396
6405
|
* @param id - The ID of the procedure to get
|
|
@@ -8627,31 +8636,31 @@ declare const userSchema: z.ZodObject<{
|
|
|
8627
8636
|
email: z.ZodNullable<z.ZodString>;
|
|
8628
8637
|
roles: z.ZodArray<z.ZodNativeEnum<typeof UserRole>, "many">;
|
|
8629
8638
|
isAnonymous: z.ZodBoolean;
|
|
8630
|
-
createdAt: z.
|
|
8631
|
-
updatedAt: z.
|
|
8632
|
-
lastLoginAt: z.
|
|
8639
|
+
createdAt: z.ZodAny;
|
|
8640
|
+
updatedAt: z.ZodAny;
|
|
8641
|
+
lastLoginAt: z.ZodAny;
|
|
8633
8642
|
patientProfile: z.ZodOptional<z.ZodString>;
|
|
8634
8643
|
practitionerProfile: z.ZodOptional<z.ZodString>;
|
|
8635
8644
|
adminProfile: z.ZodOptional<z.ZodString>;
|
|
8636
8645
|
}, "strip", z.ZodTypeAny, {
|
|
8637
|
-
createdAt: Date | Timestamp | FieldValue;
|
|
8638
|
-
updatedAt: Date | Timestamp | FieldValue;
|
|
8639
8646
|
email: string | null;
|
|
8640
8647
|
uid: string;
|
|
8641
8648
|
roles: UserRole[];
|
|
8642
8649
|
isAnonymous: boolean;
|
|
8643
|
-
|
|
8650
|
+
createdAt?: any;
|
|
8651
|
+
updatedAt?: any;
|
|
8652
|
+
lastLoginAt?: any;
|
|
8644
8653
|
patientProfile?: string | undefined;
|
|
8645
8654
|
practitionerProfile?: string | undefined;
|
|
8646
8655
|
adminProfile?: string | undefined;
|
|
8647
8656
|
}, {
|
|
8648
|
-
createdAt: Date | Timestamp | FieldValue;
|
|
8649
|
-
updatedAt: Date | Timestamp | FieldValue;
|
|
8650
8657
|
email: string | null;
|
|
8651
8658
|
uid: string;
|
|
8652
8659
|
roles: UserRole[];
|
|
8653
8660
|
isAnonymous: boolean;
|
|
8654
|
-
|
|
8661
|
+
createdAt?: any;
|
|
8662
|
+
updatedAt?: any;
|
|
8663
|
+
lastLoginAt?: any;
|
|
8655
8664
|
patientProfile?: string | undefined;
|
|
8656
8665
|
practitionerProfile?: string | undefined;
|
|
8657
8666
|
adminProfile?: string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -923,9 +923,9 @@ var userSchema = import_zod4.z.object({
|
|
|
923
923
|
email: import_zod4.z.string().email().nullable(),
|
|
924
924
|
roles: import_zod4.z.array(userRoleSchema),
|
|
925
925
|
isAnonymous: import_zod4.z.boolean(),
|
|
926
|
-
createdAt:
|
|
927
|
-
updatedAt:
|
|
928
|
-
lastLoginAt:
|
|
926
|
+
createdAt: import_zod4.z.any(),
|
|
927
|
+
updatedAt: import_zod4.z.any(),
|
|
928
|
+
lastLoginAt: import_zod4.z.any(),
|
|
929
929
|
patientProfile: import_zod4.z.string().optional(),
|
|
930
930
|
practitionerProfile: import_zod4.z.string().optional(),
|
|
931
931
|
adminProfile: import_zod4.z.string().optional()
|
|
@@ -5594,9 +5594,6 @@ var PractitionerService = class extends BaseService {
|
|
|
5594
5594
|
if (!practitioner) {
|
|
5595
5595
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
5596
5596
|
}
|
|
5597
|
-
if (!practitioner.isActive) {
|
|
5598
|
-
throw new Error(`Practitioner ${practitionerId} is not active`);
|
|
5599
|
-
}
|
|
5600
5597
|
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
5601
5598
|
if (!clinic) {
|
|
5602
5599
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
@@ -5955,7 +5952,7 @@ var UserService = class extends BaseService {
|
|
|
5955
5952
|
const q = (0, import_firestore19.query)((0, import_firestore19.collection)(this.db, USERS_COLLECTION), ...constraints);
|
|
5956
5953
|
const querySnapshot = await (0, import_firestore19.getDocs)(q);
|
|
5957
5954
|
const users = querySnapshot.docs.map((doc36) => doc36.data());
|
|
5958
|
-
return
|
|
5955
|
+
return users.map((userData) => userSchema.parse(userData));
|
|
5959
5956
|
}
|
|
5960
5957
|
/**
|
|
5961
5958
|
* Ažurira timestamp poslednjeg logovanja
|
|
@@ -9170,6 +9167,149 @@ var ProcedureService = class extends BaseService {
|
|
|
9170
9167
|
const savedDoc = await (0, import_firestore27.getDoc)(procedureRef);
|
|
9171
9168
|
return savedDoc.data();
|
|
9172
9169
|
}
|
|
9170
|
+
/**
|
|
9171
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
9172
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
9173
|
+
*
|
|
9174
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
9175
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
9176
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
9177
|
+
*/
|
|
9178
|
+
async bulkCreateProcedures(baseData, practitionerIds) {
|
|
9179
|
+
var _a;
|
|
9180
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
9181
|
+
throw new Error("Practitioner IDs array cannot be empty.");
|
|
9182
|
+
}
|
|
9183
|
+
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
9184
|
+
const validatedData = createProcedureSchema.parse(validationData);
|
|
9185
|
+
const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
|
|
9186
|
+
this.categoryService.getById(validatedData.categoryId),
|
|
9187
|
+
this.subcategoryService.getById(
|
|
9188
|
+
validatedData.categoryId,
|
|
9189
|
+
validatedData.subcategoryId
|
|
9190
|
+
),
|
|
9191
|
+
this.technologyService.getById(validatedData.technologyId),
|
|
9192
|
+
this.productService.getById(
|
|
9193
|
+
validatedData.technologyId,
|
|
9194
|
+
validatedData.productId
|
|
9195
|
+
),
|
|
9196
|
+
(0, import_firestore27.getDoc)((0, import_firestore27.doc)(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
9197
|
+
]);
|
|
9198
|
+
if (!category || !subcategory || !technology || !product) {
|
|
9199
|
+
throw new Error("One or more required base entities not found");
|
|
9200
|
+
}
|
|
9201
|
+
if (!clinicSnapshot.exists()) {
|
|
9202
|
+
throw new Error(
|
|
9203
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
9204
|
+
);
|
|
9205
|
+
}
|
|
9206
|
+
const clinic = clinicSnapshot.data();
|
|
9207
|
+
let processedPhotos = [];
|
|
9208
|
+
if (validatedData.photos && validatedData.photos.length > 0) {
|
|
9209
|
+
const batchId = this.generateId();
|
|
9210
|
+
processedPhotos = await this.processMediaArray(
|
|
9211
|
+
validatedData.photos,
|
|
9212
|
+
batchId,
|
|
9213
|
+
"procedure-photos-batch"
|
|
9214
|
+
);
|
|
9215
|
+
}
|
|
9216
|
+
const practitionersMap = /* @__PURE__ */ new Map();
|
|
9217
|
+
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
9218
|
+
const chunk = practitionerIds.slice(i, i + 30);
|
|
9219
|
+
const practitionersQuery = (0, import_firestore27.query)(
|
|
9220
|
+
(0, import_firestore27.collection)(this.db, PRACTITIONERS_COLLECTION),
|
|
9221
|
+
(0, import_firestore27.where)((0, import_firestore27.documentId)(), "in", chunk)
|
|
9222
|
+
);
|
|
9223
|
+
const practitionersSnapshot = await (0, import_firestore27.getDocs)(practitionersQuery);
|
|
9224
|
+
practitionersSnapshot.docs.forEach((doc36) => {
|
|
9225
|
+
practitionersMap.set(doc36.id, doc36.data());
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
9228
|
+
if (practitionersMap.size !== practitionerIds.length) {
|
|
9229
|
+
const foundIds = Array.from(practitionersMap.keys());
|
|
9230
|
+
const notFoundIds = practitionerIds.filter(
|
|
9231
|
+
(id) => !foundIds.includes(id)
|
|
9232
|
+
);
|
|
9233
|
+
throw new Error(
|
|
9234
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
9235
|
+
);
|
|
9236
|
+
}
|
|
9237
|
+
const batch = (0, import_firestore27.writeBatch)(this.db);
|
|
9238
|
+
const createdProcedureIds = [];
|
|
9239
|
+
const clinicInfo = {
|
|
9240
|
+
id: clinicSnapshot.id,
|
|
9241
|
+
name: clinic.name,
|
|
9242
|
+
description: clinic.description || "",
|
|
9243
|
+
featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? typeof clinic.featuredPhotos[0] === "string" ? clinic.featuredPhotos[0] : "" : typeof clinic.coverPhoto === "string" ? clinic.coverPhoto : "",
|
|
9244
|
+
location: clinic.location,
|
|
9245
|
+
contactInfo: clinic.contactInfo
|
|
9246
|
+
};
|
|
9247
|
+
for (const practitionerId of practitionerIds) {
|
|
9248
|
+
const practitioner = practitionersMap.get(practitionerId);
|
|
9249
|
+
const doctorInfo = {
|
|
9250
|
+
id: practitioner.id,
|
|
9251
|
+
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
9252
|
+
description: practitioner.basicInfo.bio || "",
|
|
9253
|
+
photo: typeof practitioner.basicInfo.profileImageUrl === "string" ? practitioner.basicInfo.profileImageUrl : "",
|
|
9254
|
+
rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
|
|
9255
|
+
services: practitioner.procedures || []
|
|
9256
|
+
};
|
|
9257
|
+
const procedureId = this.generateId();
|
|
9258
|
+
createdProcedureIds.push(procedureId);
|
|
9259
|
+
const procedureRef = (0, import_firestore27.doc)(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
9260
|
+
const newProcedure = {
|
|
9261
|
+
id: procedureId,
|
|
9262
|
+
...validatedData,
|
|
9263
|
+
practitionerId,
|
|
9264
|
+
// Override practitionerId with the correct one
|
|
9265
|
+
photos: processedPhotos,
|
|
9266
|
+
category,
|
|
9267
|
+
subcategory,
|
|
9268
|
+
technology,
|
|
9269
|
+
product,
|
|
9270
|
+
blockingConditions: technology.blockingConditions,
|
|
9271
|
+
contraindications: technology.contraindications || [],
|
|
9272
|
+
treatmentBenefits: technology.benefits,
|
|
9273
|
+
preRequirements: technology.requirements.pre,
|
|
9274
|
+
postRequirements: technology.requirements.post,
|
|
9275
|
+
certificationRequirement: technology.certificationRequirement,
|
|
9276
|
+
documentationTemplates: (technology == null ? void 0 : technology.documentationTemplates) || [],
|
|
9277
|
+
clinicInfo,
|
|
9278
|
+
doctorInfo,
|
|
9279
|
+
// Set specific doctor info
|
|
9280
|
+
reviewInfo: {
|
|
9281
|
+
totalReviews: 0,
|
|
9282
|
+
averageRating: 0,
|
|
9283
|
+
effectivenessOfTreatment: 0,
|
|
9284
|
+
outcomeExplanation: 0,
|
|
9285
|
+
painManagement: 0,
|
|
9286
|
+
followUpCare: 0,
|
|
9287
|
+
valueForMoney: 0,
|
|
9288
|
+
recommendationPercentage: 0
|
|
9289
|
+
},
|
|
9290
|
+
isActive: true
|
|
9291
|
+
};
|
|
9292
|
+
batch.set(procedureRef, {
|
|
9293
|
+
...newProcedure,
|
|
9294
|
+
createdAt: (0, import_firestore27.serverTimestamp)(),
|
|
9295
|
+
updatedAt: (0, import_firestore27.serverTimestamp)()
|
|
9296
|
+
});
|
|
9297
|
+
}
|
|
9298
|
+
await batch.commit();
|
|
9299
|
+
const fetchedProcedures = [];
|
|
9300
|
+
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
9301
|
+
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
9302
|
+
const q = (0, import_firestore27.query)(
|
|
9303
|
+
(0, import_firestore27.collection)(this.db, PROCEDURES_COLLECTION),
|
|
9304
|
+
(0, import_firestore27.where)((0, import_firestore27.documentId)(), "in", chunk)
|
|
9305
|
+
);
|
|
9306
|
+
const snapshot = await (0, import_firestore27.getDocs)(q);
|
|
9307
|
+
snapshot.forEach((doc36) => {
|
|
9308
|
+
fetchedProcedures.push(doc36.data());
|
|
9309
|
+
});
|
|
9310
|
+
}
|
|
9311
|
+
return fetchedProcedures;
|
|
9312
|
+
}
|
|
9173
9313
|
/**
|
|
9174
9314
|
* Gets a procedure by ID
|
|
9175
9315
|
* @param id - The ID of the procedure to get
|
package/dist/index.mjs
CHANGED
|
@@ -698,9 +698,9 @@ var userSchema = z4.object({
|
|
|
698
698
|
email: z4.string().email().nullable(),
|
|
699
699
|
roles: z4.array(userRoleSchema),
|
|
700
700
|
isAnonymous: z4.boolean(),
|
|
701
|
-
createdAt:
|
|
702
|
-
updatedAt:
|
|
703
|
-
lastLoginAt:
|
|
701
|
+
createdAt: z4.any(),
|
|
702
|
+
updatedAt: z4.any(),
|
|
703
|
+
lastLoginAt: z4.any(),
|
|
704
704
|
patientProfile: z4.string().optional(),
|
|
705
705
|
practitionerProfile: z4.string().optional(),
|
|
706
706
|
adminProfile: z4.string().optional()
|
|
@@ -5499,9 +5499,6 @@ var PractitionerService = class extends BaseService {
|
|
|
5499
5499
|
if (!practitioner) {
|
|
5500
5500
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
5501
5501
|
}
|
|
5502
|
-
if (!practitioner.isActive) {
|
|
5503
|
-
throw new Error(`Practitioner ${practitionerId} is not active`);
|
|
5504
|
-
}
|
|
5505
5502
|
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
5506
5503
|
if (!clinic) {
|
|
5507
5504
|
throw new Error(`Clinic ${clinicId} not found`);
|
|
@@ -5860,7 +5857,7 @@ var UserService = class extends BaseService {
|
|
|
5860
5857
|
const q = query8(collection8(this.db, USERS_COLLECTION), ...constraints);
|
|
5861
5858
|
const querySnapshot = await getDocs8(q);
|
|
5862
5859
|
const users = querySnapshot.docs.map((doc36) => doc36.data());
|
|
5863
|
-
return
|
|
5860
|
+
return users.map((userData) => userSchema.parse(userData));
|
|
5864
5861
|
}
|
|
5865
5862
|
/**
|
|
5866
5863
|
* Ažurira timestamp poslednjeg logovanja
|
|
@@ -8903,9 +8900,11 @@ import {
|
|
|
8903
8900
|
setDoc as setDoc14,
|
|
8904
8901
|
deleteDoc as deleteDoc10,
|
|
8905
8902
|
serverTimestamp as serverTimestamp13,
|
|
8903
|
+
writeBatch as writeBatch6,
|
|
8906
8904
|
orderBy as orderBy6,
|
|
8907
8905
|
limit as limit9,
|
|
8908
|
-
startAfter as startAfter8
|
|
8906
|
+
startAfter as startAfter8,
|
|
8907
|
+
documentId as documentId2
|
|
8909
8908
|
} from "firebase/firestore";
|
|
8910
8909
|
|
|
8911
8910
|
// src/types/procedure/index.ts
|
|
@@ -9156,6 +9155,149 @@ var ProcedureService = class extends BaseService {
|
|
|
9156
9155
|
const savedDoc = await getDoc19(procedureRef);
|
|
9157
9156
|
return savedDoc.data();
|
|
9158
9157
|
}
|
|
9158
|
+
/**
|
|
9159
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
9160
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
9161
|
+
*
|
|
9162
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
9163
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
9164
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
9165
|
+
*/
|
|
9166
|
+
async bulkCreateProcedures(baseData, practitionerIds) {
|
|
9167
|
+
var _a;
|
|
9168
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
9169
|
+
throw new Error("Practitioner IDs array cannot be empty.");
|
|
9170
|
+
}
|
|
9171
|
+
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
9172
|
+
const validatedData = createProcedureSchema.parse(validationData);
|
|
9173
|
+
const [category, subcategory, technology, product, clinicSnapshot] = await Promise.all([
|
|
9174
|
+
this.categoryService.getById(validatedData.categoryId),
|
|
9175
|
+
this.subcategoryService.getById(
|
|
9176
|
+
validatedData.categoryId,
|
|
9177
|
+
validatedData.subcategoryId
|
|
9178
|
+
),
|
|
9179
|
+
this.technologyService.getById(validatedData.technologyId),
|
|
9180
|
+
this.productService.getById(
|
|
9181
|
+
validatedData.technologyId,
|
|
9182
|
+
validatedData.productId
|
|
9183
|
+
),
|
|
9184
|
+
getDoc19(doc16(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId))
|
|
9185
|
+
]);
|
|
9186
|
+
if (!category || !subcategory || !technology || !product) {
|
|
9187
|
+
throw new Error("One or more required base entities not found");
|
|
9188
|
+
}
|
|
9189
|
+
if (!clinicSnapshot.exists()) {
|
|
9190
|
+
throw new Error(
|
|
9191
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
9192
|
+
);
|
|
9193
|
+
}
|
|
9194
|
+
const clinic = clinicSnapshot.data();
|
|
9195
|
+
let processedPhotos = [];
|
|
9196
|
+
if (validatedData.photos && validatedData.photos.length > 0) {
|
|
9197
|
+
const batchId = this.generateId();
|
|
9198
|
+
processedPhotos = await this.processMediaArray(
|
|
9199
|
+
validatedData.photos,
|
|
9200
|
+
batchId,
|
|
9201
|
+
"procedure-photos-batch"
|
|
9202
|
+
);
|
|
9203
|
+
}
|
|
9204
|
+
const practitionersMap = /* @__PURE__ */ new Map();
|
|
9205
|
+
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
9206
|
+
const chunk = practitionerIds.slice(i, i + 30);
|
|
9207
|
+
const practitionersQuery = query16(
|
|
9208
|
+
collection16(this.db, PRACTITIONERS_COLLECTION),
|
|
9209
|
+
where16(documentId2(), "in", chunk)
|
|
9210
|
+
);
|
|
9211
|
+
const practitionersSnapshot = await getDocs16(practitionersQuery);
|
|
9212
|
+
practitionersSnapshot.docs.forEach((doc36) => {
|
|
9213
|
+
practitionersMap.set(doc36.id, doc36.data());
|
|
9214
|
+
});
|
|
9215
|
+
}
|
|
9216
|
+
if (practitionersMap.size !== practitionerIds.length) {
|
|
9217
|
+
const foundIds = Array.from(practitionersMap.keys());
|
|
9218
|
+
const notFoundIds = practitionerIds.filter(
|
|
9219
|
+
(id) => !foundIds.includes(id)
|
|
9220
|
+
);
|
|
9221
|
+
throw new Error(
|
|
9222
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
9223
|
+
);
|
|
9224
|
+
}
|
|
9225
|
+
const batch = writeBatch6(this.db);
|
|
9226
|
+
const createdProcedureIds = [];
|
|
9227
|
+
const clinicInfo = {
|
|
9228
|
+
id: clinicSnapshot.id,
|
|
9229
|
+
name: clinic.name,
|
|
9230
|
+
description: clinic.description || "",
|
|
9231
|
+
featuredPhoto: clinic.featuredPhotos && clinic.featuredPhotos.length > 0 ? typeof clinic.featuredPhotos[0] === "string" ? clinic.featuredPhotos[0] : "" : typeof clinic.coverPhoto === "string" ? clinic.coverPhoto : "",
|
|
9232
|
+
location: clinic.location,
|
|
9233
|
+
contactInfo: clinic.contactInfo
|
|
9234
|
+
};
|
|
9235
|
+
for (const practitionerId of practitionerIds) {
|
|
9236
|
+
const practitioner = practitionersMap.get(practitionerId);
|
|
9237
|
+
const doctorInfo = {
|
|
9238
|
+
id: practitioner.id,
|
|
9239
|
+
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
9240
|
+
description: practitioner.basicInfo.bio || "",
|
|
9241
|
+
photo: typeof practitioner.basicInfo.profileImageUrl === "string" ? practitioner.basicInfo.profileImageUrl : "",
|
|
9242
|
+
rating: ((_a = practitioner.reviewInfo) == null ? void 0 : _a.averageRating) || 0,
|
|
9243
|
+
services: practitioner.procedures || []
|
|
9244
|
+
};
|
|
9245
|
+
const procedureId = this.generateId();
|
|
9246
|
+
createdProcedureIds.push(procedureId);
|
|
9247
|
+
const procedureRef = doc16(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
9248
|
+
const newProcedure = {
|
|
9249
|
+
id: procedureId,
|
|
9250
|
+
...validatedData,
|
|
9251
|
+
practitionerId,
|
|
9252
|
+
// Override practitionerId with the correct one
|
|
9253
|
+
photos: processedPhotos,
|
|
9254
|
+
category,
|
|
9255
|
+
subcategory,
|
|
9256
|
+
technology,
|
|
9257
|
+
product,
|
|
9258
|
+
blockingConditions: technology.blockingConditions,
|
|
9259
|
+
contraindications: technology.contraindications || [],
|
|
9260
|
+
treatmentBenefits: technology.benefits,
|
|
9261
|
+
preRequirements: technology.requirements.pre,
|
|
9262
|
+
postRequirements: technology.requirements.post,
|
|
9263
|
+
certificationRequirement: technology.certificationRequirement,
|
|
9264
|
+
documentationTemplates: (technology == null ? void 0 : technology.documentationTemplates) || [],
|
|
9265
|
+
clinicInfo,
|
|
9266
|
+
doctorInfo,
|
|
9267
|
+
// Set specific doctor info
|
|
9268
|
+
reviewInfo: {
|
|
9269
|
+
totalReviews: 0,
|
|
9270
|
+
averageRating: 0,
|
|
9271
|
+
effectivenessOfTreatment: 0,
|
|
9272
|
+
outcomeExplanation: 0,
|
|
9273
|
+
painManagement: 0,
|
|
9274
|
+
followUpCare: 0,
|
|
9275
|
+
valueForMoney: 0,
|
|
9276
|
+
recommendationPercentage: 0
|
|
9277
|
+
},
|
|
9278
|
+
isActive: true
|
|
9279
|
+
};
|
|
9280
|
+
batch.set(procedureRef, {
|
|
9281
|
+
...newProcedure,
|
|
9282
|
+
createdAt: serverTimestamp13(),
|
|
9283
|
+
updatedAt: serverTimestamp13()
|
|
9284
|
+
});
|
|
9285
|
+
}
|
|
9286
|
+
await batch.commit();
|
|
9287
|
+
const fetchedProcedures = [];
|
|
9288
|
+
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
9289
|
+
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
9290
|
+
const q = query16(
|
|
9291
|
+
collection16(this.db, PROCEDURES_COLLECTION),
|
|
9292
|
+
where16(documentId2(), "in", chunk)
|
|
9293
|
+
);
|
|
9294
|
+
const snapshot = await getDocs16(q);
|
|
9295
|
+
snapshot.forEach((doc36) => {
|
|
9296
|
+
fetchedProcedures.push(doc36.data());
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
return fetchedProcedures;
|
|
9300
|
+
}
|
|
9159
9301
|
/**
|
|
9160
9302
|
* Gets a procedure by ID
|
|
9161
9303
|
* @param id - The ID of the procedure to get
|
package/package.json
CHANGED
|
@@ -1228,9 +1228,10 @@ export class PractitionerService extends BaseService {
|
|
|
1228
1228
|
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
1229
1229
|
}
|
|
1230
1230
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1231
|
+
// No need to check for is practitioner active
|
|
1232
|
+
// if (!practitioner.isActive) {
|
|
1233
|
+
// throw new Error(`Practitioner ${practitionerId} is not active`);
|
|
1234
|
+
// }
|
|
1234
1235
|
|
|
1235
1236
|
// Validate that clinic exists
|
|
1236
1237
|
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
@@ -304,6 +304,187 @@ export class ProcedureService extends BaseService {
|
|
|
304
304
|
return savedDoc.data() as Procedure;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Creates multiple procedures for a list of practitioners based on common data.
|
|
309
|
+
* This method is optimized for bulk creation to reduce database reads and writes.
|
|
310
|
+
*
|
|
311
|
+
* @param baseData - The base data for the procedures to be created, omitting the practitionerId.
|
|
312
|
+
* @param practitionerIds - An array of practitioner IDs for whom the procedures will be created.
|
|
313
|
+
* @returns A promise that resolves to an array of the newly created procedures.
|
|
314
|
+
*/
|
|
315
|
+
async bulkCreateProcedures(
|
|
316
|
+
baseData: Omit<CreateProcedureData, "practitionerId">,
|
|
317
|
+
practitionerIds: string[]
|
|
318
|
+
): Promise<Procedure[]> {
|
|
319
|
+
// 1. Validation
|
|
320
|
+
if (!practitionerIds || practitionerIds.length === 0) {
|
|
321
|
+
throw new Error("Practitioner IDs array cannot be empty.");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Add a dummy practitionerId for the validation schema to pass
|
|
325
|
+
const validationData = { ...baseData, practitionerId: practitionerIds[0] };
|
|
326
|
+
const validatedData = createProcedureSchema.parse(validationData);
|
|
327
|
+
|
|
328
|
+
// 2. Fetch common data once to avoid redundant reads
|
|
329
|
+
const [category, subcategory, technology, product, clinicSnapshot] =
|
|
330
|
+
await Promise.all([
|
|
331
|
+
this.categoryService.getById(validatedData.categoryId),
|
|
332
|
+
this.subcategoryService.getById(
|
|
333
|
+
validatedData.categoryId,
|
|
334
|
+
validatedData.subcategoryId
|
|
335
|
+
),
|
|
336
|
+
this.technologyService.getById(validatedData.technologyId),
|
|
337
|
+
this.productService.getById(
|
|
338
|
+
validatedData.technologyId,
|
|
339
|
+
validatedData.productId
|
|
340
|
+
),
|
|
341
|
+
getDoc(doc(this.db, CLINICS_COLLECTION, validatedData.clinicBranchId)),
|
|
342
|
+
]);
|
|
343
|
+
|
|
344
|
+
if (!category || !subcategory || !technology || !product) {
|
|
345
|
+
throw new Error("One or more required base entities not found");
|
|
346
|
+
}
|
|
347
|
+
if (!clinicSnapshot.exists()) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`Clinic with ID ${validatedData.clinicBranchId} not found`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const clinic = clinicSnapshot.data() as Clinic;
|
|
353
|
+
|
|
354
|
+
// 3. Handle media uploads once for efficiency
|
|
355
|
+
let processedPhotos: string[] = [];
|
|
356
|
+
if (validatedData.photos && validatedData.photos.length > 0) {
|
|
357
|
+
const batchId = this.generateId(); // Use a single ID for all media in this batch
|
|
358
|
+
processedPhotos = await this.processMediaArray(
|
|
359
|
+
validatedData.photos,
|
|
360
|
+
batchId,
|
|
361
|
+
"procedure-photos-batch"
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 4. Fetch all practitioner data efficiently
|
|
366
|
+
const practitionersMap = new Map<string, Practitioner>();
|
|
367
|
+
// Use 'in' query in chunks of 30, as this is the Firestore limit
|
|
368
|
+
for (let i = 0; i < practitionerIds.length; i += 30) {
|
|
369
|
+
const chunk = practitionerIds.slice(i, i + 30);
|
|
370
|
+
const practitionersQuery = query(
|
|
371
|
+
collection(this.db, PRACTITIONERS_COLLECTION),
|
|
372
|
+
where(documentId(), "in", chunk)
|
|
373
|
+
);
|
|
374
|
+
const practitionersSnapshot = await getDocs(practitionersQuery);
|
|
375
|
+
practitionersSnapshot.docs.forEach((doc) => {
|
|
376
|
+
practitionersMap.set(doc.id, doc.data() as Practitioner);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Verify all practitioners were found
|
|
381
|
+
if (practitionersMap.size !== practitionerIds.length) {
|
|
382
|
+
const foundIds = Array.from(practitionersMap.keys());
|
|
383
|
+
const notFoundIds = practitionerIds.filter(
|
|
384
|
+
(id) => !foundIds.includes(id)
|
|
385
|
+
);
|
|
386
|
+
throw new Error(
|
|
387
|
+
`The following practitioners were not found: ${notFoundIds.join(", ")}`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 5. Use a Firestore batch for atomic creation
|
|
392
|
+
const batch = writeBatch(this.db);
|
|
393
|
+
const createdProcedureIds: string[] = [];
|
|
394
|
+
const clinicInfo = {
|
|
395
|
+
id: clinicSnapshot.id,
|
|
396
|
+
name: clinic.name,
|
|
397
|
+
description: clinic.description || "",
|
|
398
|
+
featuredPhoto:
|
|
399
|
+
clinic.featuredPhotos && clinic.featuredPhotos.length > 0
|
|
400
|
+
? typeof clinic.featuredPhotos[0] === "string"
|
|
401
|
+
? clinic.featuredPhotos[0]
|
|
402
|
+
: ""
|
|
403
|
+
: typeof clinic.coverPhoto === "string"
|
|
404
|
+
? clinic.coverPhoto
|
|
405
|
+
: "",
|
|
406
|
+
location: clinic.location,
|
|
407
|
+
contactInfo: clinic.contactInfo,
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
for (const practitionerId of practitionerIds) {
|
|
411
|
+
const practitioner = practitionersMap.get(practitionerId)!;
|
|
412
|
+
|
|
413
|
+
const doctorInfo = {
|
|
414
|
+
id: practitioner.id,
|
|
415
|
+
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
416
|
+
description: practitioner.basicInfo.bio || "",
|
|
417
|
+
photo:
|
|
418
|
+
typeof practitioner.basicInfo.profileImageUrl === "string"
|
|
419
|
+
? practitioner.basicInfo.profileImageUrl
|
|
420
|
+
: "",
|
|
421
|
+
rating: practitioner.reviewInfo?.averageRating || 0,
|
|
422
|
+
services: practitioner.procedures || [],
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const procedureId = this.generateId();
|
|
426
|
+
createdProcedureIds.push(procedureId);
|
|
427
|
+
const procedureRef = doc(this.db, PROCEDURES_COLLECTION, procedureId);
|
|
428
|
+
|
|
429
|
+
// Construct the new procedure, reusing common data
|
|
430
|
+
const newProcedure: Omit<Procedure, "createdAt" | "updatedAt"> = {
|
|
431
|
+
id: procedureId,
|
|
432
|
+
...validatedData,
|
|
433
|
+
practitionerId: practitionerId, // Override practitionerId with the correct one
|
|
434
|
+
photos: processedPhotos,
|
|
435
|
+
category,
|
|
436
|
+
subcategory,
|
|
437
|
+
technology,
|
|
438
|
+
product,
|
|
439
|
+
blockingConditions: technology.blockingConditions,
|
|
440
|
+
contraindications: technology.contraindications || [],
|
|
441
|
+
treatmentBenefits: technology.benefits,
|
|
442
|
+
preRequirements: technology.requirements.pre,
|
|
443
|
+
postRequirements: technology.requirements.post,
|
|
444
|
+
certificationRequirement: technology.certificationRequirement,
|
|
445
|
+
documentationTemplates: technology?.documentationTemplates || [],
|
|
446
|
+
clinicInfo,
|
|
447
|
+
doctorInfo, // Set specific doctor info
|
|
448
|
+
reviewInfo: {
|
|
449
|
+
totalReviews: 0,
|
|
450
|
+
averageRating: 0,
|
|
451
|
+
effectivenessOfTreatment: 0,
|
|
452
|
+
outcomeExplanation: 0,
|
|
453
|
+
painManagement: 0,
|
|
454
|
+
followUpCare: 0,
|
|
455
|
+
valueForMoney: 0,
|
|
456
|
+
recommendationPercentage: 0,
|
|
457
|
+
},
|
|
458
|
+
isActive: true,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
batch.set(procedureRef, {
|
|
462
|
+
...newProcedure,
|
|
463
|
+
createdAt: serverTimestamp(),
|
|
464
|
+
updatedAt: serverTimestamp(),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// 6. Commit the atomic batch write
|
|
469
|
+
await batch.commit();
|
|
470
|
+
|
|
471
|
+
// 7. Fetch and return the newly created procedures
|
|
472
|
+
const fetchedProcedures: Procedure[] = [];
|
|
473
|
+
for (let i = 0; i < createdProcedureIds.length; i += 30) {
|
|
474
|
+
const chunk = createdProcedureIds.slice(i, i + 30);
|
|
475
|
+
const q = query(
|
|
476
|
+
collection(this.db, PROCEDURES_COLLECTION),
|
|
477
|
+
where(documentId(), "in", chunk)
|
|
478
|
+
);
|
|
479
|
+
const snapshot = await getDocs(q);
|
|
480
|
+
snapshot.forEach((doc) => {
|
|
481
|
+
fetchedProcedures.push(doc.data() as Procedure);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return fetchedProcedures;
|
|
486
|
+
}
|
|
487
|
+
|
|
307
488
|
/**
|
|
308
489
|
* Gets a procedure by ID
|
|
309
490
|
* @param id - The ID of the procedure to get
|
|
@@ -264,7 +264,7 @@ export class UserService extends BaseService {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
const userData = userDoc.data();
|
|
267
|
-
return userSchema.parse(userData);
|
|
267
|
+
return userSchema.parse(userData) as User;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
/**
|
|
@@ -278,7 +278,7 @@ export class UserService extends BaseService {
|
|
|
278
278
|
if (querySnapshot.empty) return null;
|
|
279
279
|
|
|
280
280
|
const userData = querySnapshot.docs[0].data();
|
|
281
|
-
return userSchema.parse(userData);
|
|
281
|
+
return userSchema.parse(userData) as User;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
async getUsersByRole(role: UserRole): Promise<User[]> {
|
|
@@ -289,7 +289,7 @@ export class UserService extends BaseService {
|
|
|
289
289
|
const querySnapshot = await getDocs(q);
|
|
290
290
|
|
|
291
291
|
const users = querySnapshot.docs.map((doc) => doc.data());
|
|
292
|
-
return
|
|
292
|
+
return users.map((userData) => userSchema.parse(userData) as User);
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
/**
|
package/src/types/index.ts
CHANGED
|
@@ -14,9 +14,9 @@ export interface User {
|
|
|
14
14
|
email: string | null;
|
|
15
15
|
roles: UserRole[];
|
|
16
16
|
isAnonymous: boolean;
|
|
17
|
-
createdAt:
|
|
18
|
-
updatedAt:
|
|
19
|
-
lastLoginAt:
|
|
17
|
+
createdAt: any;
|
|
18
|
+
updatedAt: any;
|
|
19
|
+
lastLoginAt: any;
|
|
20
20
|
patientProfile?: string;
|
|
21
21
|
practitionerProfile?: string;
|
|
22
22
|
adminProfile?: string;
|
|
@@ -90,9 +90,9 @@ export const userSchema = z.object({
|
|
|
90
90
|
email: z.string().email().nullable(),
|
|
91
91
|
roles: z.array(userRoleSchema),
|
|
92
92
|
isAnonymous: z.boolean(),
|
|
93
|
-
createdAt:
|
|
94
|
-
updatedAt:
|
|
95
|
-
lastLoginAt:
|
|
93
|
+
createdAt: z.any(),
|
|
94
|
+
updatedAt: z.any(),
|
|
95
|
+
lastLoginAt: z.any(),
|
|
96
96
|
patientProfile: z.string().optional(),
|
|
97
97
|
practitionerProfile: z.string().optional(),
|
|
98
98
|
adminProfile: z.string().optional(),
|