@blackcode_sa/metaestetics-api 1.7.44 → 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 CHANGED
@@ -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
package/dist/index.d.ts CHANGED
@@ -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
package/dist/index.js CHANGED
@@ -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`);
@@ -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
@@ -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`);
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.7.44",
4
+ "version": "1.7.45",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -1228,9 +1228,10 @@ export class PractitionerService extends BaseService {
1228
1228
  throw new Error(`Practitioner ${practitionerId} not found`);
1229
1229
  }
1230
1230
 
1231
- if (!practitioner.isActive) {
1232
- throw new Error(`Practitioner ${practitionerId} is not active`);
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