@blackcode_sa/metaestetics-api 1.12.17 → 1.12.20
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/admin/index.d.mts +4 -1
- package/dist/admin/index.d.ts +4 -1
- package/dist/admin/index.js +27 -10
- package/dist/admin/index.mjs +27 -10
- package/dist/backoffice/index.js +2 -2
- package/dist/backoffice/index.mjs +2 -2
- package/dist/index.d.mts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +47 -10
- package/dist/index.mjs +47 -10
- package/package.json +1 -1
- package/src/admin/free-consultation/README.md +82 -0
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +70 -54
- package/src/backoffice/services/technology.service.ts +2 -2
- package/src/services/reviews/reviews.service.ts +71 -30
- package/src/services/user/user.service.ts +8 -1
- package/src/types/reviews/index.ts +3 -0
package/dist/admin/index.d.mts
CHANGED
|
@@ -43,6 +43,7 @@ interface BaseReview {
|
|
|
43
43
|
*/
|
|
44
44
|
interface ClinicReview extends BaseReview {
|
|
45
45
|
clinicId: string;
|
|
46
|
+
clinicName?: string;
|
|
46
47
|
cleanliness: number;
|
|
47
48
|
facilities: number;
|
|
48
49
|
staffFriendliness: number;
|
|
@@ -57,6 +58,7 @@ interface ClinicReview extends BaseReview {
|
|
|
57
58
|
*/
|
|
58
59
|
interface PractitionerReview extends BaseReview {
|
|
59
60
|
practitionerId: string;
|
|
61
|
+
practitionerName?: string;
|
|
60
62
|
knowledgeAndExpertise: number;
|
|
61
63
|
communicationSkills: number;
|
|
62
64
|
bedSideManner: number;
|
|
@@ -71,6 +73,7 @@ interface PractitionerReview extends BaseReview {
|
|
|
71
73
|
*/
|
|
72
74
|
interface ProcedureReview extends BaseReview {
|
|
73
75
|
procedureId: string;
|
|
76
|
+
procedureName?: string;
|
|
74
77
|
effectivenessOfTreatment: number;
|
|
75
78
|
outcomeExplanation: number;
|
|
76
79
|
painManagement: number;
|
|
@@ -3388,7 +3391,7 @@ declare class DocumentManagerAdminService {
|
|
|
3388
3391
|
|
|
3389
3392
|
/**
|
|
3390
3393
|
* Ensures that the free consultation infrastructure exists in the database
|
|
3391
|
-
* Creates category, subcategory, and
|
|
3394
|
+
* Creates category, subcategory, technology, and product if they don't exist
|
|
3392
3395
|
* @param db - Firestore database instance (optional, defaults to admin.firestore())
|
|
3393
3396
|
* @returns Promise<boolean> - Always returns true after ensuring infrastructure exists
|
|
3394
3397
|
*/
|
package/dist/admin/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ interface BaseReview {
|
|
|
43
43
|
*/
|
|
44
44
|
interface ClinicReview extends BaseReview {
|
|
45
45
|
clinicId: string;
|
|
46
|
+
clinicName?: string;
|
|
46
47
|
cleanliness: number;
|
|
47
48
|
facilities: number;
|
|
48
49
|
staffFriendliness: number;
|
|
@@ -57,6 +58,7 @@ interface ClinicReview extends BaseReview {
|
|
|
57
58
|
*/
|
|
58
59
|
interface PractitionerReview extends BaseReview {
|
|
59
60
|
practitionerId: string;
|
|
61
|
+
practitionerName?: string;
|
|
60
62
|
knowledgeAndExpertise: number;
|
|
61
63
|
communicationSkills: number;
|
|
62
64
|
bedSideManner: number;
|
|
@@ -71,6 +73,7 @@ interface PractitionerReview extends BaseReview {
|
|
|
71
73
|
*/
|
|
72
74
|
interface ProcedureReview extends BaseReview {
|
|
73
75
|
procedureId: string;
|
|
76
|
+
procedureName?: string;
|
|
74
77
|
effectivenessOfTreatment: number;
|
|
75
78
|
outcomeExplanation: number;
|
|
76
79
|
painManagement: number;
|
|
@@ -3388,7 +3391,7 @@ declare class DocumentManagerAdminService {
|
|
|
3388
3391
|
|
|
3389
3392
|
/**
|
|
3390
3393
|
* Ensures that the free consultation infrastructure exists in the database
|
|
3391
|
-
* Creates category, subcategory, and
|
|
3394
|
+
* Creates category, subcategory, technology, and product if they don't exist
|
|
3392
3395
|
* @param db - Firestore database instance (optional, defaults to admin.firestore())
|
|
3393
3396
|
* @returns Promise<boolean> - Always returns true after ensuring infrastructure exists
|
|
3394
3397
|
*/
|
package/dist/admin/index.js
CHANGED
|
@@ -7383,13 +7383,14 @@ var SUBCATEGORIES_COLLECTION = "subcategories";
|
|
|
7383
7383
|
// src/backoffice/types/technology.types.ts
|
|
7384
7384
|
var TECHNOLOGIES_COLLECTION = "technologies";
|
|
7385
7385
|
|
|
7386
|
+
// src/backoffice/types/product.types.ts
|
|
7387
|
+
var PRODUCTS_COLLECTION = "products";
|
|
7388
|
+
|
|
7386
7389
|
// src/admin/free-consultation/free-consultation-utils.admin.ts
|
|
7387
7390
|
async function freeConsultationInfrastructure(db) {
|
|
7388
7391
|
const firestore18 = db || admin16.firestore();
|
|
7389
7392
|
try {
|
|
7390
|
-
console.log(
|
|
7391
|
-
"[freeConsultationInfrastructure] Checking free consultation infrastructure..."
|
|
7392
|
-
);
|
|
7393
|
+
console.log("[freeConsultationInfrastructure] Checking free consultation infrastructure...");
|
|
7393
7394
|
const technologyRef = firestore18.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
|
|
7394
7395
|
const technologyDoc = await technologyRef.get();
|
|
7395
7396
|
if (technologyDoc.exists) {
|
|
@@ -7398,19 +7399,14 @@ async function freeConsultationInfrastructure(db) {
|
|
|
7398
7399
|
);
|
|
7399
7400
|
return true;
|
|
7400
7401
|
}
|
|
7401
|
-
console.log(
|
|
7402
|
-
"[freeConsultationInfrastructure] Creating free consultation infrastructure..."
|
|
7403
|
-
);
|
|
7402
|
+
console.log("[freeConsultationInfrastructure] Creating free consultation infrastructure...");
|
|
7404
7403
|
await createFreeConsultationInfrastructure(firestore18);
|
|
7405
7404
|
console.log(
|
|
7406
7405
|
"[freeConsultationInfrastructure] Successfully created free consultation infrastructure"
|
|
7407
7406
|
);
|
|
7408
7407
|
return true;
|
|
7409
7408
|
} catch (error) {
|
|
7410
|
-
console.error(
|
|
7411
|
-
"[freeConsultationInfrastructure] Error ensuring infrastructure:",
|
|
7412
|
-
error
|
|
7413
|
-
);
|
|
7409
|
+
console.error("[freeConsultationInfrastructure] Error ensuring infrastructure:", error);
|
|
7414
7410
|
throw error;
|
|
7415
7411
|
}
|
|
7416
7412
|
}
|
|
@@ -7474,11 +7470,32 @@ async function createFreeConsultationInfrastructure(db) {
|
|
|
7474
7470
|
updatedAt: now
|
|
7475
7471
|
};
|
|
7476
7472
|
batch.set(technologyRef, technologyData);
|
|
7473
|
+
const productRef = db.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech").collection(PRODUCTS_COLLECTION).doc("free-consultation-product");
|
|
7474
|
+
const productData = {
|
|
7475
|
+
id: "free-consultation-product",
|
|
7476
|
+
name: "Free Consultation Service",
|
|
7477
|
+
description: "No physical product required for consultation services",
|
|
7478
|
+
brandId: "consultation-brand",
|
|
7479
|
+
brandName: "Consultation Services",
|
|
7480
|
+
technologyId: "free-consultation-tech",
|
|
7481
|
+
technologyName: "Free Consultation Technology",
|
|
7482
|
+
categoryId: "consultation",
|
|
7483
|
+
subcategoryId: "free-consultation",
|
|
7484
|
+
isActive: true,
|
|
7485
|
+
createdAt: now,
|
|
7486
|
+
updatedAt: now,
|
|
7487
|
+
technicalDetails: "Virtual consultation service - no physical product required",
|
|
7488
|
+
warnings: [],
|
|
7489
|
+
indications: ["Initial patient assessment", "Treatment planning", "Patient education"],
|
|
7490
|
+
contraindications: []
|
|
7491
|
+
};
|
|
7492
|
+
batch.set(productRef, productData);
|
|
7477
7493
|
await batch.commit();
|
|
7478
7494
|
console.log("[freeConsultationInfrastructure] Successfully created:");
|
|
7479
7495
|
console.log(" \u2713 Category: consultation");
|
|
7480
7496
|
console.log(" \u2713 Subcategory: free-consultation");
|
|
7481
7497
|
console.log(" \u2713 Technology: free-consultation-tech");
|
|
7498
|
+
console.log(" \u2713 Product: free-consultation-product");
|
|
7482
7499
|
}
|
|
7483
7500
|
|
|
7484
7501
|
// src/admin/mailing/practitionerInvite/templates/invitation.template.ts
|
package/dist/admin/index.mjs
CHANGED
|
@@ -7321,13 +7321,14 @@ var SUBCATEGORIES_COLLECTION = "subcategories";
|
|
|
7321
7321
|
// src/backoffice/types/technology.types.ts
|
|
7322
7322
|
var TECHNOLOGIES_COLLECTION = "technologies";
|
|
7323
7323
|
|
|
7324
|
+
// src/backoffice/types/product.types.ts
|
|
7325
|
+
var PRODUCTS_COLLECTION = "products";
|
|
7326
|
+
|
|
7324
7327
|
// src/admin/free-consultation/free-consultation-utils.admin.ts
|
|
7325
7328
|
async function freeConsultationInfrastructure(db) {
|
|
7326
7329
|
const firestore18 = db || admin16.firestore();
|
|
7327
7330
|
try {
|
|
7328
|
-
console.log(
|
|
7329
|
-
"[freeConsultationInfrastructure] Checking free consultation infrastructure..."
|
|
7330
|
-
);
|
|
7331
|
+
console.log("[freeConsultationInfrastructure] Checking free consultation infrastructure...");
|
|
7331
7332
|
const technologyRef = firestore18.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech");
|
|
7332
7333
|
const technologyDoc = await technologyRef.get();
|
|
7333
7334
|
if (technologyDoc.exists) {
|
|
@@ -7336,19 +7337,14 @@ async function freeConsultationInfrastructure(db) {
|
|
|
7336
7337
|
);
|
|
7337
7338
|
return true;
|
|
7338
7339
|
}
|
|
7339
|
-
console.log(
|
|
7340
|
-
"[freeConsultationInfrastructure] Creating free consultation infrastructure..."
|
|
7341
|
-
);
|
|
7340
|
+
console.log("[freeConsultationInfrastructure] Creating free consultation infrastructure...");
|
|
7342
7341
|
await createFreeConsultationInfrastructure(firestore18);
|
|
7343
7342
|
console.log(
|
|
7344
7343
|
"[freeConsultationInfrastructure] Successfully created free consultation infrastructure"
|
|
7345
7344
|
);
|
|
7346
7345
|
return true;
|
|
7347
7346
|
} catch (error) {
|
|
7348
|
-
console.error(
|
|
7349
|
-
"[freeConsultationInfrastructure] Error ensuring infrastructure:",
|
|
7350
|
-
error
|
|
7351
|
-
);
|
|
7347
|
+
console.error("[freeConsultationInfrastructure] Error ensuring infrastructure:", error);
|
|
7352
7348
|
throw error;
|
|
7353
7349
|
}
|
|
7354
7350
|
}
|
|
@@ -7412,11 +7408,32 @@ async function createFreeConsultationInfrastructure(db) {
|
|
|
7412
7408
|
updatedAt: now
|
|
7413
7409
|
};
|
|
7414
7410
|
batch.set(technologyRef, technologyData);
|
|
7411
|
+
const productRef = db.collection(TECHNOLOGIES_COLLECTION).doc("free-consultation-tech").collection(PRODUCTS_COLLECTION).doc("free-consultation-product");
|
|
7412
|
+
const productData = {
|
|
7413
|
+
id: "free-consultation-product",
|
|
7414
|
+
name: "Free Consultation Service",
|
|
7415
|
+
description: "No physical product required for consultation services",
|
|
7416
|
+
brandId: "consultation-brand",
|
|
7417
|
+
brandName: "Consultation Services",
|
|
7418
|
+
technologyId: "free-consultation-tech",
|
|
7419
|
+
technologyName: "Free Consultation Technology",
|
|
7420
|
+
categoryId: "consultation",
|
|
7421
|
+
subcategoryId: "free-consultation",
|
|
7422
|
+
isActive: true,
|
|
7423
|
+
createdAt: now,
|
|
7424
|
+
updatedAt: now,
|
|
7425
|
+
technicalDetails: "Virtual consultation service - no physical product required",
|
|
7426
|
+
warnings: [],
|
|
7427
|
+
indications: ["Initial patient assessment", "Treatment planning", "Patient education"],
|
|
7428
|
+
contraindications: []
|
|
7429
|
+
};
|
|
7430
|
+
batch.set(productRef, productData);
|
|
7415
7431
|
await batch.commit();
|
|
7416
7432
|
console.log("[freeConsultationInfrastructure] Successfully created:");
|
|
7417
7433
|
console.log(" \u2713 Category: consultation");
|
|
7418
7434
|
console.log(" \u2713 Subcategory: free-consultation");
|
|
7419
7435
|
console.log(" \u2713 Technology: free-consultation-tech");
|
|
7436
|
+
console.log(" \u2713 Product: free-consultation-product");
|
|
7420
7437
|
}
|
|
7421
7438
|
|
|
7422
7439
|
// src/admin/mailing/practitionerInvite/templates/invitation.template.ts
|
package/dist/backoffice/index.js
CHANGED
|
@@ -2276,8 +2276,8 @@ var TechnologyService = class extends BaseService {
|
|
|
2276
2276
|
* console.log(allowedTechnologies.subcategories); // ["subcategory1", "subcategory2"]
|
|
2277
2277
|
*/
|
|
2278
2278
|
async getAllowedTechnologies(practitioner) {
|
|
2279
|
-
const allTechnologies = await this.
|
|
2280
|
-
const allowedTechnologies = allTechnologies.
|
|
2279
|
+
const allTechnologies = await this.getAllForFilter();
|
|
2280
|
+
const allowedTechnologies = allTechnologies.filter(
|
|
2281
2281
|
(technology) => this.validateCertification(technology.certificationRequirement, practitioner.certification)
|
|
2282
2282
|
);
|
|
2283
2283
|
const families = [...new Set(allowedTechnologies.map((t) => t.family))];
|
|
@@ -2292,8 +2292,8 @@ var TechnologyService = class extends BaseService {
|
|
|
2292
2292
|
* console.log(allowedTechnologies.subcategories); // ["subcategory1", "subcategory2"]
|
|
2293
2293
|
*/
|
|
2294
2294
|
async getAllowedTechnologies(practitioner) {
|
|
2295
|
-
const allTechnologies = await this.
|
|
2296
|
-
const allowedTechnologies = allTechnologies.
|
|
2295
|
+
const allTechnologies = await this.getAllForFilter();
|
|
2296
|
+
const allowedTechnologies = allTechnologies.filter(
|
|
2297
2297
|
(technology) => this.validateCertification(technology.certificationRequirement, practitioner.certification)
|
|
2298
2298
|
);
|
|
2299
2299
|
const families = [...new Set(allowedTechnologies.map((t) => t.family))];
|
package/dist/index.d.mts
CHANGED
|
@@ -43,6 +43,7 @@ interface BaseReview {
|
|
|
43
43
|
*/
|
|
44
44
|
interface ClinicReview extends BaseReview {
|
|
45
45
|
clinicId: string;
|
|
46
|
+
clinicName?: string;
|
|
46
47
|
cleanliness: number;
|
|
47
48
|
facilities: number;
|
|
48
49
|
staffFriendliness: number;
|
|
@@ -57,6 +58,7 @@ interface ClinicReview extends BaseReview {
|
|
|
57
58
|
*/
|
|
58
59
|
interface PractitionerReview extends BaseReview {
|
|
59
60
|
practitionerId: string;
|
|
61
|
+
practitionerName?: string;
|
|
60
62
|
knowledgeAndExpertise: number;
|
|
61
63
|
communicationSkills: number;
|
|
62
64
|
bedSideManner: number;
|
|
@@ -71,6 +73,7 @@ interface PractitionerReview extends BaseReview {
|
|
|
71
73
|
*/
|
|
72
74
|
interface ProcedureReview extends BaseReview {
|
|
73
75
|
procedureId: string;
|
|
76
|
+
procedureName?: string;
|
|
74
77
|
effectivenessOfTreatment: number;
|
|
75
78
|
outcomeExplanation: number;
|
|
76
79
|
painManagement: number;
|
|
@@ -6843,7 +6846,7 @@ declare class ReviewService extends BaseService {
|
|
|
6843
6846
|
* @param appointmentId - ID of the completed appointment
|
|
6844
6847
|
* @returns The created review
|
|
6845
6848
|
*/
|
|
6846
|
-
createReview(data: Omit<Review,
|
|
6849
|
+
createReview(data: Omit<Review, 'id' | 'createdAt' | 'updatedAt' | 'appointmentId' | 'overallRating'>, appointmentId: string): Promise<Review>;
|
|
6847
6850
|
/**
|
|
6848
6851
|
* Gets a review by ID
|
|
6849
6852
|
* @param reviewId The ID of the review to get
|
|
@@ -6851,9 +6854,9 @@ declare class ReviewService extends BaseService {
|
|
|
6851
6854
|
*/
|
|
6852
6855
|
getReview(reviewId: string): Promise<Review | null>;
|
|
6853
6856
|
/**
|
|
6854
|
-
* Gets all reviews for a specific patient
|
|
6857
|
+
* Gets all reviews for a specific patient with enhanced entity names
|
|
6855
6858
|
* @param patientId The ID of the patient
|
|
6856
|
-
* @returns Array of reviews for the patient
|
|
6859
|
+
* @returns Array of reviews for the patient with clinic, practitioner, and procedure names
|
|
6857
6860
|
*/
|
|
6858
6861
|
getReviewsByPatient(patientId: string): Promise<Review[]>;
|
|
6859
6862
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ interface BaseReview {
|
|
|
43
43
|
*/
|
|
44
44
|
interface ClinicReview extends BaseReview {
|
|
45
45
|
clinicId: string;
|
|
46
|
+
clinicName?: string;
|
|
46
47
|
cleanliness: number;
|
|
47
48
|
facilities: number;
|
|
48
49
|
staffFriendliness: number;
|
|
@@ -57,6 +58,7 @@ interface ClinicReview extends BaseReview {
|
|
|
57
58
|
*/
|
|
58
59
|
interface PractitionerReview extends BaseReview {
|
|
59
60
|
practitionerId: string;
|
|
61
|
+
practitionerName?: string;
|
|
60
62
|
knowledgeAndExpertise: number;
|
|
61
63
|
communicationSkills: number;
|
|
62
64
|
bedSideManner: number;
|
|
@@ -71,6 +73,7 @@ interface PractitionerReview extends BaseReview {
|
|
|
71
73
|
*/
|
|
72
74
|
interface ProcedureReview extends BaseReview {
|
|
73
75
|
procedureId: string;
|
|
76
|
+
procedureName?: string;
|
|
74
77
|
effectivenessOfTreatment: number;
|
|
75
78
|
outcomeExplanation: number;
|
|
76
79
|
painManagement: number;
|
|
@@ -6843,7 +6846,7 @@ declare class ReviewService extends BaseService {
|
|
|
6843
6846
|
* @param appointmentId - ID of the completed appointment
|
|
6844
6847
|
* @returns The created review
|
|
6845
6848
|
*/
|
|
6846
|
-
createReview(data: Omit<Review,
|
|
6849
|
+
createReview(data: Omit<Review, 'id' | 'createdAt' | 'updatedAt' | 'appointmentId' | 'overallRating'>, appointmentId: string): Promise<Review>;
|
|
6847
6850
|
/**
|
|
6848
6851
|
* Gets a review by ID
|
|
6849
6852
|
* @param reviewId The ID of the review to get
|
|
@@ -6851,9 +6854,9 @@ declare class ReviewService extends BaseService {
|
|
|
6851
6854
|
*/
|
|
6852
6855
|
getReview(reviewId: string): Promise<Review | null>;
|
|
6853
6856
|
/**
|
|
6854
|
-
* Gets all reviews for a specific patient
|
|
6857
|
+
* Gets all reviews for a specific patient with enhanced entity names
|
|
6855
6858
|
* @param patientId The ID of the patient
|
|
6856
|
-
* @returns Array of reviews for the patient
|
|
6859
|
+
* @returns Array of reviews for the patient with clinic, practitioner, and procedure names
|
|
6857
6860
|
*/
|
|
6858
6861
|
getReviewsByPatient(patientId: string): Promise<Review[]>;
|
|
6859
6862
|
/**
|
package/dist/index.js
CHANGED
|
@@ -7406,9 +7406,12 @@ var UserService = class extends BaseService {
|
|
|
7406
7406
|
if ((await this.getUserById(userId)).patientProfile || patientProfile2.userRef) {
|
|
7407
7407
|
throw new Error("User already has a patient profile.");
|
|
7408
7408
|
}
|
|
7409
|
+
const sensitiveInfo = await patientService.getSensitiveInfo(patientProfile2.id, userId);
|
|
7410
|
+
const fullDisplayName = sensitiveInfo ? `${sensitiveInfo.firstName} ${sensitiveInfo.lastName}` : patientProfile2.displayName;
|
|
7409
7411
|
await patientService.updatePatientProfile(patientProfile2.id, {
|
|
7410
7412
|
userRef: userId,
|
|
7411
|
-
isManual: false
|
|
7413
|
+
isManual: false,
|
|
7414
|
+
displayName: fullDisplayName
|
|
7412
7415
|
});
|
|
7413
7416
|
await patientService.markPatientTokenAsUsed(token.id, token.patientId, userId);
|
|
7414
7417
|
profiles.patientProfile = patientProfile2.id;
|
|
@@ -16086,17 +16089,51 @@ var ReviewService = class extends BaseService {
|
|
|
16086
16089
|
return docSnap.data();
|
|
16087
16090
|
}
|
|
16088
16091
|
/**
|
|
16089
|
-
* Gets all reviews for a specific patient
|
|
16092
|
+
* Gets all reviews for a specific patient with enhanced entity names
|
|
16090
16093
|
* @param patientId The ID of the patient
|
|
16091
|
-
* @returns Array of reviews for the patient
|
|
16094
|
+
* @returns Array of reviews for the patient with clinic, practitioner, and procedure names
|
|
16092
16095
|
*/
|
|
16093
16096
|
async getReviewsByPatient(patientId) {
|
|
16094
|
-
const q = (0, import_firestore48.query)(
|
|
16095
|
-
(0, import_firestore48.collection)(this.db, REVIEWS_COLLECTION),
|
|
16096
|
-
(0, import_firestore48.where)("patientId", "==", patientId)
|
|
16097
|
-
);
|
|
16097
|
+
const q = (0, import_firestore48.query)((0, import_firestore48.collection)(this.db, REVIEWS_COLLECTION), (0, import_firestore48.where)("patientId", "==", patientId));
|
|
16098
16098
|
const snapshot = await (0, import_firestore48.getDocs)(q);
|
|
16099
|
-
|
|
16099
|
+
const reviews = snapshot.docs.map((doc38) => doc38.data());
|
|
16100
|
+
const enhancedReviews = await Promise.all(
|
|
16101
|
+
reviews.map(async (review) => {
|
|
16102
|
+
try {
|
|
16103
|
+
const appointmentDoc = await (0, import_firestore48.getDoc)(
|
|
16104
|
+
(0, import_firestore48.doc)(this.db, APPOINTMENTS_COLLECTION, review.appointmentId)
|
|
16105
|
+
);
|
|
16106
|
+
if (appointmentDoc.exists()) {
|
|
16107
|
+
const appointment = appointmentDoc.data();
|
|
16108
|
+
const enhancedReview = { ...review };
|
|
16109
|
+
if (enhancedReview.clinicReview && appointment.clinicInfo) {
|
|
16110
|
+
enhancedReview.clinicReview = {
|
|
16111
|
+
...enhancedReview.clinicReview,
|
|
16112
|
+
clinicName: appointment.clinicInfo.name
|
|
16113
|
+
};
|
|
16114
|
+
}
|
|
16115
|
+
if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
|
|
16116
|
+
enhancedReview.practitionerReview = {
|
|
16117
|
+
...enhancedReview.practitionerReview,
|
|
16118
|
+
practitionerName: appointment.practitionerInfo.name
|
|
16119
|
+
};
|
|
16120
|
+
}
|
|
16121
|
+
if (enhancedReview.procedureReview && appointment.procedureInfo) {
|
|
16122
|
+
enhancedReview.procedureReview = {
|
|
16123
|
+
...enhancedReview.procedureReview,
|
|
16124
|
+
procedureName: appointment.procedureInfo.name
|
|
16125
|
+
};
|
|
16126
|
+
}
|
|
16127
|
+
return enhancedReview;
|
|
16128
|
+
}
|
|
16129
|
+
return review;
|
|
16130
|
+
} catch (error) {
|
|
16131
|
+
console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
|
|
16132
|
+
return review;
|
|
16133
|
+
}
|
|
16134
|
+
})
|
|
16135
|
+
);
|
|
16136
|
+
return enhancedReviews;
|
|
16100
16137
|
}
|
|
16101
16138
|
/**
|
|
16102
16139
|
* Gets all reviews for a specific clinic
|
|
@@ -17298,8 +17335,8 @@ var TechnologyService = class extends BaseService {
|
|
|
17298
17335
|
* console.log(allowedTechnologies.subcategories); // ["subcategory1", "subcategory2"]
|
|
17299
17336
|
*/
|
|
17300
17337
|
async getAllowedTechnologies(practitioner) {
|
|
17301
|
-
const allTechnologies = await this.
|
|
17302
|
-
const allowedTechnologies = allTechnologies.
|
|
17338
|
+
const allTechnologies = await this.getAllForFilter();
|
|
17339
|
+
const allowedTechnologies = allTechnologies.filter(
|
|
17303
17340
|
(technology) => this.validateCertification(technology.certificationRequirement, practitioner.certification)
|
|
17304
17341
|
);
|
|
17305
17342
|
const families = [...new Set(allowedTechnologies.map((t) => t.family))];
|
package/dist/index.mjs
CHANGED
|
@@ -7447,9 +7447,12 @@ var UserService = class extends BaseService {
|
|
|
7447
7447
|
if ((await this.getUserById(userId)).patientProfile || patientProfile2.userRef) {
|
|
7448
7448
|
throw new Error("User already has a patient profile.");
|
|
7449
7449
|
}
|
|
7450
|
+
const sensitiveInfo = await patientService.getSensitiveInfo(patientProfile2.id, userId);
|
|
7451
|
+
const fullDisplayName = sensitiveInfo ? `${sensitiveInfo.firstName} ${sensitiveInfo.lastName}` : patientProfile2.displayName;
|
|
7450
7452
|
await patientService.updatePatientProfile(patientProfile2.id, {
|
|
7451
7453
|
userRef: userId,
|
|
7452
|
-
isManual: false
|
|
7454
|
+
isManual: false,
|
|
7455
|
+
displayName: fullDisplayName
|
|
7453
7456
|
});
|
|
7454
7457
|
await patientService.markPatientTokenAsUsed(token.id, token.patientId, userId);
|
|
7455
7458
|
profiles.patientProfile = patientProfile2.id;
|
|
@@ -16350,17 +16353,51 @@ var ReviewService = class extends BaseService {
|
|
|
16350
16353
|
return docSnap.data();
|
|
16351
16354
|
}
|
|
16352
16355
|
/**
|
|
16353
|
-
* Gets all reviews for a specific patient
|
|
16356
|
+
* Gets all reviews for a specific patient with enhanced entity names
|
|
16354
16357
|
* @param patientId The ID of the patient
|
|
16355
|
-
* @returns Array of reviews for the patient
|
|
16358
|
+
* @returns Array of reviews for the patient with clinic, practitioner, and procedure names
|
|
16356
16359
|
*/
|
|
16357
16360
|
async getReviewsByPatient(patientId) {
|
|
16358
|
-
const q = query31(
|
|
16359
|
-
collection31(this.db, REVIEWS_COLLECTION),
|
|
16360
|
-
where31("patientId", "==", patientId)
|
|
16361
|
-
);
|
|
16361
|
+
const q = query31(collection31(this.db, REVIEWS_COLLECTION), where31("patientId", "==", patientId));
|
|
16362
16362
|
const snapshot = await getDocs31(q);
|
|
16363
|
-
|
|
16363
|
+
const reviews = snapshot.docs.map((doc38) => doc38.data());
|
|
16364
|
+
const enhancedReviews = await Promise.all(
|
|
16365
|
+
reviews.map(async (review) => {
|
|
16366
|
+
try {
|
|
16367
|
+
const appointmentDoc = await getDoc33(
|
|
16368
|
+
doc31(this.db, APPOINTMENTS_COLLECTION, review.appointmentId)
|
|
16369
|
+
);
|
|
16370
|
+
if (appointmentDoc.exists()) {
|
|
16371
|
+
const appointment = appointmentDoc.data();
|
|
16372
|
+
const enhancedReview = { ...review };
|
|
16373
|
+
if (enhancedReview.clinicReview && appointment.clinicInfo) {
|
|
16374
|
+
enhancedReview.clinicReview = {
|
|
16375
|
+
...enhancedReview.clinicReview,
|
|
16376
|
+
clinicName: appointment.clinicInfo.name
|
|
16377
|
+
};
|
|
16378
|
+
}
|
|
16379
|
+
if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
|
|
16380
|
+
enhancedReview.practitionerReview = {
|
|
16381
|
+
...enhancedReview.practitionerReview,
|
|
16382
|
+
practitionerName: appointment.practitionerInfo.name
|
|
16383
|
+
};
|
|
16384
|
+
}
|
|
16385
|
+
if (enhancedReview.procedureReview && appointment.procedureInfo) {
|
|
16386
|
+
enhancedReview.procedureReview = {
|
|
16387
|
+
...enhancedReview.procedureReview,
|
|
16388
|
+
procedureName: appointment.procedureInfo.name
|
|
16389
|
+
};
|
|
16390
|
+
}
|
|
16391
|
+
return enhancedReview;
|
|
16392
|
+
}
|
|
16393
|
+
return review;
|
|
16394
|
+
} catch (error) {
|
|
16395
|
+
console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
|
|
16396
|
+
return review;
|
|
16397
|
+
}
|
|
16398
|
+
})
|
|
16399
|
+
);
|
|
16400
|
+
return enhancedReviews;
|
|
16364
16401
|
}
|
|
16365
16402
|
/**
|
|
16366
16403
|
* Gets all reviews for a specific clinic
|
|
@@ -17618,8 +17655,8 @@ var TechnologyService = class extends BaseService {
|
|
|
17618
17655
|
* console.log(allowedTechnologies.subcategories); // ["subcategory1", "subcategory2"]
|
|
17619
17656
|
*/
|
|
17620
17657
|
async getAllowedTechnologies(practitioner) {
|
|
17621
|
-
const allTechnologies = await this.
|
|
17622
|
-
const allowedTechnologies = allTechnologies.
|
|
17658
|
+
const allTechnologies = await this.getAllForFilter();
|
|
17659
|
+
const allowedTechnologies = allTechnologies.filter(
|
|
17623
17660
|
(technology) => this.validateCertification(technology.certificationRequirement, practitioner.certification)
|
|
17624
17661
|
);
|
|
17625
17662
|
const families = [...new Set(allowedTechnologies.map((t) => t.family))];
|
package/package.json
CHANGED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Free Consultation Infrastructure
|
|
2
|
+
|
|
3
|
+
This module manages the infrastructure required for free consultation procedures in the MetaEstetics system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The free consultation feature allows practitioners to offer complimentary initial consultations to patients. This requires specific infrastructure components to be present in the database.
|
|
8
|
+
|
|
9
|
+
## Infrastructure Components
|
|
10
|
+
|
|
11
|
+
The system creates the following components when `freeConsultationInfrastructure()` is called:
|
|
12
|
+
|
|
13
|
+
### 1. Category: "consultation"
|
|
14
|
+
- **ID**: `consultation`
|
|
15
|
+
- **Name**: Consultation
|
|
16
|
+
- **Description**: Professional consultation services for treatment planning and assessment
|
|
17
|
+
- **Family**: AESTHETICS
|
|
18
|
+
|
|
19
|
+
### 2. Subcategory: "free-consultation"
|
|
20
|
+
- **ID**: `free-consultation`
|
|
21
|
+
- **Name**: Free Consultation
|
|
22
|
+
- **Description**: Complimentary initial consultation to discuss treatment options and assess patient needs
|
|
23
|
+
- **Parent Category**: consultation
|
|
24
|
+
|
|
25
|
+
### 3. Technology: "free-consultation-tech"
|
|
26
|
+
- **ID**: `free-consultation-tech`
|
|
27
|
+
- **Name**: Free Consultation Technology
|
|
28
|
+
- **Description**: Technology framework for providing free initial consultations to patients
|
|
29
|
+
- **Requirements**: No pre/post requirements
|
|
30
|
+
- **Certification**: Minimum level AESTHETICIAN
|
|
31
|
+
|
|
32
|
+
### 4. Product: "free-consultation-product"
|
|
33
|
+
- **ID**: `free-consultation-product`
|
|
34
|
+
- **Name**: Free Consultation Service
|
|
35
|
+
- **Description**: No physical product required for consultation services
|
|
36
|
+
- **Brand**: Consultation Services
|
|
37
|
+
- **Type**: Virtual service
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Ensuring Infrastructure Exists
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { freeConsultationInfrastructure } from './free-consultation-utils.admin';
|
|
45
|
+
|
|
46
|
+
// Ensure infrastructure exists before creating consultation procedures
|
|
47
|
+
const infrastructureExists = await freeConsultationInfrastructure();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Creating Free Consultation Procedures
|
|
51
|
+
|
|
52
|
+
The infrastructure is automatically checked and created when practitioners enable free consultations through the `PractitionerService.EnableFreeConsultation()` method.
|
|
53
|
+
|
|
54
|
+
## Error Handling
|
|
55
|
+
|
|
56
|
+
If you encounter the error:
|
|
57
|
+
```
|
|
58
|
+
Product with ID free-consultation-product not found for technology free-consultation-tech
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This indicates that the infrastructure was not properly created. The system will automatically attempt to create the missing infrastructure when the error occurs.
|
|
62
|
+
|
|
63
|
+
## Database Structure
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
categories/
|
|
67
|
+
consultation/
|
|
68
|
+
subcategories/
|
|
69
|
+
free-consultation/
|
|
70
|
+
|
|
71
|
+
technologies/
|
|
72
|
+
free-consultation-tech/
|
|
73
|
+
products/
|
|
74
|
+
free-consultation-product/
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- The infrastructure is created atomically using Firestore batch operations
|
|
80
|
+
- All components are created with `isActive: true`
|
|
81
|
+
- The system checks for existing infrastructure before attempting to create new components
|
|
82
|
+
- No physical products are required for consultation procedures
|
|
@@ -1,80 +1,72 @@
|
|
|
1
|
-
import * as admin from
|
|
2
|
-
import { ProcedureFamily } from
|
|
1
|
+
import * as admin from 'firebase-admin';
|
|
2
|
+
import { ProcedureFamily } from '../../backoffice/types/static/procedure-family.types';
|
|
3
3
|
import {
|
|
4
4
|
CertificationLevel,
|
|
5
5
|
CertificationSpecialty,
|
|
6
|
-
} from
|
|
7
|
-
import { CATEGORIES_COLLECTION } from
|
|
8
|
-
import { SUBCATEGORIES_COLLECTION } from
|
|
9
|
-
import { TECHNOLOGIES_COLLECTION } from
|
|
6
|
+
} from '../../backoffice/types/static/certification.types';
|
|
7
|
+
import { CATEGORIES_COLLECTION } from '../../backoffice/types/category.types';
|
|
8
|
+
import { SUBCATEGORIES_COLLECTION } from '../../backoffice/types/subcategory.types';
|
|
9
|
+
import { TECHNOLOGIES_COLLECTION } from '../../backoffice/types/technology.types';
|
|
10
|
+
import { PRODUCTS_COLLECTION } from '../../backoffice/types/product.types';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Ensures that the free consultation infrastructure exists in the database
|
|
13
|
-
* Creates category, subcategory, and
|
|
14
|
+
* Creates category, subcategory, technology, and product if they don't exist
|
|
14
15
|
* @param db - Firestore database instance (optional, defaults to admin.firestore())
|
|
15
16
|
* @returns Promise<boolean> - Always returns true after ensuring infrastructure exists
|
|
16
17
|
*/
|
|
17
18
|
export async function freeConsultationInfrastructure(
|
|
18
|
-
db?: admin.firestore.Firestore
|
|
19
|
+
db?: admin.firestore.Firestore,
|
|
19
20
|
): Promise<boolean> {
|
|
20
21
|
const firestore = db || admin.firestore();
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
|
-
console.log(
|
|
24
|
-
"[freeConsultationInfrastructure] Checking free consultation infrastructure..."
|
|
25
|
-
);
|
|
24
|
+
console.log('[freeConsultationInfrastructure] Checking free consultation infrastructure...');
|
|
26
25
|
|
|
27
26
|
// Check if the technology already exists
|
|
28
27
|
const technologyRef = firestore
|
|
29
28
|
.collection(TECHNOLOGIES_COLLECTION)
|
|
30
|
-
.doc(
|
|
29
|
+
.doc('free-consultation-tech');
|
|
31
30
|
|
|
32
31
|
const technologyDoc = await technologyRef.get();
|
|
33
32
|
|
|
34
33
|
if (technologyDoc.exists) {
|
|
35
34
|
console.log(
|
|
36
|
-
|
|
35
|
+
'[freeConsultationInfrastructure] Free consultation infrastructure already exists',
|
|
37
36
|
);
|
|
38
37
|
return true;
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
console.log(
|
|
42
|
-
"[freeConsultationInfrastructure] Creating free consultation infrastructure..."
|
|
43
|
-
);
|
|
40
|
+
console.log('[freeConsultationInfrastructure] Creating free consultation infrastructure...');
|
|
44
41
|
|
|
45
|
-
// Create the infrastructure in the correct order: Category → Subcategory → Technology
|
|
42
|
+
// Create the infrastructure in the correct order: Category → Subcategory → Technology → Product
|
|
46
43
|
await createFreeConsultationInfrastructure(firestore);
|
|
47
44
|
|
|
48
45
|
console.log(
|
|
49
|
-
|
|
46
|
+
'[freeConsultationInfrastructure] Successfully created free consultation infrastructure',
|
|
50
47
|
);
|
|
51
48
|
return true;
|
|
52
49
|
} catch (error) {
|
|
53
|
-
console.error(
|
|
54
|
-
"[freeConsultationInfrastructure] Error ensuring infrastructure:",
|
|
55
|
-
error
|
|
56
|
-
);
|
|
50
|
+
console.error('[freeConsultationInfrastructure] Error ensuring infrastructure:', error);
|
|
57
51
|
throw error;
|
|
58
52
|
}
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
/**
|
|
62
56
|
* Creates the complete free consultation infrastructure
|
|
57
|
+
* Creates category, subcategory, technology, and product in the correct order
|
|
63
58
|
* @param db - Firestore database instance
|
|
64
59
|
*/
|
|
65
|
-
async function createFreeConsultationInfrastructure(
|
|
66
|
-
db: admin.firestore.Firestore
|
|
67
|
-
): Promise<void> {
|
|
60
|
+
async function createFreeConsultationInfrastructure(db: admin.firestore.Firestore): Promise<void> {
|
|
68
61
|
const batch = db.batch();
|
|
69
62
|
const now = new Date();
|
|
70
63
|
|
|
71
64
|
// 1. Create Category: "consultation"
|
|
72
|
-
const categoryRef = db.collection(CATEGORIES_COLLECTION).doc(
|
|
65
|
+
const categoryRef = db.collection(CATEGORIES_COLLECTION).doc('consultation');
|
|
73
66
|
const categoryData = {
|
|
74
|
-
id:
|
|
75
|
-
name:
|
|
76
|
-
description:
|
|
77
|
-
"Professional consultation services for treatment planning and assessment",
|
|
67
|
+
id: 'consultation',
|
|
68
|
+
name: 'Consultation',
|
|
69
|
+
description: 'Professional consultation services for treatment planning and assessment',
|
|
78
70
|
family: ProcedureFamily.AESTHETICS,
|
|
79
71
|
isActive: true,
|
|
80
72
|
createdAt: now,
|
|
@@ -85,16 +77,16 @@ async function createFreeConsultationInfrastructure(
|
|
|
85
77
|
// 2. Create Subcategory: "free-consultation"
|
|
86
78
|
const subcategoryRef = db
|
|
87
79
|
.collection(CATEGORIES_COLLECTION)
|
|
88
|
-
.doc(
|
|
80
|
+
.doc('consultation')
|
|
89
81
|
.collection(SUBCATEGORIES_COLLECTION)
|
|
90
|
-
.doc(
|
|
82
|
+
.doc('free-consultation');
|
|
91
83
|
|
|
92
84
|
const subcategoryData = {
|
|
93
|
-
id:
|
|
94
|
-
name:
|
|
85
|
+
id: 'free-consultation',
|
|
86
|
+
name: 'Free Consultation',
|
|
95
87
|
description:
|
|
96
|
-
|
|
97
|
-
categoryId:
|
|
88
|
+
'Complimentary initial consultation to discuss treatment options and assess patient needs',
|
|
89
|
+
categoryId: 'consultation',
|
|
98
90
|
isActive: true,
|
|
99
91
|
createdAt: now,
|
|
100
92
|
updatedAt: now,
|
|
@@ -102,19 +94,15 @@ async function createFreeConsultationInfrastructure(
|
|
|
102
94
|
batch.set(subcategoryRef, subcategoryData);
|
|
103
95
|
|
|
104
96
|
// 3. Create Technology: "free-consultation-tech"
|
|
105
|
-
const technologyRef = db
|
|
106
|
-
.collection(TECHNOLOGIES_COLLECTION)
|
|
107
|
-
.doc("free-consultation-tech");
|
|
97
|
+
const technologyRef = db.collection(TECHNOLOGIES_COLLECTION).doc('free-consultation-tech');
|
|
108
98
|
const technologyData = {
|
|
109
|
-
id:
|
|
110
|
-
name:
|
|
111
|
-
description:
|
|
112
|
-
"Technology framework for providing free initial consultations to patients",
|
|
99
|
+
id: 'free-consultation-tech',
|
|
100
|
+
name: 'Free Consultation Technology',
|
|
101
|
+
description: 'Technology framework for providing free initial consultations to patients',
|
|
113
102
|
family: ProcedureFamily.AESTHETICS,
|
|
114
|
-
categoryId:
|
|
115
|
-
subcategoryId:
|
|
116
|
-
technicalDetails:
|
|
117
|
-
"Standard consultation protocol for initial patient assessment",
|
|
103
|
+
categoryId: 'consultation',
|
|
104
|
+
subcategoryId: 'free-consultation',
|
|
105
|
+
technicalDetails: 'Standard consultation protocol for initial patient assessment',
|
|
118
106
|
requirements: {
|
|
119
107
|
pre: [], // No pre-requirements for consultation
|
|
120
108
|
post: [], // No post-requirements for consultation
|
|
@@ -122,9 +110,9 @@ async function createFreeConsultationInfrastructure(
|
|
|
122
110
|
blockingConditions: [], // No blocking conditions for consultation
|
|
123
111
|
contraindications: [], // No contraindications for consultation
|
|
124
112
|
benefits: [
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
113
|
+
'IMPROVED_PATIENT_UNDERSTANDING',
|
|
114
|
+
'BETTER_TREATMENT_PLANNING',
|
|
115
|
+
'ENHANCED_PATIENT_CONFIDENCE',
|
|
128
116
|
],
|
|
129
117
|
certificationRequirement: {
|
|
130
118
|
minimumLevel: CertificationLevel.AESTHETICIAN,
|
|
@@ -137,11 +125,39 @@ async function createFreeConsultationInfrastructure(
|
|
|
137
125
|
};
|
|
138
126
|
batch.set(technologyRef, technologyData);
|
|
139
127
|
|
|
128
|
+
// 4. Create Product: "free-consultation-product"
|
|
129
|
+
const productRef = db
|
|
130
|
+
.collection(TECHNOLOGIES_COLLECTION)
|
|
131
|
+
.doc('free-consultation-tech')
|
|
132
|
+
.collection(PRODUCTS_COLLECTION)
|
|
133
|
+
.doc('free-consultation-product');
|
|
134
|
+
|
|
135
|
+
const productData = {
|
|
136
|
+
id: 'free-consultation-product',
|
|
137
|
+
name: 'Free Consultation Service',
|
|
138
|
+
description: 'No physical product required for consultation services',
|
|
139
|
+
brandId: 'consultation-brand',
|
|
140
|
+
brandName: 'Consultation Services',
|
|
141
|
+
technologyId: 'free-consultation-tech',
|
|
142
|
+
technologyName: 'Free Consultation Technology',
|
|
143
|
+
categoryId: 'consultation',
|
|
144
|
+
subcategoryId: 'free-consultation',
|
|
145
|
+
isActive: true,
|
|
146
|
+
createdAt: now,
|
|
147
|
+
updatedAt: now,
|
|
148
|
+
technicalDetails: 'Virtual consultation service - no physical product required',
|
|
149
|
+
warnings: [],
|
|
150
|
+
indications: ['Initial patient assessment', 'Treatment planning', 'Patient education'],
|
|
151
|
+
contraindications: [],
|
|
152
|
+
};
|
|
153
|
+
batch.set(productRef, productData);
|
|
154
|
+
|
|
140
155
|
// Commit all changes atomically
|
|
141
156
|
await batch.commit();
|
|
142
157
|
|
|
143
|
-
console.log(
|
|
144
|
-
console.log(
|
|
145
|
-
console.log(
|
|
146
|
-
console.log(
|
|
158
|
+
console.log('[freeConsultationInfrastructure] Successfully created:');
|
|
159
|
+
console.log(' ✓ Category: consultation');
|
|
160
|
+
console.log(' ✓ Subcategory: free-consultation');
|
|
161
|
+
console.log(' ✓ Technology: free-consultation-tech');
|
|
162
|
+
console.log(' ✓ Product: free-consultation-product');
|
|
147
163
|
}
|
|
@@ -693,10 +693,10 @@ export class TechnologyService extends BaseService implements ITechnologyService
|
|
|
693
693
|
subcategories: string[];
|
|
694
694
|
}> {
|
|
695
695
|
// Get all active technologies
|
|
696
|
-
const allTechnologies = await this.
|
|
696
|
+
const allTechnologies = await this.getAllForFilter();
|
|
697
697
|
|
|
698
698
|
// Filter technologies based on certification requirements
|
|
699
|
-
const allowedTechnologies = allTechnologies.
|
|
699
|
+
const allowedTechnologies = allTechnologies.filter(technology =>
|
|
700
700
|
this.validateCertification(technology.certificationRequirement, practitioner.certification),
|
|
701
701
|
);
|
|
702
702
|
|
|
@@ -8,23 +8,21 @@ import {
|
|
|
8
8
|
setDoc,
|
|
9
9
|
deleteDoc,
|
|
10
10
|
serverTimestamp,
|
|
11
|
-
} from
|
|
12
|
-
import { BaseService } from
|
|
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
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
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,11 +36,8 @@ export class ReviewService extends BaseService {
|
|
|
38
36
|
* @returns The created review
|
|
39
37
|
*/
|
|
40
38
|
async createReview(
|
|
41
|
-
data: Omit<
|
|
42
|
-
|
|
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 {
|
|
48
43
|
// Validate input data
|
|
@@ -166,17 +161,63 @@ export class ReviewService extends BaseService {
|
|
|
166
161
|
}
|
|
167
162
|
|
|
168
163
|
/**
|
|
169
|
-
* Gets all reviews for a specific patient
|
|
164
|
+
* Gets all reviews for a specific patient with enhanced entity names
|
|
170
165
|
* @param patientId The ID of the patient
|
|
171
|
-
* @returns Array of reviews for the patient
|
|
166
|
+
* @returns Array of reviews for the patient with clinic, practitioner, and procedure names
|
|
172
167
|
*/
|
|
173
168
|
async getReviewsByPatient(patientId: string): Promise<Review[]> {
|
|
174
|
-
const q = query(
|
|
175
|
-
collection(this.db, REVIEWS_COLLECTION),
|
|
176
|
-
where("patientId", "==", patientId)
|
|
177
|
-
);
|
|
169
|
+
const q = query(collection(this.db, REVIEWS_COLLECTION), where('patientId', '==', patientId));
|
|
178
170
|
const snapshot = await getDocs(q);
|
|
179
|
-
|
|
171
|
+
const reviews = snapshot.docs.map(doc => doc.data() as Review);
|
|
172
|
+
|
|
173
|
+
// Enhance reviews with entity names from appointments
|
|
174
|
+
const enhancedReviews = await Promise.all(
|
|
175
|
+
reviews.map(async review => {
|
|
176
|
+
try {
|
|
177
|
+
// Fetch the associated appointment
|
|
178
|
+
const appointmentDoc = await getDoc(
|
|
179
|
+
doc(this.db, APPOINTMENTS_COLLECTION, review.appointmentId),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (appointmentDoc.exists()) {
|
|
183
|
+
const appointment = appointmentDoc.data() as Appointment;
|
|
184
|
+
|
|
185
|
+
// Create enhanced review with entity names
|
|
186
|
+
const enhancedReview = { ...review };
|
|
187
|
+
|
|
188
|
+
if (enhancedReview.clinicReview && appointment.clinicInfo) {
|
|
189
|
+
enhancedReview.clinicReview = {
|
|
190
|
+
...enhancedReview.clinicReview,
|
|
191
|
+
clinicName: appointment.clinicInfo.name,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (enhancedReview.practitionerReview && appointment.practitionerInfo) {
|
|
196
|
+
enhancedReview.practitionerReview = {
|
|
197
|
+
...enhancedReview.practitionerReview,
|
|
198
|
+
practitionerName: appointment.practitionerInfo.name,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (enhancedReview.procedureReview && appointment.procedureInfo) {
|
|
203
|
+
enhancedReview.procedureReview = {
|
|
204
|
+
...enhancedReview.procedureReview,
|
|
205
|
+
procedureName: appointment.procedureInfo.name,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return enhancedReview;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return review;
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.warn(`Failed to enhance review ${review.id} with entity names:`, error);
|
|
215
|
+
return review;
|
|
216
|
+
}
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return enhancedReviews;
|
|
180
221
|
}
|
|
181
222
|
|
|
182
223
|
/**
|
|
@@ -187,10 +228,10 @@ export class ReviewService extends BaseService {
|
|
|
187
228
|
async getReviewsByClinic(clinicId: string): Promise<Review[]> {
|
|
188
229
|
const q = query(
|
|
189
230
|
collection(this.db, REVIEWS_COLLECTION),
|
|
190
|
-
where(
|
|
231
|
+
where('clinicReview.clinicId', '==', clinicId),
|
|
191
232
|
);
|
|
192
233
|
const snapshot = await getDocs(q);
|
|
193
|
-
return snapshot.docs.map(
|
|
234
|
+
return snapshot.docs.map(doc => doc.data() as Review);
|
|
194
235
|
}
|
|
195
236
|
|
|
196
237
|
/**
|
|
@@ -201,10 +242,10 @@ export class ReviewService extends BaseService {
|
|
|
201
242
|
async getReviewsByPractitioner(practitionerId: string): Promise<Review[]> {
|
|
202
243
|
const q = query(
|
|
203
244
|
collection(this.db, REVIEWS_COLLECTION),
|
|
204
|
-
where(
|
|
245
|
+
where('practitionerReview.practitionerId', '==', practitionerId),
|
|
205
246
|
);
|
|
206
247
|
const snapshot = await getDocs(q);
|
|
207
|
-
return snapshot.docs.map(
|
|
248
|
+
return snapshot.docs.map(doc => doc.data() as Review);
|
|
208
249
|
}
|
|
209
250
|
|
|
210
251
|
/**
|
|
@@ -215,10 +256,10 @@ export class ReviewService extends BaseService {
|
|
|
215
256
|
async getReviewsByProcedure(procedureId: string): Promise<Review[]> {
|
|
216
257
|
const q = query(
|
|
217
258
|
collection(this.db, REVIEWS_COLLECTION),
|
|
218
|
-
where(
|
|
259
|
+
where('procedureReview.procedureId', '==', procedureId),
|
|
219
260
|
);
|
|
220
261
|
const snapshot = await getDocs(q);
|
|
221
|
-
return snapshot.docs.map(
|
|
262
|
+
return snapshot.docs.map(doc => doc.data() as Review);
|
|
222
263
|
}
|
|
223
264
|
|
|
224
265
|
/**
|
|
@@ -229,7 +270,7 @@ export class ReviewService extends BaseService {
|
|
|
229
270
|
async getReviewByAppointment(appointmentId: string): Promise<Review | null> {
|
|
230
271
|
const q = query(
|
|
231
272
|
collection(this.db, REVIEWS_COLLECTION),
|
|
232
|
-
where(
|
|
273
|
+
where('appointmentId', '==', appointmentId),
|
|
233
274
|
);
|
|
234
275
|
const snapshot = await getDocs(q);
|
|
235
276
|
|
|
@@ -178,10 +178,17 @@ export class UserService extends BaseService {
|
|
|
178
178
|
throw new Error('User already has a patient profile.');
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
// Claim the profile: link userRef
|
|
181
|
+
// Claim the profile: link userRef, set isManual to false, and update displayName
|
|
182
|
+
// Get sensitive info to construct full display name
|
|
183
|
+
const sensitiveInfo = await patientService.getSensitiveInfo(patientProfile.id, userId);
|
|
184
|
+
const fullDisplayName = sensitiveInfo
|
|
185
|
+
? `${sensitiveInfo.firstName} ${sensitiveInfo.lastName}`
|
|
186
|
+
: patientProfile.displayName;
|
|
187
|
+
|
|
182
188
|
await patientService.updatePatientProfile(patientProfile.id, {
|
|
183
189
|
userRef: userId,
|
|
184
190
|
isManual: false,
|
|
191
|
+
displayName: fullDisplayName,
|
|
185
192
|
});
|
|
186
193
|
|
|
187
194
|
// Mark the token as used
|
|
@@ -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
|