@blackcode_sa/metaestetics-api 1.7.27 → 1.7.29

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.
@@ -1001,4 +1001,145 @@ export class ProcedureService extends BaseService {
1001
1001
 
1002
1002
  return filteredProcedures;
1003
1003
  }
1004
+
1005
+ /**
1006
+ * Creates a consultation procedure without requiring a product
1007
+ * This is a special method for consultation procedures that don't use products
1008
+ * @param data - The data for creating a consultation procedure (without productId)
1009
+ * @returns The created procedure
1010
+ */
1011
+ async createConsultationProcedure(
1012
+ data: Omit<CreateProcedureData, "productId">
1013
+ ): Promise<Procedure> {
1014
+ // Generate procedure ID first so we can use it for media uploads
1015
+ const procedureId = this.generateId();
1016
+
1017
+ // Get references to related entities (Category, Subcategory, Technology)
1018
+ // For consultation, we don't need a product
1019
+ const [category, subcategory, technology] = await Promise.all([
1020
+ this.categoryService.getById(data.categoryId),
1021
+ this.subcategoryService.getById(data.categoryId, data.subcategoryId),
1022
+ this.technologyService.getById(data.technologyId),
1023
+ ]);
1024
+
1025
+ if (!category || !subcategory || !technology) {
1026
+ throw new Error("One or more required base entities not found");
1027
+ }
1028
+
1029
+ // Get clinic and practitioner information for aggregation
1030
+ const clinicRef = doc(this.db, CLINICS_COLLECTION, data.clinicBranchId);
1031
+ const clinicSnapshot = await getDoc(clinicRef);
1032
+ if (!clinicSnapshot.exists()) {
1033
+ throw new Error(`Clinic with ID ${data.clinicBranchId} not found`);
1034
+ }
1035
+ const clinic = clinicSnapshot.data() as Clinic;
1036
+
1037
+ const practitionerRef = doc(
1038
+ this.db,
1039
+ PRACTITIONERS_COLLECTION,
1040
+ data.practitionerId
1041
+ );
1042
+ const practitionerSnapshot = await getDoc(practitionerRef);
1043
+ if (!practitionerSnapshot.exists()) {
1044
+ throw new Error(`Practitioner with ID ${data.practitionerId} not found`);
1045
+ }
1046
+ const practitioner = practitionerSnapshot.data() as Practitioner;
1047
+
1048
+ // Process photos if provided
1049
+ let processedPhotos: string[] = [];
1050
+ if (data.photos && data.photos.length > 0) {
1051
+ processedPhotos = await this.processMediaArray(
1052
+ data.photos,
1053
+ procedureId,
1054
+ "procedure-photos"
1055
+ );
1056
+ }
1057
+
1058
+ // Create aggregated clinic info for the procedure document
1059
+ const clinicInfo = {
1060
+ id: clinicSnapshot.id,
1061
+ name: clinic.name,
1062
+ description: clinic.description || "",
1063
+ featuredPhoto:
1064
+ clinic.featuredPhotos && clinic.featuredPhotos.length > 0
1065
+ ? typeof clinic.featuredPhotos[0] === "string"
1066
+ ? clinic.featuredPhotos[0]
1067
+ : ""
1068
+ : typeof clinic.coverPhoto === "string"
1069
+ ? clinic.coverPhoto
1070
+ : "",
1071
+ location: clinic.location,
1072
+ contactInfo: clinic.contactInfo,
1073
+ };
1074
+
1075
+ // Create aggregated doctor info for the procedure document
1076
+ const doctorInfo = {
1077
+ id: practitionerSnapshot.id,
1078
+ name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
1079
+ description: practitioner.basicInfo.bio || "",
1080
+ photo:
1081
+ typeof practitioner.basicInfo.profileImageUrl === "string"
1082
+ ? practitioner.basicInfo.profileImageUrl
1083
+ : "",
1084
+ rating: practitioner.reviewInfo?.averageRating || 0,
1085
+ services: practitioner.procedures || [],
1086
+ };
1087
+
1088
+ // Create a placeholder product for consultation procedures
1089
+ const consultationProduct: Product = {
1090
+ id: "consultation-no-product",
1091
+ name: "No Product Required",
1092
+ description: "Consultation procedures do not require specific products",
1093
+ brandId: "consultation-brand",
1094
+ brandName: "Consultation",
1095
+ technologyId: data.technologyId,
1096
+ technologyName: technology.name,
1097
+ isActive: true,
1098
+ createdAt: new Date(),
1099
+ updatedAt: new Date(),
1100
+ };
1101
+
1102
+ // Create the procedure object
1103
+ const newProcedure: Omit<Procedure, "createdAt" | "updatedAt"> = {
1104
+ id: procedureId,
1105
+ ...data,
1106
+ photos: processedPhotos,
1107
+ category,
1108
+ subcategory,
1109
+ technology,
1110
+ product: consultationProduct, // Use placeholder product
1111
+ blockingConditions: technology.blockingConditions,
1112
+ contraindications: technology.contraindications || [],
1113
+ treatmentBenefits: technology.benefits,
1114
+ preRequirements: technology.requirements.pre,
1115
+ postRequirements: technology.requirements.post,
1116
+ certificationRequirement: technology.certificationRequirement,
1117
+ documentationTemplates: technology?.documentationTemplates || [],
1118
+ clinicInfo,
1119
+ doctorInfo,
1120
+ reviewInfo: {
1121
+ totalReviews: 0,
1122
+ averageRating: 0,
1123
+ effectivenessOfTreatment: 0,
1124
+ outcomeExplanation: 0,
1125
+ painManagement: 0,
1126
+ followUpCare: 0,
1127
+ valueForMoney: 0,
1128
+ recommendationPercentage: 0,
1129
+ },
1130
+ isActive: true,
1131
+ };
1132
+
1133
+ // Create the procedure document
1134
+ const procedureRef = doc(this.db, PROCEDURES_COLLECTION, procedureId);
1135
+ await setDoc(procedureRef, {
1136
+ ...newProcedure,
1137
+ createdAt: serverTimestamp(),
1138
+ updatedAt: serverTimestamp(),
1139
+ });
1140
+
1141
+ // Return the created procedure (fetch again to get server timestamps)
1142
+ const savedDoc = await getDoc(procedureRef);
1143
+ return savedDoc.data() as Procedure;
1144
+ }
1004
1145
  }
