@blackcode_sa/metaestetics-api 1.12.21 → 1.12.22

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.
@@ -8,23 +8,21 @@ import {
8
8
  setDoc,
9
9
  deleteDoc,
10
10
  serverTimestamp,
11
- } from "firebase/firestore";
12
- import { BaseService } from "../base.service";
11
+ } from 'firebase/firestore';
12
+ import { BaseService } from '../base.service';
13
13
  import {
14
14
  Review,
15
15
  ClinicReview,
16
16
  PractitionerReview,
17
17
  ProcedureReview,
18
18
  REVIEWS_COLLECTION,
19
- } from "../../types/reviews";
20
- import {
21
- createReviewSchema,
22
- reviewSchema,
23
- } from "../../validations/reviews.schema";
24
- import { z } from "zod";
25
- import { Auth } from "firebase/auth";
26
- import { Firestore } from "firebase/firestore";
27
- import { FirebaseApp } from "firebase/app";
19
+ } from '../../types/reviews';
20
+ import { createReviewSchema, reviewSchema } from '../../validations/reviews.schema';
21
+ import { z } from 'zod';
22
+ import { Auth } from 'firebase/auth';
23
+ import { Firestore } from 'firebase/firestore';
24
+ import { FirebaseApp } from 'firebase/app';
25
+ import { Appointment, APPOINTMENTS_COLLECTION } from '../../types/appointment';
28
26
 
