@blackcode_sa/metaestetics-api 1.5.29 → 1.5.30
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 +126 -1
- package/dist/admin/index.d.ts +126 -1
- package/dist/admin/index.js +347 -10
- package/dist/admin/index.mjs +345 -10
- package/dist/index.d.mts +64 -71
- package/dist/index.d.ts +64 -71
- package/dist/index.js +323 -688
- package/dist/index.mjs +359 -728
- package/package.json +2 -1
- package/src/admin/aggregation/README.md +79 -0
- package/src/admin/aggregation/clinic/README.md +52 -0
- package/src/admin/aggregation/patient/README.md +27 -0
- package/src/admin/aggregation/practitioner/README.md +42 -0
- package/src/admin/aggregation/procedure/README.md +43 -0
- package/src/admin/index.ts +9 -2
- package/src/admin/mailing/README.md +95 -0
- package/src/admin/mailing/base.mailing.service.ts +131 -0
- package/src/admin/mailing/index.ts +2 -0
- package/src/admin/mailing/practitionerInvite/index.ts +1 -0
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +256 -0
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -0
- package/src/services/README.md +106 -0
- package/src/services/clinic/README.md +87 -0
- package/src/services/clinic/clinic.service.ts +3 -126
- package/src/services/clinic/utils/clinic.utils.ts +2 -2
- package/src/services/practitioner/README.md +145 -0
- package/src/services/practitioner/practitioner.service.ts +119 -395
- package/src/services/procedure/README.md +88 -0
- package/src/services/procedure/procedure.service.ts +332 -369
|
@@ -66,120 +66,27 @@ export class PractitionerService extends BaseService {
|
|
|
66
66
|
|
|
67
67
|
private getClinicService(): ClinicService {
|
|
68
68
|
if (!this.clinicService) {
|
|
69
|
-
throw new Error("
|
|
69
|
+
throw new Error("Clinic service not initialized!");
|
|
70
70
|
}
|
|
71
71
|
return this.clinicService;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/**
|
|
75
|
-
* Postavlja referencu na ClinicService nakon inicijalizacije
|
|
76
|
-
*/
|
|
77
74
|
setClinicService(clinicService: ClinicService): void {
|
|
78
75
|
this.clinicService = clinicService;
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
/**
|
|
82
|
-
*
|
|
83
|
-
* @param clinicIds Array of clinic IDs the practitioner works at
|
|
84
|
-
* @returns Array of ClinicInfo objects
|
|
85
|
-
*/
|
|
86
|
-
private async aggregateClinicInfo(
|
|
87
|
-
clinicIds: string[]
|
|
88
|
-
): Promise<ClinicInfo[]> {
|
|
89
|
-
const clinicsInfo: ClinicInfo[] = [];
|
|
90
|
-
|
|
91
|
-
for (const clinicId of clinicIds) {
|
|
92
|
-
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
93
|
-
if (!clinic) continue;
|
|
94
|
-
|
|
95
|
-
clinicsInfo.push({
|
|
96
|
-
id: clinic.id,
|
|
97
|
-
featuredPhoto:
|
|
98
|
-
clinic.featuredPhotos && clinic.featuredPhotos.length > 0
|
|
99
|
-
? clinic.featuredPhotos[0]
|
|
100
|
-
: clinic.coverPhoto || "",
|
|
101
|
-
name: clinic.name,
|
|
102
|
-
description: clinic.description || "",
|
|
103
|
-
location: clinic.location,
|
|
104
|
-
contactInfo: clinic.contactInfo,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return clinicsInfo;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @deprecated Aggregation of procedure info is now handled by ProcedureService.
|
|
113
|
-
*/
|
|
114
|
-
private async aggregateProcedureInfo(
|
|
115
|
-
clinicIds: string[],
|
|
116
|
-
practitionerId: string
|
|
117
|
-
): Promise<ProcedureSummaryInfo[]> {
|
|
118
|
-
console.warn("PractitionerService.aggregateProcedureInfo is deprecated.");
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Updates aggregated data (clinics and procedures) for a practitioner
|
|
124
|
-
* @param practitionerId ID of the practitioner to update
|
|
125
|
-
* @returns Updated practitioner
|
|
126
|
-
*/
|
|
127
|
-
async updateAggregatedData(
|
|
128
|
-
practitionerId: string
|
|
129
|
-
): Promise<Practitioner | null> {
|
|
130
|
-
const practitioner = await this.getPractitioner(practitionerId);
|
|
131
|
-
if (!practitioner) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Aggregate clinic info
|
|
136
|
-
const clinicsInfo = await this.aggregateClinicInfo(practitioner.clinics);
|
|
137
|
-
|
|
138
|
-
// Aggregate procedure info
|
|
139
|
-
const proceduresInfo = await this.aggregateProcedureInfo(
|
|
140
|
-
practitioner.clinics,
|
|
141
|
-
practitionerId
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
// Update the practitioner with aggregated data
|
|
145
|
-
const updatedPractitioner = await this.updatePractitioner(practitionerId, {
|
|
146
|
-
clinicsInfo: clinicsInfo,
|
|
147
|
-
proceduresInfo: proceduresInfo,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return updatedPractitioner;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Kreira novog zdravstvenog radnika
|
|
79
|
+
* Creates a new practitioner
|
|
155
80
|
*/
|
|
156
81
|
async createPractitioner(
|
|
157
82
|
data: CreatePractitionerData
|
|
158
83
|
): Promise<Practitioner> {
|
|
159
84
|
try {
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
// Provera da li već postoji profil za ovog korisnika
|
|
164
|
-
const existingPractitioner = await this.getPractitionerByUserRef(
|
|
165
|
-
validatedData.userRef
|
|
166
|
-
);
|
|
167
|
-
if (existingPractitioner) {
|
|
168
|
-
throw new Error("User already has a practitioner profile");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Provera da li sve klinike postoje
|
|
172
|
-
if (validatedData.clinics) {
|
|
173
|
-
for (const clinicId of validatedData.clinics) {
|
|
174
|
-
const clinic = await this.getClinicService().getClinic(clinicId);
|
|
175
|
-
if (!clinic) {
|
|
176
|
-
throw new Error(`Clinic ${clinicId} not found`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
85
|
+
const validData = createPractitionerSchema.parse(data);
|
|
86
|
+
const practitionerId = this.generateId();
|
|
180
87
|
|
|
181
|
-
//
|
|
182
|
-
const
|
|
88
|
+
// Default review info
|
|
89
|
+
const reviewInfo: PractitionerReviewInfo = {
|
|
183
90
|
totalReviews: 0,
|
|
184
91
|
averageRating: 0,
|
|
185
92
|
knowledgeAndExpertise: 0,
|
|
@@ -190,80 +97,58 @@ export class PractitionerService extends BaseService {
|
|
|
190
97
|
recommendationPercentage: 0,
|
|
191
98
|
};
|
|
192
99
|
|
|
193
|
-
//
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
let clinicsInfo = validatedData.clinicsInfo || [];
|
|
198
|
-
if (
|
|
199
|
-
clinicsInfo.length === 0 &&
|
|
200
|
-
validatedData.clinics &&
|
|
201
|
-
validatedData.clinics.length > 0
|
|
202
|
-
) {
|
|
203
|
-
clinicsInfo = await this.aggregateClinicInfo(validatedData.clinics);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Can't aggregate procedures at creation time since the practitioner ID doesn't exist yet
|
|
207
|
-
// We'll initialize with an empty array and update after creation if needed
|
|
208
|
-
const proceduresInfo: ProcedureSummaryInfo[] = [];
|
|
209
|
-
|
|
210
|
-
const practitionerData: Omit<Practitioner, "createdAt" | "updatedAt"> & {
|
|
211
|
-
createdAt: ReturnType<typeof serverTimestamp>;
|
|
212
|
-
updatedAt: ReturnType<typeof serverTimestamp>;
|
|
100
|
+
// Create practitioner object
|
|
101
|
+
const practitioner: Omit<Practitioner, "createdAt" | "updatedAt"> & {
|
|
102
|
+
createdAt: FieldValue;
|
|
103
|
+
updatedAt: FieldValue;
|
|
213
104
|
} = {
|
|
214
105
|
id: practitionerId,
|
|
215
|
-
userRef:
|
|
216
|
-
basicInfo:
|
|
217
|
-
certification:
|
|
218
|
-
clinics:
|
|
219
|
-
clinicWorkingHours:
|
|
220
|
-
clinicsInfo:
|
|
106
|
+
userRef: validData.userRef,
|
|
107
|
+
basicInfo: validData.basicInfo,
|
|
108
|
+
certification: validData.certification,
|
|
109
|
+
clinics: validData.clinics || [],
|
|
110
|
+
clinicWorkingHours: validData.clinicWorkingHours || [],
|
|
111
|
+
clinicsInfo: [],
|
|
221
112
|
procedures: [],
|
|
222
|
-
proceduresInfo:
|
|
223
|
-
reviewInfo
|
|
224
|
-
isActive:
|
|
225
|
-
isVerified:
|
|
226
|
-
|
|
113
|
+
proceduresInfo: [],
|
|
114
|
+
reviewInfo,
|
|
115
|
+
isActive: validData.isActive !== undefined ? validData.isActive : true,
|
|
116
|
+
isVerified:
|
|
117
|
+
validData.isVerified !== undefined ? validData.isVerified : false,
|
|
118
|
+
status: validData.status || PractitionerStatus.ACTIVE,
|
|
227
119
|
createdAt: serverTimestamp(),
|
|
228
120
|
updatedAt: serverTimestamp(),
|
|
229
121
|
};
|
|
230
122
|
|
|
231
|
-
//
|
|
123
|
+
// Validate the entire object
|
|
232
124
|
practitionerSchema.parse({
|
|
233
|
-
...
|
|
125
|
+
...practitioner,
|
|
234
126
|
createdAt: Timestamp.now(),
|
|
235
127
|
updatedAt: Timestamp.now(),
|
|
236
128
|
});
|
|
237
129
|
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
130
|
+
// Create practitioner document
|
|
131
|
+
const practitionerRef = doc(
|
|
132
|
+
this.db,
|
|
133
|
+
PRACTITIONERS_COLLECTION,
|
|
134
|
+
practitionerId
|
|
242
135
|
);
|
|
243
136
|
|
|
244
|
-
|
|
245
|
-
let savedPractitioner = await this.getPractitioner(practitionerData.id);
|
|
246
|
-
if (!savedPractitioner) {
|
|
247
|
-
throw new Error("Failed to create practitioner profile");
|
|
248
|
-
}
|
|
137
|
+
await setDoc(practitionerRef, practitioner);
|
|
249
138
|
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
) {
|
|
257
|
-
savedPractitioner =
|
|
258
|
-
(await this.updateAggregatedData(savedPractitioner.id)) ||
|
|
259
|
-
savedPractitioner;
|
|
139
|
+
// Return the created practitioner
|
|
140
|
+
const createdPractitioner = await this.getPractitioner(practitionerId);
|
|
141
|
+
if (!createdPractitioner) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Failed to retrieve created practitioner ${practitionerId}`
|
|
144
|
+
);
|
|
260
145
|
}
|
|
261
|
-
|
|
262
|
-
return savedPractitioner;
|
|
146
|
+
return createdPractitioner;
|
|
263
147
|
} catch (error) {
|
|
264
148
|
if (error instanceof z.ZodError) {
|
|
265
|
-
throw new Error(
|
|
149
|
+
throw new Error(`Invalid practitioner data: ${error.message}`);
|
|
266
150
|
}
|
|
151
|
+
console.error("Error creating practitioner:", error);
|
|
267
152
|
throw error;
|
|
268
153
|
}
|
|
269
154
|
}
|
|
@@ -321,13 +206,7 @@ export class PractitionerService extends BaseService {
|
|
|
321
206
|
// Generate ID for the new practitioner
|
|
322
207
|
const practitionerId = this.generateId();
|
|
323
208
|
|
|
324
|
-
|
|
325
|
-
let clinicsInfo = validatedData.clinicsInfo || [];
|
|
326
|
-
if (clinicsInfo.length === 0 && clinics.length > 0) {
|
|
327
|
-
clinicsInfo = await this.aggregateClinicInfo(clinics);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Can't aggregate procedures for draft practitioners yet
|
|
209
|
+
const clinicsInfo = validatedData.clinicsInfo || [];
|
|
331
210
|
const proceduresInfo: ProcedureSummaryInfo[] = [];
|
|
332
211
|
|
|
333
212
|
const practitionerData: Omit<Practitioner, "createdAt" | "updatedAt"> & {
|
|
@@ -647,156 +526,126 @@ export class PractitionerService extends BaseService {
|
|
|
647
526
|
}
|
|
648
527
|
|
|
649
528
|
/**
|
|
650
|
-
*
|
|
529
|
+
* Updates a practitioner
|
|
651
530
|
*/
|
|
652
531
|
async updatePractitioner(
|
|
653
532
|
practitionerId: string,
|
|
654
533
|
data: UpdatePractitionerData
|
|
655
534
|
): Promise<Practitioner> {
|
|
656
|
-
const practitionerRef = doc(
|
|
657
|
-
this.db,
|
|
658
|
-
PRACTITIONERS_COLLECTION,
|
|
659
|
-
practitionerId
|
|
660
|
-
);
|
|
661
|
-
const practitionerDoc = await getDoc(practitionerRef);
|
|
662
|
-
|
|
663
|
-
if (!practitionerDoc.exists()) {
|
|
664
|
-
throw new Error("Practitioner not found");
|
|
665
|
-
}
|
|
666
|
-
|
|
667
535
|
try {
|
|
668
|
-
|
|
536
|
+
// Validate update data
|
|
537
|
+
const validData = data; // Using the passed data directly as it's already validated by the schema type
|
|
669
538
|
|
|
670
|
-
//
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// If clinics changed and clinicsInfo wasn't explicitly provided, update clinicsInfo
|
|
681
|
-
if (!data.clinicsInfo) {
|
|
682
|
-
data.clinicsInfo = await this.aggregateClinicInfo(data.clinics);
|
|
683
|
-
}
|
|
539
|
+
// Get current practitioner data
|
|
540
|
+
const practitionerRef = doc(
|
|
541
|
+
this.db,
|
|
542
|
+
PRACTITIONERS_COLLECTION,
|
|
543
|
+
practitionerId
|
|
544
|
+
);
|
|
545
|
+
const practitionerDoc = await getDoc(practitionerRef);
|
|
684
546
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
data.proceduresInfo = await this.aggregateProcedureInfo(
|
|
688
|
-
data.clinics,
|
|
689
|
-
practitionerId
|
|
690
|
-
);
|
|
691
|
-
}
|
|
547
|
+
if (!practitionerDoc.exists()) {
|
|
548
|
+
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
692
549
|
}
|
|
693
550
|
|
|
551
|
+
const currentPractitioner = practitionerDoc.data() as Practitioner;
|
|
552
|
+
|
|
553
|
+
// Prepare update data
|
|
694
554
|
const updateData = {
|
|
695
|
-
...
|
|
555
|
+
...validData,
|
|
696
556
|
updatedAt: serverTimestamp(),
|
|
697
557
|
};
|
|
698
558
|
|
|
699
|
-
//
|
|
700
|
-
practitionerSchema.parse({
|
|
701
|
-
...currentPractitioner,
|
|
702
|
-
...data,
|
|
703
|
-
updatedAt: Timestamp.now(),
|
|
704
|
-
});
|
|
705
|
-
|
|
559
|
+
// Update practitioner
|
|
706
560
|
await updateDoc(practitionerRef, updateData);
|
|
707
561
|
|
|
562
|
+
// Return updated practitioner
|
|
708
563
|
const updatedPractitioner = await this.getPractitioner(practitionerId);
|
|
709
564
|
if (!updatedPractitioner) {
|
|
710
|
-
throw new Error(
|
|
565
|
+
throw new Error(
|
|
566
|
+
`Failed to retrieve updated practitioner ${practitionerId}`
|
|
567
|
+
);
|
|
711
568
|
}
|
|
712
|
-
|
|
713
569
|
return updatedPractitioner;
|
|
714
570
|
} catch (error) {
|
|
715
571
|
if (error instanceof z.ZodError) {
|
|
716
|
-
throw new Error(
|
|
572
|
+
throw new Error(`Invalid practitioner update data: ${error.message}`);
|
|
717
573
|
}
|
|
574
|
+
console.error(`Error updating practitioner ${practitionerId}:`, error);
|
|
718
575
|
throw error;
|
|
719
576
|
}
|
|
720
577
|
}
|
|
721
578
|
|
|
722
579
|
/**
|
|
723
|
-
*
|
|
580
|
+
* Adds a clinic to a practitioner
|
|
724
581
|
*/
|
|
725
582
|
async addClinic(practitionerId: string, clinicId: string): Promise<void> {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
583
|
+
try {
|
|
584
|
+
// Get practitioner
|
|
585
|
+
const practitionerRef = doc(
|
|
586
|
+
this.db,
|
|
587
|
+
PRACTITIONERS_COLLECTION,
|
|
588
|
+
practitionerId
|
|
589
|
+
);
|
|
590
|
+
const practitionerDoc = await getDoc(practitionerRef);
|
|
730
591
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
592
|
+
if (!practitionerDoc.exists()) {
|
|
593
|
+
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
594
|
+
}
|
|
735
595
|
|
|
736
|
-
|
|
737
|
-
throw new Error("Practitioner is already associated with this clinic");
|
|
738
|
-
}
|
|
596
|
+
const practitioner = practitionerDoc.data() as Practitioner;
|
|
739
597
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
description: clinic.description || "",
|
|
748
|
-
featuredPhoto:
|
|
749
|
-
clinic.featuredPhotos && clinic.featuredPhotos.length > 0
|
|
750
|
-
? clinic.featuredPhotos[0]
|
|
751
|
-
: clinic.coverPhoto || "",
|
|
752
|
-
location: clinic.location,
|
|
753
|
-
contactInfo: clinic.contactInfo,
|
|
754
|
-
};
|
|
755
|
-
|
|
756
|
-
const updatedClinicsInfo = [...practitioner.clinicsInfo, clinicInfo];
|
|
757
|
-
|
|
758
|
-
// Update practitioner with new clinic information
|
|
759
|
-
await this.updatePractitioner(practitionerId, {
|
|
760
|
-
clinics: updatedClinics,
|
|
761
|
-
clinicsInfo: updatedClinicsInfo,
|
|
762
|
-
});
|
|
598
|
+
// Check if clinic already added
|
|
599
|
+
if (practitioner.clinics?.includes(clinicId)) {
|
|
600
|
+
console.log(
|
|
601
|
+
`Clinic ${clinicId} already added to practitioner ${practitionerId}`
|
|
602
|
+
);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
763
605
|
|
|
764
|
-
|
|
765
|
-
|
|
606
|
+
// Add clinic to clinics array
|
|
607
|
+
await updateDoc(practitionerRef, {
|
|
608
|
+
clinics: arrayUnion(clinicId),
|
|
609
|
+
updatedAt: serverTimestamp(),
|
|
610
|
+
});
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.error(
|
|
613
|
+
`Error adding clinic ${clinicId} to practitioner ${practitionerId}:`,
|
|
614
|
+
error
|
|
615
|
+
);
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
766
618
|
}
|
|
767
619
|
|
|
768
620
|
/**
|
|
769
|
-
*
|
|
621
|
+
* Removes a clinic from a practitioner
|
|
770
622
|
*/
|
|
771
623
|
async removeClinic(practitionerId: string, clinicId: string): Promise<void> {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
// Remove clinic from the list
|
|
782
|
-
const updatedClinics = practitioner.clinics.filter((id) => id !== clinicId);
|
|
783
|
-
|
|
784
|
-
// Update clinicsInfo array
|
|
785
|
-
const updatedClinicsInfo = practitioner.clinicsInfo.filter(
|
|
786
|
-
(clinic) => clinic.id !== clinicId
|
|
787
|
-
);
|
|
624
|
+
try {
|
|
625
|
+
// Get practitioner
|
|
626
|
+
const practitionerRef = doc(
|
|
627
|
+
this.db,
|
|
628
|
+
PRACTITIONERS_COLLECTION,
|
|
629
|
+
practitionerId
|
|
630
|
+
);
|
|
631
|
+
const practitionerDoc = await getDoc(practitionerRef);
|
|
788
632
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
);
|
|
633
|
+
if (!practitionerDoc.exists()) {
|
|
634
|
+
throw new Error(`Practitioner ${practitionerId} not found`);
|
|
635
|
+
}
|
|
793
636
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
})
|
|
637
|
+
// Remove clinic from clinics array
|
|
638
|
+
await updateDoc(practitionerRef, {
|
|
639
|
+
clinics: arrayRemove(clinicId),
|
|
640
|
+
updatedAt: serverTimestamp(),
|
|
641
|
+
});
|
|
642
|
+
} catch (error) {
|
|
643
|
+
console.error(
|
|
644
|
+
`Error removing clinic ${clinicId} from practitioner ${practitionerId}:`,
|
|
645
|
+
error
|
|
646
|
+
);
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
800
649
|
}
|
|
801
650
|
|
|
802
651
|
/**
|
|
@@ -1147,129 +996,4 @@ export class PractitionerService extends BaseService {
|
|
|
1147
996
|
throw error;
|
|
1148
997
|
}
|
|
1149
998
|
}
|
|
1150
|
-
|
|
1151
|
-
// --- Helper Functions ---
|
|
1152
|
-
|
|
1153
|
-
/**
|
|
1154
|
-
* Aggregates essential clinic information for embedding in Practitioner.
|
|
1155
|
-
* @param clinicIds Array of clinic IDs the practitioner works at
|
|
1156
|
-
* @returns Array of ClinicInfo objects
|
|
1157
|
-
*/
|
|
1158
|
-
private async _aggregateClinicInfoForPractitioner(
|
|
1159
|
-
clinicIds: string[]
|
|
1160
|
-
): Promise<ClinicInfo[]> {
|
|
1161
|
-
const clinicsInfo: ClinicInfo[] = [];
|
|
1162
|
-
const clinicService = this.getClinicService(); // Get service instance
|
|
1163
|
-
|
|
1164
|
-
for (const clinicId of clinicIds) {
|
|
1165
|
-
try {
|
|
1166
|
-
const clinic = await clinicService.getClinic(clinicId);
|
|
1167
|
-
if (!clinic) {
|
|
1168
|
-
console.warn(
|
|
1169
|
-
`Clinic ${clinicId} not found during practitioner aggregation.`
|
|
1170
|
-
);
|
|
1171
|
-
continue;
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
clinicsInfo.push({
|
|
1175
|
-
id: clinic.id,
|
|
1176
|
-
featuredPhoto:
|
|
1177
|
-
clinic.featuredPhotos && clinic.featuredPhotos.length > 0
|
|
1178
|
-
? clinic.featuredPhotos[0]
|
|
1179
|
-
: clinic.coverPhoto || "",
|
|
1180
|
-
name: clinic.name,
|
|
1181
|
-
description: clinic.description || "",
|
|
1182
|
-
location: clinic.location,
|
|
1183
|
-
contactInfo: clinic.contactInfo,
|
|
1184
|
-
});
|
|
1185
|
-
} catch (error) {
|
|
1186
|
-
console.error(
|
|
1187
|
-
`Error fetching clinic ${clinicId} for practitioner aggregation:`,
|
|
1188
|
-
error
|
|
1189
|
-
);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
return clinicsInfo;
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
/**
|
|
1196
|
-
* Creates an aggregated DoctorInfo object from Practitioner data.
|
|
1197
|
-
* @param practitioner The practitioner object
|
|
1198
|
-
* @returns DoctorInfo object
|
|
1199
|
-
*/
|
|
1200
|
-
private _createDoctorInfoForClinic(practitioner: Practitioner): DoctorInfo {
|
|
1201
|
-
return {
|
|
1202
|
-
id: practitioner.id,
|
|
1203
|
-
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
1204
|
-
description: practitioner.basicInfo.bio || "",
|
|
1205
|
-
photo: practitioner.basicInfo.profileImageUrl || "",
|
|
1206
|
-
rating: practitioner.reviewInfo?.averageRating || 0,
|
|
1207
|
-
services: practitioner.procedures || [], // List of procedure IDs
|
|
1208
|
-
};
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
/**
|
|
1212
|
-
* Updates the DoctorInfo within the doctorsInfo array for multiple clinics.
|
|
1213
|
-
* @param clinicIds IDs of clinics to update
|
|
1214
|
-
* @param doctorInfo The updated DoctorInfo object
|
|
1215
|
-
*/
|
|
1216
|
-
private async _updateDoctorInfoInClinics(
|
|
1217
|
-
clinicIds: string[],
|
|
1218
|
-
doctorInfo: DoctorInfo
|
|
1219
|
-
): Promise<void> {
|
|
1220
|
-
const batch = writeBatch(this.db);
|
|
1221
|
-
const practitionerId = doctorInfo.id;
|
|
1222
|
-
|
|
1223
|
-
for (const clinicId of clinicIds) {
|
|
1224
|
-
const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
|
|
1225
|
-
// First, remove the old doctor info based on ID
|
|
1226
|
-
batch.update(clinicRef, {
|
|
1227
|
-
doctorsInfo: arrayRemove(...[{ id: practitionerId }]),
|
|
1228
|
-
updatedAt: serverTimestamp(),
|
|
1229
|
-
});
|
|
1230
|
-
// Then, add the updated doctor info
|
|
1231
|
-
batch.update(clinicRef, {
|
|
1232
|
-
doctorsInfo: arrayUnion(doctorInfo),
|
|
1233
|
-
updatedAt: serverTimestamp(),
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
try {
|
|
1237
|
-
await batch.commit();
|
|
1238
|
-
} catch (error) {
|
|
1239
|
-
console.error(
|
|
1240
|
-
`Error updating doctor info in clinics for practitioner ${practitionerId}:`,
|
|
1241
|
-
error
|
|
1242
|
-
);
|
|
1243
|
-
// Decide on error handling: throw, retry, log?
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
/**
|
|
1248
|
-
* Removes DoctorInfo from the doctorsInfo array for multiple clinics.
|
|
1249
|
-
* @param clinicIds IDs of clinics to update
|
|
1250
|
-
* @param practitionerId ID of the practitioner whose info should be removed
|
|
1251
|
-
*/
|
|
1252
|
-
private async _removeDoctorInfoFromClinics(
|
|
1253
|
-
clinicIds: string[],
|
|
1254
|
-
practitionerId: string
|
|
1255
|
-
): Promise<void> {
|
|
1256
|
-
const batch = writeBatch(this.db);
|
|
1257
|
-
|
|
1258
|
-
for (const clinicId of clinicIds) {
|
|
1259
|
-
const clinicRef = doc(this.db, CLINICS_COLLECTION, clinicId);
|
|
1260
|
-
batch.update(clinicRef, {
|
|
1261
|
-
doctors: arrayRemove(practitionerId), // Also remove from simple ID list
|
|
1262
|
-
doctorsInfo: arrayRemove(...[{ id: practitionerId }]), // Remove by ID matcher
|
|
1263
|
-
updatedAt: serverTimestamp(),
|
|
1264
|
-
});
|
|
1265
|
-
}
|
|
1266
|
-
try {
|
|
1267
|
-
await batch.commit();
|
|
1268
|
-
} catch (error) {
|
|
1269
|
-
console.error(
|
|
1270
|
-
`Error removing doctor info from clinics for practitioner ${practitionerId}:`,
|
|
1271
|
-
error
|
|
1272
|
-
);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
999
|
}
|