@@ -94,6 +94,7 @@ export interface Practitioner {
94
94
  clinicWorkingHours: PractitionerClinicWorkingHours[]; // Radno vreme za svaku kliniku
95
95
  clinicsInfo: ClinicInfo[]; // Aggregated clinic information
96
96
  procedures: string[]; // Reference na procedure koje izvodi
97
+ freeConsultations?: Record<string, string> | null; // Map of clinic IDs to procedure ID for free consultations (one per clinic)
97
98
  proceduresInfo: ProcedureSummaryInfo[]; // Aggregated procedure information
98
99
  reviewInfo: PractitionerReviewInfo; // Aggregated review information
99
100
  isActive: boolean;
@@ -113,6 +114,7 @@ export interface CreatePractitionerData {
113
114
  clinics?: string[];
114
115
  clinicWorkingHours?: PractitionerClinicWorkingHours[];
115
116
  clinicsInfo?: ClinicInfo[];
117
+ freeConsultations?: Record<string, string> | null;
116
118
  isActive: boolean;
117
119
  isVerified: boolean;
118
120
  status?: PractitionerStatus;
@@ -127,6 +129,7 @@ export interface CreateDraftPractitionerData {
127
129
  clinics?: string[];
128
130
  clinicWorkingHours?: PractitionerClinicWorkingHours[];
129
131
  clinicsInfo?: ClinicInfo[];
132
+ freeConsultations?: Record<string, string> | null;
130
133
  isActive?: boolean;
131
134
  isVerified?: boolean;
132
135
  }
@@ -125,6 +125,7 @@ export const practitionerSchema = z.object({
125
125
  clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema),
126
126
  clinicsInfo: z.array(clinicInfoSchema),
127
127
  procedures: z.array(z.string()),
128
+ freeConsultations: z.record(z.string(), z.string()).optional().nullable(),
128
129
  proceduresInfo: z.array(procedureSummaryInfoSchema),
129
130
  reviewInfo: practitionerReviewInfoSchema,
130
131
  isActive: z.boolean(),
@@ -144,6 +145,7 @@ export const createPractitionerSchema = z.object({
144
145
  clinics: z.array(z.string()).optional(),
145
146
  clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
146
147
  clinicsInfo: z.array(clinicInfoSchema).optional(),
148
+ freeConsultations: z.record(z.string(), z.string()).optional().nullable(),
147
149
  proceduresInfo: z.array(procedureSummaryInfoSchema).optional(),
148
150
  isActive: z.boolean(),
149
151
  isVerified: z.boolean(),
@@ -159,6 +161,7 @@ export const createDraftPractitionerSchema = z.object({
159
161
  clinics: z.array(z.string()).optional(),
160
162
  clinicWorkingHours: z.array(practitionerClinicWorkingHoursSchema).optional(),
161
163
  clinicsInfo: z.array(clinicInfoSchema).optional(),
164
+ freeConsultations: z.record(z.string(), z.string()).optional().nullable(),
162
165
  proceduresInfo: z.array(procedureSummaryInfoSchema).optional(),
163
166
  isActive: z.boolean().optional().default(false),
164
167
  isVerified: z.boolean().optional().default(false),