29
27
  export class ReviewService extends BaseService {
30
28
  constructor(db: Firestore, auth: Auth, app: FirebaseApp) {
@@ -38,13 +36,20 @@ export class ReviewService extends BaseService {
38
36
  * @returns The created review
39
37
  */
40
38
  async createReview(
41
- data: Omit<
42
- Review,
43
- "id" | "createdAt" | "updatedAt" | "appointmentId" | "overallRating"
44
- >,
45
- appointmentId: string
39
+ data: Omit<Review, 'id' | 'createdAt' | 'updatedAt' | 'appointmentId' | 'overallRating'>,
40
+ appointmentId: string,
46
41
  ): Promise<Review> {
47
42
  try {
43
+ console.log('🔍 ReviewService.createReview - Input data:', {
44
+ appointmentId,
45
+ hasClinicReview: !!data.clinicReview,
46
+ hasPractitionerReview: !!data.practitionerReview,
47
+ hasProcedureReview: !!data.procedureReview,
48
+ practitionerId: data.practitionerReview?.practitionerId,
49
+ clinicId: data.clinicReview?.clinicId,
50
+ procedureId: data.procedureReview?.procedureId,
51
+ });
52
+
48
53
  // Validate input data
49
54
  const validatedData = createReviewSchema.parse(data);
50
55
 
@@ -137,6 +142,13 @@ export class ReviewService extends BaseService {
137
142
  updatedAt: serverTimestamp(),
138
143
  });
139
144
 
145
+ console.log('✅ ReviewService.createReview - Review saved to Firestore:', {
146
+ reviewId,
147
+ practitionerId: review.practitionerReview?.practitionerId,
148
+ clinicId: review.clinicReview?.clinicId,
149
+ procedureId: review.procedureReview?.procedureId,
150
+ });
151
+
140
152
  // Note: Related entity updates (clinic, practitioner, procedure) are now handled
141
153
  // by cloud functions through the ReviewsAggregationService
142
154
 
@@ -150,75 +162,423 @@ export class ReviewService extends BaseService {
150
162
  }
151
163
 
152
164
  /**
153
- * Gets a review by ID
165
+ * Gets a review by ID with enhanced entity names
154
166
  * @param reviewId The ID of the review to get
155
- * @returns The review if found, null otherwise
167
+ * @returns The review with entity names if found, null otherwise
156
168
  */
157
169
  async getReview(reviewId: string): Promise<Review | null> {
170
+ console.log('🔍 ReviewService.getReview - Getting review:', reviewId);
171
+
158
172
  const docRef = doc(this.db, REVIEWS_COLLECTION, reviewId);
159
173
  const docSnap = await getDoc(docRef);
160
174
 
161
175
  if (!docSnap.exists()) {
176
+ console.log('❌ ReviewService.getReview - Review not found:', reviewId);
162
177
  return null;
163
178
  }
164
179
 
165
- return docSnap.data() as Review;
180
+ const review = { ...docSnap.data(), id: reviewId } as Review;
181
+
182
+ try {
183
+ // Fetch the associated appointment to enhance with entity names
184
+ const appointmentDoc = await getDoc(
185
+ doc(this.db, APPOINTMENTS_COLLECTION, review.appointmentId),
186
+ );
187
+
188
+ if (appointmentDoc.exists()) {
189
+ const appointment = appointmentDoc.data() as Appointment;
190
+
191
+ // Create enhanced review with entity names
192
+ const enhancedReview = { ...review };
193
+
194
+ if (enhancedReview.clinicReview && appointment.clinicInfo) {
195
+ enhancedReview.clinicReview = {
196
+ ...enhancedReview.clinicReview,
197
+ clinicName: appointment.clinicInfo.name,
198
+ };
199
+ }
200
+
201
+ if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
202
+ enhancedReview.practitionerReview = {
203
+ ...enhancedReview.practitionerReview,
204
+ practitionerName: appointment.practitionerInfo.name,
205
+ };
206
+ }
207
+
208
+ if (enhancedReview.procedureReview && appointment.procedureInfo) {
209
+ enhancedReview.procedureReview = {
210
+ ...enhancedReview.procedureReview,
211
+ procedureName: appointment.procedureInfo.name,
212
+ };
213
+ }
214
+
215
+ // Add patient name to the main review object
216
+ if (appointment.patientInfo) {
217
+ enhancedReview.patientName = appointment.patientInfo.fullName;
218
+ }
219
+
220
+ console.log('✅ ReviewService.getReview - Enhanced review:', {
221
+ reviewId,
222
+ hasEntityNames: !!(
223
+ enhancedReview.clinicReview?.clinicName ||
224
+ enhancedReview.practitionerReview?.practitionerName ||
225
+ enhancedReview.procedureReview?.procedureName ||
226
+ enhancedReview.patientName
227
+ ),
228
+ });
229
+
230
+ return enhancedReview;
231
+ }
232
+
233
+ console.log('⚠️ ReviewService.getReview - Appointment not found for review:', reviewId);
234
+ return review;
235
+ } catch (error) {
236
+ console.warn(`Failed to enhance review ${reviewId} with entity names:`, error);
237
+ return review;
238
+ }
166
239
  }
167
240
 
168
241
  /**
169
- * Gets all reviews for a specific patient
242
+ * Gets all reviews for a specific patient with enhanced entity names
170
243
  * @param patientId The ID of the patient
171
- * @returns Array of reviews for the patient
244
+ * @returns Array of reviews for the patient with clinic, practitioner, and procedure names
172
245
  */
173
246
  async getReviewsByPatient(patientId: string): Promise<Review[]> {
174
- const q = query(
175
- collection(this.db, REVIEWS_COLLECTION),
176
- where("patientId", "==", patientId)
177
- );
247
+ const q = query(collection(this.db, REVIEWS_COLLECTION), where('patientId', '==', patientId));
178
248
  const snapshot = await getDocs(q);
179
- return snapshot.docs.map((doc) => doc.data() as Review);
249
+ const reviews = snapshot.docs.map(doc => doc.data() as Review);
250
+
251
+ // Enhance reviews with entity names from appointments
252
+ const enhancedReviews = await Promise.all(
253
+ reviews.map(async review => {
254
+ try {
255
+ // Fetch the associated appointment
256
+ const appointmentDoc = await getDoc(
257
+ doc(this.db, APPOINTMENTS_COLLECTION, review.appointmentId),
258
+ );
259
+
260
+ if (appointmentDoc.exists()) {
261
+ const appointment = appointmentDoc.data() as Appointment;
262
+
263
+ // Create enhanced review with entity names
264
+ const enhancedReview = { ...review };
265
+
266
+ if (enhancedReview.clinicReview && appointment.clinicInfo) {
267
+ enhancedReview.clinicReview = {
268
+ ...enhancedReview.clinicReview,
269
+ clinicName: appointment.clinicInfo.name,
270
+ };
271
+ }
272
+
273
+ if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
274
+ enhancedReview.practitionerReview = {
275
+ ...enhancedReview.practitionerReview,
276
+ practitionerName: appointment.practitionerInfo.name,
277
+ };
278
+ }
279
+
280
+ if (enhancedReview.procedureReview && appointment.procedureInfo) {
281
+ enhancedReview.procedureReview = {
282
+ ...enhancedReview.procedureReview,
283
+ procedureName: appointment.procedureInfo.name,
284
+ };
285
+ }
286
+
287
+ // Add patient name to the main review object
288
+ if (appointment.patientInfo) {
289
+ enhancedReview.patientName = appointment.patientInfo.fullName;
290
+ }
291
+
292
+ return enhancedReview;
293
+ }
294
+
295
+ return review;
296
+ } catch (error) {
297
+ console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
298
+ return review;
299
+ }
300
+ }),
301
+ );
302
+
303
+ return enhancedReviews;
180
304
  }
181
305
 
182
306
  /**
183
- * Gets all reviews for a specific clinic
307
+ * Gets all reviews for a specific clinic with enhanced entity names
184
308
  * @param clinicId The ID of the clinic
185
- * @returns Array of reviews containing clinic reviews
309
+ * @returns Array of reviews containing clinic reviews with clinic, practitioner, and procedure names
186
310
  */
187
311
  async getReviewsByClinic(clinicId: string): Promise<Review[]> {
312
+ console.log('🔍 ReviewService.getReviewsByClinic - Querying for clinic:', clinicId);
313
+
188
314
  const q = query(
189
315
  collection(this.db, REVIEWS_COLLECTION),
190
- where("clinicReview.clinicId", "==", clinicId)
316
+ where('clinicReview.clinicId', '==', clinicId),
191
317
  );
192
318
  const snapshot = await getDocs(q);
193
- return snapshot.docs.map((doc) => doc.data() as Review);
319
+ const reviews = snapshot.docs.map(doc => {
320
+ const data = doc.data() as Review;
321
+ return { ...data, id: doc.id };
322
+ });
323
+
324
+ console.log('🔍 ReviewService.getReviewsByClinic - Found reviews before enhancement:', {
325
+ clinicId,
326
+ reviewCount: reviews.length,
327
+ reviewIds: reviews.map(r => r.id),
328
+ });
329
+
330
+ // Enhance reviews with entity names from appointments
331
+ const enhancedReviews = await Promise.all(
332
+ reviews.map(async review => {
333
+ try {
334
+ // Fetch the associated appointment
335
+ const appointmentDoc = await getDoc(
336
+ doc(this.db, APPOINTMENTS_COLLECTION, review.appointmentId),
337
+ );
338
+
339
+ if (appointmentDoc.exists()) {
340
+ const appointment = appointmentDoc.data() as Appointment;
341
+
342
+ // Create enhanced review with entity names
343
+ const enhancedReview = { ...review };
344
+
345
+ if (enhancedReview.clinicReview && appointment.clinicInfo) {
346
+ enhancedReview.clinicReview = {
347
+ ...enhancedReview.clinicReview,
348
+ clinicName: appointment.clinicInfo.name,
349
+ };
350
+ }
351
+
352
+ if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
353
+ enhancedReview.practitionerReview = {
354
+ ...enhancedReview.practitionerReview,
355
+ practitionerName: appointment.practitionerInfo.name,
356
+ };
357
+ }
358
+
359
+ if (enhancedReview.procedureReview && appointment.procedureInfo) {
360
+ enhancedReview.procedureReview = {
361
+ ...enhancedReview.procedureReview,
362
+ procedureName: appointment.procedureInfo.name,
363
+ };
364
+ }
365
+
366
+ // Add patient name to the main review object
367
+ if (appointment.patientInfo) {
368
+ enhancedReview.patientName = appointment.patientInfo.fullName;
369
+ }
370
+
371
+ return enhancedReview;
372
+ }
373
+
374
+ return review;
375
+ } catch (error) {
376
+ console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
377
+ return review;
378
+ }
379
+ }),
380
+ );
381
+
382
+ console.log('✅ ReviewService.getReviewsByClinic - Enhanced reviews:', {
383
+ clinicId,
384
+ reviewCount: enhancedReviews.length,
385
+ reviewIds: enhancedReviews.map(r => r.id),
386
+ hasEntityNames: enhancedReviews.some(
387
+ r =>
388
+ r.clinicReview?.clinicName ||
389
+ r.practitionerReview?.practitionerName ||
390
+ r.procedureReview?.procedureName ||
391
+ r.patientName,
392
+ ),
393
+ });
394
+
395
+ return enhancedReviews;
194
396
  }
195
397
 
196
398
  /**
197
- * Gets all reviews for a specific practitioner
399
+ * Gets all reviews for a specific practitioner with enhanced entity names
198
400
  * @param practitionerId The ID of the practitioner
199
- * @returns Array of reviews containing practitioner reviews
401
+ * @returns Array of reviews containing practitioner reviews with clinic, practitioner, and procedure names
200
402
  */
201
403
  async getReviewsByPractitioner(practitionerId: string): Promise<Review[]> {
404
+ console.log(
405
+ '🔍 ReviewService.getReviewsByPractitioner - Querying for practitioner:',
406
+ practitionerId,
407
+ );
408
+
202
409
  const q = query(
203
410
  collection(this.db, REVIEWS_COLLECTION),
204
- where("practitionerReview.practitionerId", "==", practitionerId)
411
+ where('practitionerReview.practitionerId', '==', practitionerId),
205
412
  );
206
413
  const snapshot = await getDocs(q);
207
- return snapshot.docs.map((doc) => doc.data() as Review);
414
+ const reviews = snapshot.docs.map(doc => {
415
+ const data = doc.data() as Review;
416
+ return { ...data, id: doc.id };
417
+ });
418
+
419
+ console.log('🔍 ReviewService.getReviewsByPractitioner - Found reviews before enhancement:', {
420
+ practitionerId,
421
+ reviewCount: reviews.length,
422
+ reviewIds: reviews.map(r => r.id),
423
+ });
424
+
425
+ // Enhance reviews with entity names from appointments
426
+ const enhancedReviews = await Promise.all(
427
+ reviews.map(async review => {
428
+ try {
429
+ // Fetch the associated appointment
430
+ const appointmentDoc = await getDoc(
431
+ doc(this.db, APPOINTMENTS_COLLECTION, review.appointmentId),
432
+ );
433
+
434
+ if (appointmentDoc.exists()) {
435
+ const appointment = appointmentDoc.data() as Appointment;
436
+
437
+ // Create enhanced review with entity names
438
+ const enhancedReview = { ...review };
439
+
440
+ if (enhancedReview.clinicReview && appointment.clinicInfo) {
441
+ enhancedReview.clinicReview = {
442
+ ...enhancedReview.clinicReview,
443
+ clinicName: appointment.clinicInfo.name,
444
+ };
445
+ }
446
+
447
+ if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
448
+ enhancedReview.practitionerReview = {
449
+ ...enhancedReview.practitionerReview,
450
+ practitionerName: appointment.practitionerInfo.name,
451
+ };
452
+ }
453
+
454
+ if (enhancedReview.procedureReview && appointment.procedureInfo) {
455
+ enhancedReview.procedureReview = {
456
+ ...enhancedReview.procedureReview,
457
+ procedureName: appointment.procedureInfo.name,
458
+ };
459
+ }
460
+
461
+ // Add patient name to the main review object
462
+ if (appointment.patientInfo) {
463
+ enhancedReview.patientName = appointment.patientInfo.fullName;
464
+ }
465
+
466
+ return enhancedReview;
467
+ }
468
+
469
+ return review;
470
+ } catch (error) {
471
+ console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
472
+ return review;
473
+ }
474
+ }),
475
+ );
476
+
477
+ console.log('✅ ReviewService.getReviewsByPractitioner - Enhanced reviews:', {
478
+ practitionerId,
479
+ reviewCount: enhancedReviews.length,
480
+ reviewIds: enhancedReviews.map(r => r.id),
481
+ hasEntityNames: enhancedReviews.some(
482
+ r =>
483
+ r.clinicReview?.clinicName ||
484
+ r.practitionerReview?.practitionerName ||
485
+ r.procedureReview?.procedureName,
486
+ ),
487
+ });
488
+
489
+ return enhancedReviews;
208
490
  }
