@blackcode_sa/metaestetics-api 1.7.19 → 1.7.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 +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/backoffice/index.d.mts +6 -1
- package/dist/backoffice/index.d.ts +6 -1
- package/dist/index.d.mts +41 -26
- package/dist/index.d.ts +41 -26
- package/dist/index.js +1240 -1177
- package/dist/index.mjs +1229 -1166
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +4 -1
- package/src/services/practitioner/practitioner.service.ts +92 -3
- package/src/services/procedure/procedure.service.ts +8 -2
- package/src/types/practitioner/index.ts +2 -1
- package/src/validations/media.schema.ts +1 -1
- package/src/validations/practitioner.schema.ts +3 -2
package/package.json
CHANGED
|
@@ -613,7 +613,10 @@ export class BookingAdmin {
|
|
|
613
613
|
};
|
|
614
614
|
const practitionerInfo: PractitionerProfileInfo = {
|
|
615
615
|
id: practitionerSnap.id,
|
|
616
|
-
practitionerPhoto:
|
|
616
|
+
practitionerPhoto:
|
|
617
|
+
typeof practitionerData.basicInfo.profileImageUrl === "string"
|
|
618
|
+
? practitionerData.basicInfo.profileImageUrl
|
|
619
|
+
: null,
|
|
617
620
|
name: `${practitionerData.basicInfo.firstName} ${practitionerData.basicInfo.lastName}`,
|
|
618
621
|
email: practitionerData.basicInfo.email,
|
|
619
622
|
phone: practitionerData.basicInfo.phoneNumber || null,
|
|
@@ -34,6 +34,11 @@ import {
|
|
|
34
34
|
} from "../../types/practitioner";
|
|
35
35
|
import { ProcedureSummaryInfo } from "../../types/procedure";
|
|
36
36
|
import { ClinicService } from "../clinic/clinic.service";
|
|
37
|
+
import {
|
|
38
|
+
MediaService,
|
|
39
|
+
MediaAccessLevel,
|
|
40
|
+
MediaResource,
|
|
41
|
+
} from "../media/media.service";
|
|
37
42
|
import {
|
|
38
43
|
practitionerSchema,
|
|
39
44
|
createPractitionerSchema,
|
|
@@ -53,6 +58,7 @@ import { ClinicInfo } from "../../types/profile";
|
|
|
53
58
|
|
|
54
59
|
export class PractitionerService extends BaseService {
|
|
55
60
|
private clinicService?: ClinicService;
|
|
61
|
+
private mediaService: MediaService;
|
|
56
62
|
|
|
57
63
|
constructor(
|
|
58
64
|
db: Firestore,
|
|
@@ -62,6 +68,7 @@ export class PractitionerService extends BaseService {
|
|
|
62
68
|
) {
|
|
63
69
|
super(db, auth, app);
|
|
64
70
|
this.clinicService = clinicService;
|
|
71
|
+
this.mediaService = new MediaService(db, auth, app);
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
private getClinicService(): ClinicService {
|
|
@@ -75,6 +82,71 @@ export class PractitionerService extends BaseService {
|
|
|
75
82
|
this.clinicService = clinicService;
|
|
76
83
|
}
|
|
77
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Handles profile photo upload for practitioners
|
|
87
|
+
* @param profilePhoto - MediaResource (File, Blob, or URL string)
|
|
88
|
+
* @param practitionerId - ID of the practitioner
|
|
89
|
+
* @returns URL string of the uploaded or existing photo
|
|
90
|
+
*/
|
|
91
|
+
private async handleProfilePhotoUpload(
|
|
92
|
+
profilePhoto: MediaResource | undefined,
|
|
93
|
+
practitionerId: string
|
|
94
|
+
): Promise<string | undefined> {
|
|
95
|
+
if (!profilePhoto) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If it's already a URL string, return it as is
|
|
100
|
+
if (typeof profilePhoto === "string") {
|
|
101
|
+
return profilePhoto;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If it's a File or Blob, upload it
|
|
105
|
+
if (profilePhoto instanceof File || profilePhoto instanceof Blob) {
|
|
106
|
+
console.log(
|
|
107
|
+
`[PractitionerService] Uploading profile photo for practitioner ${practitionerId}`
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const mediaMetadata = await this.mediaService.uploadMedia(
|
|
111
|
+
profilePhoto,
|
|
112
|
+
practitionerId, // Using practitionerId as ownerId
|
|
113
|
+
MediaAccessLevel.PUBLIC, // Profile photos should be public
|
|
114
|
+
"practitioner_profile_photos",
|
|
115
|
+
profilePhoto instanceof File
|
|
116
|
+
? profilePhoto.name
|
|
117
|
+
: `profile_photo_${practitionerId}`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return mediaMetadata.url;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Processes BasicPractitionerInfo to handle profile photo uploads
|
|
128
|
+
* @param basicInfo - The basic info containing potential MediaResource profile photo
|
|
129
|
+
* @param practitionerId - ID of the practitioner
|
|
130
|
+
* @returns Processed basic info with URL string for profileImageUrl
|
|
131
|
+
*/
|
|
132
|
+
private async processBasicInfo(
|
|
133
|
+
basicInfo: PractitionerBasicInfo & { profileImageUrl?: MediaResource },
|
|
134
|
+
practitionerId: string
|
|
135
|
+
): Promise<PractitionerBasicInfo> {
|
|
136
|
+
const processedBasicInfo = { ...basicInfo };
|
|
137
|
+
|
|
138
|
+
// Handle profile photo upload if needed
|
|
139
|
+
if (basicInfo.profileImageUrl) {
|
|
140
|
+
const uploadedUrl = await this.handleProfilePhotoUpload(
|
|
141
|
+
basicInfo.profileImageUrl,
|
|
142
|
+
practitionerId
|
|
143
|
+
);
|
|
144
|
+
processedBasicInfo.profileImageUrl = uploadedUrl;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return processedBasicInfo;
|
|
148
|
+
}
|
|
149
|
+
|
|
78
150
|
/**
|
|
79
151
|
* Creates a new practitioner
|
|
80
152
|
*/
|
|
@@ -104,7 +176,10 @@ export class PractitionerService extends BaseService {
|
|
|
104
176
|
} = {
|
|
105
177
|
id: practitionerId,
|
|
106
178
|
userRef: validData.userRef,
|
|
107
|
-
basicInfo:
|
|
179
|
+
basicInfo: await this.processBasicInfo(
|
|
180
|
+
validData.basicInfo,
|
|
181
|
+
practitionerId
|
|
182
|
+
),
|
|
108
183
|
certification: validData.certification,
|
|
109
184
|
clinics: validData.clinics || [],
|
|
110
185
|
clinicWorkingHours: validData.clinicWorkingHours || [],
|
|
@@ -254,7 +329,10 @@ export class PractitionerService extends BaseService {
|
|
|
254
329
|
} = {
|
|
255
330
|
id: practitionerId,
|
|
256
331
|
userRef: "", // Prazno - biće popunjeno kada korisnik kreira nalog
|
|
257
|
-
basicInfo:
|
|
332
|
+
basicInfo: await this.processBasicInfo(
|
|
333
|
+
validatedData.basicInfo,
|
|
334
|
+
practitionerId
|
|
335
|
+
),
|
|
258
336
|
certification: validatedData.certification,
|
|
259
337
|
clinics: clinics,
|
|
260
338
|
clinicWorkingHours: validatedData.clinicWorkingHours || [],
|
|
@@ -619,9 +697,20 @@ export class PractitionerService extends BaseService {
|
|
|
619
697
|
|
|
620
698
|
const currentPractitioner = practitionerDoc.data() as Practitioner;
|
|
621
699
|
|
|
700
|
+
// Process basicInfo if it's being updated to handle profile photo uploads
|
|
701
|
+
let processedData = { ...validData };
|
|
702
|
+
if (validData.basicInfo) {
|
|
703
|
+
processedData.basicInfo = await this.processBasicInfo(
|
|
704
|
+
validData.basicInfo as PractitionerBasicInfo & {
|
|
705
|
+
profileImageUrl?: MediaResource;
|
|
706
|
+
},
|
|
707
|
+
practitionerId
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
622
711
|
// Prepare update data
|
|
623
712
|
const updateData = {
|
|
624
|
-
...
|
|
713
|
+
...processedData,
|
|
625
714
|
updatedAt: serverTimestamp(),
|
|
626
715
|
};
|
|
627
716
|
|
|
@@ -251,7 +251,10 @@ export class ProcedureService extends BaseService {
|
|
|
251
251
|
id: practitionerSnapshot.id,
|
|
252
252
|
name: `${practitioner.basicInfo.firstName} ${practitioner.basicInfo.lastName}`,
|
|
253
253
|
description: practitioner.basicInfo.bio || "",
|
|
254
|
-
photo:
|
|
254
|
+
photo:
|
|
255
|
+
typeof practitioner.basicInfo.profileImageUrl === "string"
|
|
256
|
+
? practitioner.basicInfo.profileImageUrl
|
|
257
|
+
: "", // Default to empty string if not a processed URL
|
|
255
258
|
rating: practitioner.reviewInfo?.averageRating || 0,
|
|
256
259
|
services: practitioner.procedures || [],
|
|
257
260
|
};
|
|
@@ -412,7 +415,10 @@ export class ProcedureService extends BaseService {
|
|
|
412
415
|
id: newPractitioner.id,
|
|
413
416
|
name: `${newPractitioner.basicInfo.firstName} ${newPractitioner.basicInfo.lastName}`,
|
|
414
417
|
description: newPractitioner.basicInfo.bio || "",
|
|
415
|
-
photo:
|
|
418
|
+
photo:
|
|
419
|
+
typeof newPractitioner.basicInfo.profileImageUrl === "string"
|
|
420
|
+
? newPractitioner.basicInfo.profileImageUrl
|
|
421
|
+
: "", // Default to empty string if not a processed URL
|
|
416
422
|
rating: newPractitioner.reviewInfo?.averageRating || 0,
|
|
417
423
|
services: newPractitioner.procedures || [],
|
|
418
424
|
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
PricingMeasure,
|
|
12
12
|
} from "../../backoffice/types/static/pricing.types";
|
|
13
13
|
import { ProcedureSummaryInfo } from "../procedure";
|
|
14
|
+
import type { MediaResource } from "../../services/media/media.service";
|
|
14
15
|
|
|
15
16
|
export const PRACTITIONERS_COLLECTION = "practitioners";
|
|
16
17
|
export const REGISTER_TOKENS_COLLECTION = "register_tokens";
|
|
@@ -26,7 +27,7 @@ export interface PractitionerBasicInfo {
|
|
|
26
27
|
phoneNumber: string;
|
|
27
28
|
dateOfBirth: Timestamp | Date;
|
|
28
29
|
gender: "male" | "female" | "other";
|
|
29
|
-
profileImageUrl?:
|
|
30
|
+
profileImageUrl?: MediaResource;
|
|
30
31
|
bio?: string;
|
|
31
32
|
languages: string[];
|
|
32
33
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
PricingMeasure,
|
|
16
16
|
} from "../backoffice/types/static/pricing.types";
|
|
17
17
|
import { clinicInfoSchema, procedureSummaryInfoSchema } from "./shared.schema";
|
|
18
|
+
import { mediaResourceSchema } from "./media.schema";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Šema za validaciju osnovnih informacija o zdravstvenom radniku
|
|
@@ -27,7 +28,7 @@ export const practitionerBasicInfoSchema = z.object({
|
|
|
27
28
|
phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/, "Invalid phone number"),
|
|
28
29
|
dateOfBirth: z.instanceof(Timestamp).or(z.date()),
|
|
29
30
|
gender: z.enum(["male", "female", "other"]),
|
|
30
|
-
profileImageUrl:
|
|
31
|
+
profileImageUrl: mediaResourceSchema.optional(),
|
|
31
32
|
bio: z.string().max(1000).optional(),
|
|
32
33
|
languages: z.array(z.string()).min(1),
|
|
33
34
|
});
|
|
@@ -204,7 +205,7 @@ export const practitionerSignupSchema = z.object({
|
|
|
204
205
|
basicInfo: z
|
|
205
206
|
.object({
|
|
206
207
|
phoneNumber: z.string().optional(),
|
|
207
|
-
profileImageUrl:
|
|
208
|
+
profileImageUrl: mediaResourceSchema.optional(),
|
|
208
209
|
gender: z.enum(["male", "female", "other"]).optional(),
|
|
209
210
|
bio: z.string().optional(),
|
|
210
211
|
})
|