209
491
 
210
492
  /**
211
- * Gets all reviews for a specific procedure
493
+ * Gets all reviews for a specific procedure with enhanced entity names
212
494
  * @param procedureId The ID of the procedure
213
- * @returns Array of reviews containing procedure reviews
495
+ * @returns Array of reviews containing procedure reviews with clinic, practitioner, and procedure names
214
496
  */
215
497
  async getReviewsByProcedure(procedureId: string): Promise<Review[]> {
498
+ console.log('🔍 ReviewService.getReviewsByProcedure - Querying for procedure:', procedureId);
499
+
216
500
  const q = query(
217
501
  collection(this.db, REVIEWS_COLLECTION),
218
- where("procedureReview.procedureId", "==", procedureId)
502
+ where('procedureReview.procedureId', '==', procedureId),
219
503
  );
220
504
  const snapshot = await getDocs(q);
221
- return snapshot.docs.map((doc) => doc.data() as Review);
505
+ const reviews = snapshot.docs.map(doc => {
506
+ const data = doc.data() as Review;
507
+ return { ...data, id: doc.id };
508
+ });
509
+
510
+ console.log('🔍 ReviewService.getReviewsByProcedure - Found reviews before enhancement:', {
511
+ procedureId,
512
+ reviewCount: reviews.length,
513
+ reviewIds: reviews.map(r => r.id),
514
+ });
515
+
516
+ // Enhance reviews with entity names from appointments
517
+ const enhancedReviews = await Promise.all(
518
+ reviews.map(async review => {
519
+ try {
520
+ // Fetch the associated appointment
521
+ const appointmentDoc = await getDoc(
522
+ doc(this.db, APPOINTMENTS_COLLECTION, review.appointmentId),
523
+ );
524
+
525
+ if (appointmentDoc.exists()) {
526
+ const appointment = appointmentDoc.data() as Appointment;
527
+
528
+ // Create enhanced review with entity names
529
+ const enhancedReview = { ...review };
530
+
531
+ if (enhancedReview.clinicReview && appointment.clinicInfo) {
532
+ enhancedReview.clinicReview = {
533
+ ...enhancedReview.clinicReview,
534
+ clinicName: appointment.clinicInfo.name,
535
+ };
536
+ }
537
+
538
+ if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
539
+ enhancedReview.practitionerReview = {
540
+ ...enhancedReview.practitionerReview,
541
+ practitionerName: appointment.practitionerInfo.name,
542
+ };
543
+ }
544
+
545
+ if (enhancedReview.procedureReview && appointment.procedureInfo) {
546
+ enhancedReview.procedureReview = {
547
+ ...enhancedReview.procedureReview,
548
+ procedureName: appointment.procedureInfo.name,
549
+ };
550
+ }
551
+
552
+ // Add patient name to the main review object
553
+ if (appointment.patientInfo) {
554
+ enhancedReview.patientName = appointment.patientInfo.fullName;
555
+ }
556
+
557
+ return enhancedReview;
558
+ }
559
+
560
+ return review;
561
+ } catch (error) {
562
+ console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
563
+ return review;
564
+ }
565
+ }),
566
+ );
567
+
568
+ console.log('✅ ReviewService.getReviewsByProcedure - Enhanced reviews:', {
569
+ procedureId,
570
+ reviewCount: enhancedReviews.length,
571
+ reviewIds: enhancedReviews.map(r => r.id),
572
+ hasEntityNames: enhancedReviews.some(
573
+ r =>
574
+ r.clinicReview?.clinicName ||
575
+ r.practitionerReview?.practitionerName ||
576
+ r.procedureReview?.procedureName ||
577
+ r.patientName,
578
+ ),
579
+ });
580
+
581
+ return enhancedReviews;
222
582
  }
223
583
 
224
584
  /**
@@ -229,7 +589,7 @@ export class ReviewService extends BaseService {
229
589
  async getReviewByAppointment(appointmentId: string): Promise<Review | null> {
230
590
  const q = query(
231
591
  collection(this.db, REVIEWS_COLLECTION),
232
- where("appointmentId", "==", appointmentId)
592
+ where('appointmentId', '==', appointmentId),
233
593
  );
234
594
  const snapshot = await getDocs(q);
235
595
 
@@ -22,6 +22,7 @@ interface BaseReview {
22
22
  */
23
23
  export interface ClinicReview extends BaseReview {
24
24
  clinicId: string;
25
+ clinicName?: string; // Enhanced field: clinic name from appointment
25
26
  cleanliness: number; // 1-5 stars
26
27
  facilities: number; // 1-5 stars
27
28
  staffFriendliness: number; // 1-5 stars
@@ -37,6 +38,7 @@ export interface ClinicReview extends BaseReview {
37
38
  */
38
39
  export interface PractitionerReview extends BaseReview {
39
40
  practitionerId: string;
41
+ practitionerName?: string; // Enhanced field: practitioner name from appointment
40
42
  knowledgeAndExpertise: number; // 1-5 stars
41
43
  communicationSkills: number; // 1-5 stars
42
44
  bedSideManner: number; // 1-5 stars
@@ -52,6 +54,7 @@ export interface PractitionerReview extends BaseReview {
52
54
  */
53
55
  export interface ProcedureReview extends BaseReview {
54
56
  procedureId: string;
57
+ procedureName?: string; // Enhanced field: procedure name from appointment
55
58
  effectivenessOfTreatment: number; // 1-5 stars
56
59
  outcomeExplanation: number; // 1-5 stars
57
60
  painManagement: number; // 1-5 stars
@@ -114,6 +117,7 @@ export interface Review {
114
117
  id: string;
115
118
  appointmentId: string;
116
119
  patientId: string;
120
+ patientName?: string; // Enhanced field: patient name from appointment
117
121
  createdAt: Date;
118
122
  updatedAt: Date;
119
123
  clinicReview?: ClinicReview;
@@ -123,4 +127,4 @@ export interface Review {
123
127
  overallRating: number; // Average of all available ratings
124
128
  }
125
129
 
126
- export const REVIEWS_COLLECTION = "reviews";
130
+ export const REVIEWS_COLLECTION = 'reviews';