@blackcode_sa/metaestetics-api 1.5.52 → 1.6.1

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/index.d.mts CHANGED
@@ -5326,6 +5326,36 @@ declare class AuthService extends BaseService {
5326
5326
  * @returns Promise koji se razrešava kada je lozinka promenjena
5327
5327
  */
5328
5328
  confirmPasswordReset(oobCode: string, newPassword: string): Promise<void>;
5329
+ /**
5330
+ * Registers a new practitioner user with email and password
5331
+ * Can either create a new practitioner profile or claim an existing draft profile with a token
5332
+ *
5333
+ * @param data - Practitioner signup data containing either new profile details or token for claiming draft profile
5334
+ * @returns Object containing the created user and practitioner profile
5335
+ */
5336
+ signUpPractitioner(data: {
5337
+ email: string;
5338
+ password: string;
5339
+ firstName: string;
5340
+ lastName: string;
5341
+ token?: string;
5342
+ profileData?: Partial<CreatePractitionerData>;
5343
+ }): Promise<{
5344
+ user: User;
5345
+ practitioner: Practitioner;
5346
+ }>;
5347
+ /**
5348
+ * Signs in a user with email and password specifically for practitioner role
5349
+ * @param email - User's email
5350
+ * @param password - User's password
5351
+ * @returns Object containing the user and practitioner profile
5352
+ * @throws {AUTH_ERRORS.INVALID_ROLE} If user doesn't have practitioner role
5353
+ * @throws {AUTH_ERRORS.NOT_FOUND} If practitioner profile is not found
5354
+ */
5355
+ signInPractitioner(email: string, password: string): Promise<{
5356
+ user: User;
5357
+ practitioner: Practitioner;
5358
+ }>;
5329
5359
  }
5330
5360
 
5331
5361
  /**
@@ -12052,6 +12082,81 @@ declare const createPractitionerTokenSchema: z.ZodObject<{
12052
12082
  clinicId: string;
12053
12083
  expiresAt?: Date | undefined;
12054
12084
  }>;
12085
+ /**
12086
+ * Simplified schema for practitioner signup
12087
+ */
12088
+ declare const practitionerSignupSchema: z.ZodObject<{
12089
+ email: z.ZodString;
12090
+ password: z.ZodString;
12091
+ firstName: z.ZodString;
12092
+ lastName: z.ZodString;
12093
+ token: z.ZodOptional<z.ZodString>;
12094
+ profileData: z.ZodOptional<z.ZodObject<{
12095
+ basicInfo: z.ZodOptional<z.ZodObject<{
12096
+ phoneNumber: z.ZodOptional<z.ZodString>;
12097
+ profileImageUrl: z.ZodOptional<z.ZodString>;
12098
+ gender: z.ZodOptional<z.ZodEnum<["male", "female", "other"]>>;
12099
+ bio: z.ZodOptional<z.ZodString>;
12100
+ }, "strip", z.ZodTypeAny, {
12101
+ gender?: "male" | "female" | "other" | undefined;
12102
+ phoneNumber?: string | undefined;
12103
+ profileImageUrl?: string | undefined;
12104
+ bio?: string | undefined;
12105
+ }, {
12106
+ gender?: "male" | "female" | "other" | undefined;
12107
+ phoneNumber?: string | undefined;
12108
+ profileImageUrl?: string | undefined;
12109
+ bio?: string | undefined;
12110
+ }>>;
12111
+ certification: z.ZodOptional<z.ZodAny>;
12112
+ }, "strip", z.ZodTypeAny, {
12113
+ basicInfo?: {
12114
+ gender?: "male" | "female" | "other" | undefined;
12115
+ phoneNumber?: string | undefined;
12116
+ profileImageUrl?: string | undefined;
12117
+ bio?: string | undefined;
12118
+ } | undefined;
12119
+ certification?: any;
12120
+ }, {
12121
+ basicInfo?: {
12122
+ gender?: "male" | "female" | "other" | undefined;
12123
+ phoneNumber?: string | undefined;
12124
+ profileImageUrl?: string | undefined;
12125
+ bio?: string | undefined;
12126
+ } | undefined;
12127
+ certification?: any;
12128
+ }>>;
12129
+ }, "strip", z.ZodTypeAny, {
12130
+ firstName: string;
12131
+ lastName: string;
12132
+ email: string;
12133
+ password: string;
12134
+ token?: string | undefined;
12135
+ profileData?: {
12136
+ basicInfo?: {
12137
+ gender?: "male" | "female" | "other" | undefined;
12138
+ phoneNumber?: string | undefined;
12139
+ profileImageUrl?: string | undefined;
12140
+ bio?: string | undefined;
12141
+ } | undefined;
12142
+ certification?: any;
12143
+ } | undefined;
12144
+ }, {
12145
+ firstName: string;
12146
+ lastName: string;
12147
+ email: string;
12148
+ password: string;
12149
+ token?: string | undefined;
12150
+ profileData?: {
12151
+ basicInfo?: {
12152
+ gender?: "male" | "female" | "other" | undefined;
12153
+ phoneNumber?: string | undefined;
12154
+ profileImageUrl?: string | undefined;
12155
+ bio?: string | undefined;
12156
+ } | undefined;
12157
+ certification?: any;
12158
+ } | undefined;
12159
+ }>;
12055
12160
 
12056
12161
  /**
12057
12162
  * Validaciona šema za kontakt informacije
@@ -18192,4 +18297,4 @@ declare const createReviewSchema: z.ZodEffects<z.ZodObject<{
18192
18297
  } | undefined;
18193
18298
  }>;
18194
18299
 
18195
- export { APPOINTMENTS_COLLECTION, AUTH_ERRORS, type AddAllergyData, type AddBlockingConditionData, type AddContraindicationData, type AddMedicationData, type AddressData, type AdminInfo, type AdminToken, AdminTokenStatus, type Allergy, type AllergySubtype, AllergyType, type AllergyTypeWithSubtype, type Appointment, type AppointmentNotification, type AppointmentReminderNotification, AppointmentService, AppointmentStatus, AuthError, AuthService, type BaseNotification, BlockingCondition, type Brand, BrandService, CALENDAR_COLLECTION, CLINICS_COLLECTION, CLINIC_ADMINS_COLLECTION, CLINIC_GROUPS_COLLECTION, type CalendarEvent, CalendarEventStatus, type CalendarEventTime, CalendarEventType, CalendarServiceV2, CalendarSyncStatus, type Category, CategoryService, CertificationLevel, CertificationSpecialty, type Clinic, type ClinicAdmin, ClinicAdminService, type ClinicAdminSignupData, type ClinicBranchSetupData, type ClinicContactInfo, type ClinicGroup, ClinicGroupService, type ClinicGroupSetupData, type ClinicInfo, type ClinicLocation, ClinicPhotoTag, type ClinicReview, type ClinicReviewInfo, ClinicService, ClinicTag, type ClinicTags, type ContactPerson, Contraindication, CosmeticAllergySubtype, type CreateAdminTokenData, type CreateAppointmentData, type CreateAppointmentParams, type CreateCalendarEventData, type CreateClinicAdminData, type CreateClinicData, type CreateClinicGroupData, type CreateDefaultClinicGroupData, type CreateDocumentTemplateData, type CreateDraftPractitionerData, type CreatePatientLocationInfoData, type CreatePatientMedicalInfoData, type CreatePatientProfileData, type CreatePatientSensitiveInfoData, type CreatePractitionerData, type CreatePractitionerTokenData, type CreateProcedureData, type CreateSyncedCalendarData, type CreateUserData, Currency, DOCUMENTATION_TEMPLATES_COLLECTION, type DateRange, type DoctorInfo, type DocumentElement, DocumentElementType, type DocumentTemplate, DocumentationTemplateService, DynamicVariable, type EmergencyContact, EnvironmentalAllergySubtype, FILLED_DOCUMENTS_COLLECTION, type FilledDocument, FilledDocumentService, FilledDocumentStatus, FirebaseErrorCode, type FirebaseUser, FoodAllergySubtype, type GamificationInfo, Gender, HeadingLevel, Language, ListType, type LocationData, MedicationAllergySubtype, type Notification, NotificationService, NotificationStatus, NotificationType, PATIENTS_COLLECTION, PATIENT_APPOINTMENTS_COLLECTION, PATIENT_LOCATION_INFO_COLLECTION, PATIENT_MEDICAL_HISTORY_COLLECTION, PATIENT_MEDICAL_INFO_COLLECTION, PATIENT_SENSITIVE_INFO_COLLECTION, PRACTITIONERS_COLLECTION, PROCEDURES_COLLECTION, type PatientClinic, type PatientDoctor, type PatientLocationInfo, type PatientMedicalInfo, type PatientProfile, type PatientProfileComplete, type PatientProfileInfo, type PatientSensitiveInfo, PatientService, PaymentStatus, type PostRequirementNotification, PracticeType, type Practitioner, type PractitionerBasicInfo, type PractitionerCertification, type PractitionerClinicProcedures, type PractitionerClinicWorkingHours, type PractitionerProfileInfo, type PractitionerReview, type PractitionerReviewInfo, PractitionerService, PractitionerStatus, type PractitionerToken, PractitionerTokenStatus, type PractitionerWorkingHours, type PreRequirementNotification, PricingMeasure, type Procedure, type ProcedureCategorization, ProcedureFamily, type ProcedureInfo, type ProcedureReview, type ProcedureReviewInfo, ProcedureService, type Product, ProductService, REGISTER_TOKENS_COLLECTION, REVIEWS_COLLECTION, type RequesterInfo, type Requirement, RequirementType, type Review, ReviewService, SYNCED_CALENDARS_COLLECTION, type SearchAppointmentsParams, type SearchCalendarEventsParams, SearchLocationEnum, type SearchPatientsParams, type Subcategory, SubcategoryService, SubscriptionModel, type SyncedCalendar, type SyncedCalendarEvent, SyncedCalendarProvider, SyncedCalendarsService, type Technology, TechnologyService, type TimeSlot, TimeUnit, TreatmentBenefit, USER_ERRORS, type UpdateAllergyData, type UpdateAppointmentData, type UpdateAppointmentParams, type UpdateBlockingConditionData, type UpdateCalendarEventData, type UpdateClinicAdminData, type UpdateClinicData, type UpdateClinicGroupData, type UpdateContraindicationData, type UpdateDocumentTemplateData, type UpdateMedicationData, type UpdatePatientLocationInfoData, type UpdatePatientMedicalInfoData, type UpdatePatientProfileData, type UpdatePatientSensitiveInfoData, type UpdatePractitionerData, type UpdateProcedureData, type UpdateSyncedCalendarData, type UpdateVitalStatsData, type User, UserRole, UserService, type ValidationSchema, type VitalStats, type WorkingHours, addAllergySchema, addBlockingConditionSchema, addContraindicationSchema, addMedicationSchema, addressDataSchema, adminInfoSchema, adminTokenSchema, allergySchema, allergySubtypeSchema, appointmentNotificationSchema, appointmentReminderNotificationSchema, baseNotificationSchema, blockingConditionSchema, calendarEventSchema, calendarEventTimeSchema, clinicAdminOptionsSchema, clinicAdminSchema, clinicAdminSignupSchema, clinicBranchSetupSchema, clinicContactInfoSchema, clinicGroupSchema, clinicGroupSetupSchema, clinicInfoSchema, clinicLocationSchema, clinicReviewInfoSchema, clinicReviewSchema, clinicSchema, clinicTagsSchema, contactPersonSchema, contraindicationSchema, createAdminTokenSchema, createAppointmentSchema, createCalendarEventSchema, createClinicAdminSchema, createClinicGroupSchema, createClinicReviewSchema, createClinicSchema, createDefaultClinicGroupSchema, createDocumentTemplateSchema, createDraftPractitionerSchema, createPatientLocationInfoSchema, createPatientMedicalInfoSchema, createPatientProfileSchema, createPatientSensitiveInfoSchema, createPractitionerReviewSchema, createPractitionerSchema, createPractitionerTokenSchema, createProcedureReviewSchema, createReviewSchema, createUserOptionsSchema, doctorInfoSchema, documentElementSchema, documentElementWithoutIdSchema, documentTemplateSchema, emailSchema, emergencyContactSchema, gamificationSchema, getFirebaseApp, getFirebaseAuth, getFirebaseDB, getFirebaseInstance, initializeFirebase, locationDataSchema, medicationSchema, notificationSchema, passwordSchema, patientClinicSchema, patientDoctorSchema, patientLocationInfoSchema, patientMedicalInfoSchema, patientProfileInfoSchema, patientProfileSchema, patientSensitiveInfoSchema, postRequirementNotificationSchema, practitionerBasicInfoSchema, practitionerCertificationSchema, practitionerClinicWorkingHoursSchema, practitionerProfileInfoSchema, practitionerReviewInfoSchema, practitionerReviewSchema, practitionerSchema, practitionerTokenSchema, practitionerWorkingHoursSchema, preRequirementNotificationSchema, procedureCategorizationSchema, procedureInfoSchema, procedureReviewInfoSchema, procedureReviewSchema, procedureSummaryInfoSchema, requesterInfoSchema, reviewSchema, searchAppointmentsSchema, searchPatientsSchema, syncedCalendarEventSchema, timeSlotSchema, timestampSchema, updateAllergySchema, updateAppointmentSchema, updateBlockingConditionSchema, updateCalendarEventSchema, updateClinicAdminSchema, updateClinicGroupSchema, updateClinicSchema, updateContraindicationSchema, updateDocumentTemplateSchema, updateMedicationSchema, updatePatientMedicalInfoSchema, updateVitalStatsSchema, userRoleSchema, userRolesSchema, userSchema, vitalStatsSchema, workingHoursSchema };
18300
+ export { APPOINTMENTS_COLLECTION, AUTH_ERRORS, type AddAllergyData, type AddBlockingConditionData, type AddContraindicationData, type AddMedicationData, type AddressData, type AdminInfo, type AdminToken, AdminTokenStatus, type Allergy, type AllergySubtype, AllergyType, type AllergyTypeWithSubtype, type Appointment, type AppointmentNotification, type AppointmentReminderNotification, AppointmentService, AppointmentStatus, AuthError, AuthService, type BaseNotification, BlockingCondition, type Brand, BrandService, CALENDAR_COLLECTION, CLINICS_COLLECTION, CLINIC_ADMINS_COLLECTION, CLINIC_GROUPS_COLLECTION, type CalendarEvent, CalendarEventStatus, type CalendarEventTime, CalendarEventType, CalendarServiceV2, CalendarSyncStatus, type Category, CategoryService, CertificationLevel, CertificationSpecialty, type Clinic, type ClinicAdmin, ClinicAdminService, type ClinicAdminSignupData, type ClinicBranchSetupData, type ClinicContactInfo, type ClinicGroup, ClinicGroupService, type ClinicGroupSetupData, type ClinicInfo, type ClinicLocation, ClinicPhotoTag, type ClinicReview, type ClinicReviewInfo, ClinicService, ClinicTag, type ClinicTags, type ContactPerson, Contraindication, CosmeticAllergySubtype, type CreateAdminTokenData, type CreateAppointmentData, type CreateAppointmentParams, type CreateCalendarEventData, type CreateClinicAdminData, type CreateClinicData, type CreateClinicGroupData, type CreateDefaultClinicGroupData, type CreateDocumentTemplateData, type CreateDraftPractitionerData, type CreatePatientLocationInfoData, type CreatePatientMedicalInfoData, type CreatePatientProfileData, type CreatePatientSensitiveInfoData, type CreatePractitionerData, type CreatePractitionerTokenData, type CreateProcedureData, type CreateSyncedCalendarData, type CreateUserData, Currency, DOCUMENTATION_TEMPLATES_COLLECTION, type DateRange, type DoctorInfo, type DocumentElement, DocumentElementType, type DocumentTemplate, DocumentationTemplateService, DynamicVariable, type EmergencyContact, EnvironmentalAllergySubtype, FILLED_DOCUMENTS_COLLECTION, type FilledDocument, FilledDocumentService, FilledDocumentStatus, FirebaseErrorCode, type FirebaseUser, FoodAllergySubtype, type GamificationInfo, Gender, HeadingLevel, Language, ListType, type LocationData, MedicationAllergySubtype, type Notification, NotificationService, NotificationStatus, NotificationType, PATIENTS_COLLECTION, PATIENT_APPOINTMENTS_COLLECTION, PATIENT_LOCATION_INFO_COLLECTION, PATIENT_MEDICAL_HISTORY_COLLECTION, PATIENT_MEDICAL_INFO_COLLECTION, PATIENT_SENSITIVE_INFO_COLLECTION, PRACTITIONERS_COLLECTION, PROCEDURES_COLLECTION, type PatientClinic, type PatientDoctor, type PatientLocationInfo, type PatientMedicalInfo, type PatientProfile, type PatientProfileComplete, type PatientProfileInfo, type PatientSensitiveInfo, PatientService, PaymentStatus, type PostRequirementNotification, PracticeType, type Practitioner, type PractitionerBasicInfo, type PractitionerCertification, type PractitionerClinicProcedures, type PractitionerClinicWorkingHours, type PractitionerProfileInfo, type PractitionerReview, type PractitionerReviewInfo, PractitionerService, PractitionerStatus, type PractitionerToken, PractitionerTokenStatus, type PractitionerWorkingHours, type PreRequirementNotification, PricingMeasure, type Procedure, type ProcedureCategorization, ProcedureFamily, type ProcedureInfo, type ProcedureReview, type ProcedureReviewInfo, ProcedureService, type Product, ProductService, REGISTER_TOKENS_COLLECTION, REVIEWS_COLLECTION, type RequesterInfo, type Requirement, RequirementType, type Review, ReviewService, SYNCED_CALENDARS_COLLECTION, type SearchAppointmentsParams, type SearchCalendarEventsParams, SearchLocationEnum, type SearchPatientsParams, type Subcategory, SubcategoryService, SubscriptionModel, type SyncedCalendar, type SyncedCalendarEvent, SyncedCalendarProvider, SyncedCalendarsService, type Technology, TechnologyService, type TimeSlot, TimeUnit, TreatmentBenefit, USER_ERRORS, type UpdateAllergyData, type UpdateAppointmentData, type UpdateAppointmentParams, type UpdateBlockingConditionData, type UpdateCalendarEventData, type UpdateClinicAdminData, type UpdateClinicData, type UpdateClinicGroupData, type UpdateContraindicationData, type UpdateDocumentTemplateData, type UpdateMedicationData, type UpdatePatientLocationInfoData, type UpdatePatientMedicalInfoData, type UpdatePatientProfileData, type UpdatePatientSensitiveInfoData, type UpdatePractitionerData, type UpdateProcedureData, type UpdateSyncedCalendarData, type UpdateVitalStatsData, type User, UserRole, UserService, type ValidationSchema, type VitalStats, type WorkingHours, addAllergySchema, addBlockingConditionSchema, addContraindicationSchema, addMedicationSchema, addressDataSchema, adminInfoSchema, adminTokenSchema, allergySchema, allergySubtypeSchema, appointmentNotificationSchema, appointmentReminderNotificationSchema, baseNotificationSchema, blockingConditionSchema, calendarEventSchema, calendarEventTimeSchema, clinicAdminOptionsSchema, clinicAdminSchema, clinicAdminSignupSchema, clinicBranchSetupSchema, clinicContactInfoSchema, clinicGroupSchema, clinicGroupSetupSchema, clinicInfoSchema, clinicLocationSchema, clinicReviewInfoSchema, clinicReviewSchema, clinicSchema, clinicTagsSchema, contactPersonSchema, contraindicationSchema, createAdminTokenSchema, createAppointmentSchema, createCalendarEventSchema, createClinicAdminSchema, createClinicGroupSchema, createClinicReviewSchema, createClinicSchema, createDefaultClinicGroupSchema, createDocumentTemplateSchema, createDraftPractitionerSchema, createPatientLocationInfoSchema, createPatientMedicalInfoSchema, createPatientProfileSchema, createPatientSensitiveInfoSchema, createPractitionerReviewSchema, createPractitionerSchema, createPractitionerTokenSchema, createProcedureReviewSchema, createReviewSchema, createUserOptionsSchema, doctorInfoSchema, documentElementSchema, documentElementWithoutIdSchema, documentTemplateSchema, emailSchema, emergencyContactSchema, gamificationSchema, getFirebaseApp, getFirebaseAuth, getFirebaseDB, getFirebaseInstance, initializeFirebase, locationDataSchema, medicationSchema, notificationSchema, passwordSchema, patientClinicSchema, patientDoctorSchema, patientLocationInfoSchema, patientMedicalInfoSchema, patientProfileInfoSchema, patientProfileSchema, patientSensitiveInfoSchema, postRequirementNotificationSchema, practitionerBasicInfoSchema, practitionerCertificationSchema, practitionerClinicWorkingHoursSchema, practitionerProfileInfoSchema, practitionerReviewInfoSchema, practitionerReviewSchema, practitionerSchema, practitionerSignupSchema, practitionerTokenSchema, practitionerWorkingHoursSchema, preRequirementNotificationSchema, procedureCategorizationSchema, procedureInfoSchema, procedureReviewInfoSchema, procedureReviewSchema, procedureSummaryInfoSchema, requesterInfoSchema, reviewSchema, searchAppointmentsSchema, searchPatientsSchema, syncedCalendarEventSchema, timeSlotSchema, timestampSchema, updateAllergySchema, updateAppointmentSchema, updateBlockingConditionSchema, updateCalendarEventSchema, updateClinicAdminSchema, updateClinicGroupSchema, updateClinicSchema, updateContraindicationSchema, updateDocumentTemplateSchema, updateMedicationSchema, updatePatientMedicalInfoSchema, updateVitalStatsSchema, userRoleSchema, userRolesSchema, userSchema, vitalStatsSchema, workingHoursSchema };
package/dist/index.d.ts CHANGED
@@ -5326,6 +5326,36 @@ declare class AuthService extends BaseService {
5326
5326
  * @returns Promise koji se razrešava kada je lozinka promenjena
5327
5327
  */
5328
5328
  confirmPasswordReset(oobCode: string, newPassword: string): Promise<void>;
5329
+ /**
5330
+ * Registers a new practitioner user with email and password
5331
+ * Can either create a new practitioner profile or claim an existing draft profile with a token
5332
+ *
5333
+ * @param data - Practitioner signup data containing either new profile details or token for claiming draft profile
5334
+ * @returns Object containing the created user and practitioner profile
5335
+ */
5336
+ signUpPractitioner(data: {
5337
+ email: string;
5338
+ password: string;
5339
+ firstName: string;
5340
+ lastName: string;
5341
+ token?: string;
5342
+ profileData?: Partial<CreatePractitionerData>;
5343
+ }): Promise<{
5344
+ user: User;
5345
+ practitioner: Practitioner;
5346
+ }>;
5347
+ /**
5348
+ * Signs in a user with email and password specifically for practitioner role
5349
+ * @param email - User's email
5350
+ * @param password - User's password
5351
+ * @returns Object containing the user and practitioner profile
5352
+ * @throws {AUTH_ERRORS.INVALID_ROLE} If user doesn't have practitioner role
5353
+ * @throws {AUTH_ERRORS.NOT_FOUND} If practitioner profile is not found
5354
+ */
5355
+ signInPractitioner(email: string, password: string): Promise<{
5356
+ user: User;
5357
+ practitioner: Practitioner;
5358
+ }>;
5329
5359
  }
5330
5360
 
5331
5361
  /**
@@ -12052,6 +12082,81 @@ declare const createPractitionerTokenSchema: z.ZodObject<{
12052
12082
  clinicId: string;
12053
12083
  expiresAt?: Date | undefined;
12054
12084
  }>;
12085
+ /**
12086
+ * Simplified schema for practitioner signup
12087
+ */
12088
+ declare const practitionerSignupSchema: z.ZodObject<{
12089
+ email: z.ZodString;
12090
+ password: z.ZodString;
12091
+ firstName: z.ZodString;
12092
+ lastName: z.ZodString;
12093
+ token: z.ZodOptional<z.ZodString>;
12094
+ profileData: z.ZodOptional<z.ZodObject<{
12095
+ basicInfo: z.ZodOptional<z.ZodObject<{
12096
+ phoneNumber: z.ZodOptional<z.ZodString>;
12097
+ profileImageUrl: z.ZodOptional<z.ZodString>;
12098
+ gender: z.ZodOptional<z.ZodEnum<["male", "female", "other"]>>;
12099
+ bio: z.ZodOptional<z.ZodString>;
12100
+ }, "strip", z.ZodTypeAny, {
12101
+ gender?: "male" | "female" | "other" | undefined;
12102
+ phoneNumber?: string | undefined;
12103
+ profileImageUrl?: string | undefined;
12104
+ bio?: string | undefined;
12105
+ }, {
12106
+ gender?: "male" | "female" | "other" | undefined;
12107
+ phoneNumber?: string | undefined;
12108
+ profileImageUrl?: string | undefined;
12109
+ bio?: string | undefined;
12110
+ }>>;
12111
+ certification: z.ZodOptional<z.ZodAny>;
12112
+ }, "strip", z.ZodTypeAny, {
12113
+ basicInfo?: {
12114
+ gender?: "male" | "female" | "other" | undefined;
12115
+ phoneNumber?: string | undefined;
12116
+ profileImageUrl?: string | undefined;
12117
+ bio?: string | undefined;
12118
+ } | undefined;
12119
+ certification?: any;
12120
+ }, {
12121
+ basicInfo?: {
12122
+ gender?: "male" | "female" | "other" | undefined;
12123
+ phoneNumber?: string | undefined;
12124
+ profileImageUrl?: string | undefined;
12125
+ bio?: string | undefined;
12126
+ } | undefined;
12127
+ certification?: any;
12128
+ }>>;
12129
+ }, "strip", z.ZodTypeAny, {
12130
+ firstName: string;
12131
+ lastName: string;
12132
+ email: string;
12133
+ password: string;
12134
+ token?: string | undefined;
12135
+ profileData?: {
12136
+ basicInfo?: {
12137
+ gender?: "male" | "female" | "other" | undefined;
12138
+ phoneNumber?: string | undefined;
12139
+ profileImageUrl?: string | undefined;
12140
+ bio?: string | undefined;
12141
+ } | undefined;
12142
+ certification?: any;
12143
+ } | undefined;
12144
+ }, {
12145
+ firstName: string;
12146
+ lastName: string;
12147
+ email: string;
12148
+ password: string;
12149
+ token?: string | undefined;
12150
+ profileData?: {
12151
+ basicInfo?: {
12152
+ gender?: "male" | "female" | "other" | undefined;
12153
+ phoneNumber?: string | undefined;
12154
+ profileImageUrl?: string | undefined;
12155
+ bio?: string | undefined;
12156
+ } | undefined;
12157
+ certification?: any;
12158
+ } | undefined;
12159
+ }>;
12055
12160
 
12056
12161
  /**
12057
12162
  * Validaciona šema za kontakt informacije
@@ -18192,4 +18297,4 @@ declare const createReviewSchema: z.ZodEffects<z.ZodObject<{
18192
18297
  } | undefined;
18193
18298
  }>;
18194
18299
 
18195
- export { APPOINTMENTS_COLLECTION, AUTH_ERRORS, type AddAllergyData, type AddBlockingConditionData, type AddContraindicationData, type AddMedicationData, type AddressData, type AdminInfo, type AdminToken, AdminTokenStatus, type Allergy, type AllergySubtype, AllergyType, type AllergyTypeWithSubtype, type Appointment, type AppointmentNotification, type AppointmentReminderNotification, AppointmentService, AppointmentStatus, AuthError, AuthService, type BaseNotification, BlockingCondition, type Brand, BrandService, CALENDAR_COLLECTION, CLINICS_COLLECTION, CLINIC_ADMINS_COLLECTION, CLINIC_GROUPS_COLLECTION, type CalendarEvent, CalendarEventStatus, type CalendarEventTime, CalendarEventType, CalendarServiceV2, CalendarSyncStatus, type Category, CategoryService, CertificationLevel, CertificationSpecialty, type Clinic, type ClinicAdmin, ClinicAdminService, type ClinicAdminSignupData, type ClinicBranchSetupData, type ClinicContactInfo, type ClinicGroup, ClinicGroupService, type ClinicGroupSetupData, type ClinicInfo, type ClinicLocation, ClinicPhotoTag, type ClinicReview, type ClinicReviewInfo, ClinicService, ClinicTag, type ClinicTags, type ContactPerson, Contraindication, CosmeticAllergySubtype, type CreateAdminTokenData, type CreateAppointmentData, type CreateAppointmentParams, type CreateCalendarEventData, type CreateClinicAdminData, type CreateClinicData, type CreateClinicGroupData, type CreateDefaultClinicGroupData, type CreateDocumentTemplateData, type CreateDraftPractitionerData, type CreatePatientLocationInfoData, type CreatePatientMedicalInfoData, type CreatePatientProfileData, type CreatePatientSensitiveInfoData, type CreatePractitionerData, type CreatePractitionerTokenData, type CreateProcedureData, type CreateSyncedCalendarData, type CreateUserData, Currency, DOCUMENTATION_TEMPLATES_COLLECTION, type DateRange, type DoctorInfo, type DocumentElement, DocumentElementType, type DocumentTemplate, DocumentationTemplateService, DynamicVariable, type EmergencyContact, EnvironmentalAllergySubtype, FILLED_DOCUMENTS_COLLECTION, type FilledDocument, FilledDocumentService, FilledDocumentStatus, FirebaseErrorCode, type FirebaseUser, FoodAllergySubtype, type GamificationInfo, Gender, HeadingLevel, Language, ListType, type LocationData, MedicationAllergySubtype, type Notification, NotificationService, NotificationStatus, NotificationType, PATIENTS_COLLECTION, PATIENT_APPOINTMENTS_COLLECTION, PATIENT_LOCATION_INFO_COLLECTION, PATIENT_MEDICAL_HISTORY_COLLECTION, PATIENT_MEDICAL_INFO_COLLECTION, PATIENT_SENSITIVE_INFO_COLLECTION, PRACTITIONERS_COLLECTION, PROCEDURES_COLLECTION, type PatientClinic, type PatientDoctor, type PatientLocationInfo, type PatientMedicalInfo, type PatientProfile, type PatientProfileComplete, type PatientProfileInfo, type PatientSensitiveInfo, PatientService, PaymentStatus, type PostRequirementNotification, PracticeType, type Practitioner, type PractitionerBasicInfo, type PractitionerCertification, type PractitionerClinicProcedures, type PractitionerClinicWorkingHours, type PractitionerProfileInfo, type PractitionerReview, type PractitionerReviewInfo, PractitionerService, PractitionerStatus, type PractitionerToken, PractitionerTokenStatus, type PractitionerWorkingHours, type PreRequirementNotification, PricingMeasure, type Procedure, type ProcedureCategorization, ProcedureFamily, type ProcedureInfo, type ProcedureReview, type ProcedureReviewInfo, ProcedureService, type Product, ProductService, REGISTER_TOKENS_COLLECTION, REVIEWS_COLLECTION, type RequesterInfo, type Requirement, RequirementType, type Review, ReviewService, SYNCED_CALENDARS_COLLECTION, type SearchAppointmentsParams, type SearchCalendarEventsParams, SearchLocationEnum, type SearchPatientsParams, type Subcategory, SubcategoryService, SubscriptionModel, type SyncedCalendar, type SyncedCalendarEvent, SyncedCalendarProvider, SyncedCalendarsService, type Technology, TechnologyService, type TimeSlot, TimeUnit, TreatmentBenefit, USER_ERRORS, type UpdateAllergyData, type UpdateAppointmentData, type UpdateAppointmentParams, type UpdateBlockingConditionData, type UpdateCalendarEventData, type UpdateClinicAdminData, type UpdateClinicData, type UpdateClinicGroupData, type UpdateContraindicationData, type UpdateDocumentTemplateData, type UpdateMedicationData, type UpdatePatientLocationInfoData, type UpdatePatientMedicalInfoData, type UpdatePatientProfileData, type UpdatePatientSensitiveInfoData, type UpdatePractitionerData, type UpdateProcedureData, type UpdateSyncedCalendarData, type UpdateVitalStatsData, type User, UserRole, UserService, type ValidationSchema, type VitalStats, type WorkingHours, addAllergySchema, addBlockingConditionSchema, addContraindicationSchema, addMedicationSchema, addressDataSchema, adminInfoSchema, adminTokenSchema, allergySchema, allergySubtypeSchema, appointmentNotificationSchema, appointmentReminderNotificationSchema, baseNotificationSchema, blockingConditionSchema, calendarEventSchema, calendarEventTimeSchema, clinicAdminOptionsSchema, clinicAdminSchema, clinicAdminSignupSchema, clinicBranchSetupSchema, clinicContactInfoSchema, clinicGroupSchema, clinicGroupSetupSchema, clinicInfoSchema, clinicLocationSchema, clinicReviewInfoSchema, clinicReviewSchema, clinicSchema, clinicTagsSchema, contactPersonSchema, contraindicationSchema, createAdminTokenSchema, createAppointmentSchema, createCalendarEventSchema, createClinicAdminSchema, createClinicGroupSchema, createClinicReviewSchema, createClinicSchema, createDefaultClinicGroupSchema, createDocumentTemplateSchema, createDraftPractitionerSchema, createPatientLocationInfoSchema, createPatientMedicalInfoSchema, createPatientProfileSchema, createPatientSensitiveInfoSchema, createPractitionerReviewSchema, createPractitionerSchema, createPractitionerTokenSchema, createProcedureReviewSchema, createReviewSchema, createUserOptionsSchema, doctorInfoSchema, documentElementSchema, documentElementWithoutIdSchema, documentTemplateSchema, emailSchema, emergencyContactSchema, gamificationSchema, getFirebaseApp, getFirebaseAuth, getFirebaseDB, getFirebaseInstance, initializeFirebase, locationDataSchema, medicationSchema, notificationSchema, passwordSchema, patientClinicSchema, patientDoctorSchema, patientLocationInfoSchema, patientMedicalInfoSchema, patientProfileInfoSchema, patientProfileSchema, patientSensitiveInfoSchema, postRequirementNotificationSchema, practitionerBasicInfoSchema, practitionerCertificationSchema, practitionerClinicWorkingHoursSchema, practitionerProfileInfoSchema, practitionerReviewInfoSchema, practitionerReviewSchema, practitionerSchema, practitionerTokenSchema, practitionerWorkingHoursSchema, preRequirementNotificationSchema, procedureCategorizationSchema, procedureInfoSchema, procedureReviewInfoSchema, procedureReviewSchema, procedureSummaryInfoSchema, requesterInfoSchema, reviewSchema, searchAppointmentsSchema, searchPatientsSchema, syncedCalendarEventSchema, timeSlotSchema, timestampSchema, updateAllergySchema, updateAppointmentSchema, updateBlockingConditionSchema, updateCalendarEventSchema, updateClinicAdminSchema, updateClinicGroupSchema, updateClinicSchema, updateContraindicationSchema, updateDocumentTemplateSchema, updateMedicationSchema, updatePatientMedicalInfoSchema, updateVitalStatsSchema, userRoleSchema, userRolesSchema, userSchema, vitalStatsSchema, workingHoursSchema };
18300
+ export { APPOINTMENTS_COLLECTION, AUTH_ERRORS, type AddAllergyData, type AddBlockingConditionData, type AddContraindicationData, type AddMedicationData, type AddressData, type AdminInfo, type AdminToken, AdminTokenStatus, type Allergy, type AllergySubtype, AllergyType, type AllergyTypeWithSubtype, type Appointment, type AppointmentNotification, type AppointmentReminderNotification, AppointmentService, AppointmentStatus, AuthError, AuthService, type BaseNotification, BlockingCondition, type Brand, BrandService, CALENDAR_COLLECTION, CLINICS_COLLECTION, CLINIC_ADMINS_COLLECTION, CLINIC_GROUPS_COLLECTION, type CalendarEvent, CalendarEventStatus, type CalendarEventTime, CalendarEventType, CalendarServiceV2, CalendarSyncStatus, type Category, CategoryService, CertificationLevel, CertificationSpecialty, type Clinic, type ClinicAdmin, ClinicAdminService, type ClinicAdminSignupData, type ClinicBranchSetupData, type ClinicContactInfo, type ClinicGroup, ClinicGroupService, type ClinicGroupSetupData, type ClinicInfo, type ClinicLocation, ClinicPhotoTag, type ClinicReview, type ClinicReviewInfo, ClinicService, ClinicTag, type ClinicTags, type ContactPerson, Contraindication, CosmeticAllergySubtype, type CreateAdminTokenData, type CreateAppointmentData, type CreateAppointmentParams, type CreateCalendarEventData, type CreateClinicAdminData, type CreateClinicData, type CreateClinicGroupData, type CreateDefaultClinicGroupData, type CreateDocumentTemplateData, type CreateDraftPractitionerData, type CreatePatientLocationInfoData, type CreatePatientMedicalInfoData, type CreatePatientProfileData, type CreatePatientSensitiveInfoData, type CreatePractitionerData, type CreatePractitionerTokenData, type CreateProcedureData, type CreateSyncedCalendarData, type CreateUserData, Currency, DOCUMENTATION_TEMPLATES_COLLECTION, type DateRange, type DoctorInfo, type DocumentElement, DocumentElementType, type DocumentTemplate, DocumentationTemplateService, DynamicVariable, type EmergencyContact, EnvironmentalAllergySubtype, FILLED_DOCUMENTS_COLLECTION, type FilledDocument, FilledDocumentService, FilledDocumentStatus, FirebaseErrorCode, type FirebaseUser, FoodAllergySubtype, type GamificationInfo, Gender, HeadingLevel, Language, ListType, type LocationData, MedicationAllergySubtype, type Notification, NotificationService, NotificationStatus, NotificationType, PATIENTS_COLLECTION, PATIENT_APPOINTMENTS_COLLECTION, PATIENT_LOCATION_INFO_COLLECTION, PATIENT_MEDICAL_HISTORY_COLLECTION, PATIENT_MEDICAL_INFO_COLLECTION, PATIENT_SENSITIVE_INFO_COLLECTION, PRACTITIONERS_COLLECTION, PROCEDURES_COLLECTION, type PatientClinic, type PatientDoctor, type PatientLocationInfo, type PatientMedicalInfo, type PatientProfile, type PatientProfileComplete, type PatientProfileInfo, type PatientSensitiveInfo, PatientService, PaymentStatus, type PostRequirementNotification, PracticeType, type Practitioner, type PractitionerBasicInfo, type PractitionerCertification, type PractitionerClinicProcedures, type PractitionerClinicWorkingHours, type PractitionerProfileInfo, type PractitionerReview, type PractitionerReviewInfo, PractitionerService, PractitionerStatus, type PractitionerToken, PractitionerTokenStatus, type PractitionerWorkingHours, type PreRequirementNotification, PricingMeasure, type Procedure, type ProcedureCategorization, ProcedureFamily, type ProcedureInfo, type ProcedureReview, type ProcedureReviewInfo, ProcedureService, type Product, ProductService, REGISTER_TOKENS_COLLECTION, REVIEWS_COLLECTION, type RequesterInfo, type Requirement, RequirementType, type Review, ReviewService, SYNCED_CALENDARS_COLLECTION, type SearchAppointmentsParams, type SearchCalendarEventsParams, SearchLocationEnum, type SearchPatientsParams, type Subcategory, SubcategoryService, SubscriptionModel, type SyncedCalendar, type SyncedCalendarEvent, SyncedCalendarProvider, SyncedCalendarsService, type Technology, TechnologyService, type TimeSlot, TimeUnit, TreatmentBenefit, USER_ERRORS, type UpdateAllergyData, type UpdateAppointmentData, type UpdateAppointmentParams, type UpdateBlockingConditionData, type UpdateCalendarEventData, type UpdateClinicAdminData, type UpdateClinicData, type UpdateClinicGroupData, type UpdateContraindicationData, type UpdateDocumentTemplateData, type UpdateMedicationData, type UpdatePatientLocationInfoData, type UpdatePatientMedicalInfoData, type UpdatePatientProfileData, type UpdatePatientSensitiveInfoData, type UpdatePractitionerData, type UpdateProcedureData, type UpdateSyncedCalendarData, type UpdateVitalStatsData, type User, UserRole, UserService, type ValidationSchema, type VitalStats, type WorkingHours, addAllergySchema, addBlockingConditionSchema, addContraindicationSchema, addMedicationSchema, addressDataSchema, adminInfoSchema, adminTokenSchema, allergySchema, allergySubtypeSchema, appointmentNotificationSchema, appointmentReminderNotificationSchema, baseNotificationSchema, blockingConditionSchema, calendarEventSchema, calendarEventTimeSchema, clinicAdminOptionsSchema, clinicAdminSchema, clinicAdminSignupSchema, clinicBranchSetupSchema, clinicContactInfoSchema, clinicGroupSchema, clinicGroupSetupSchema, clinicInfoSchema, clinicLocationSchema, clinicReviewInfoSchema, clinicReviewSchema, clinicSchema, clinicTagsSchema, contactPersonSchema, contraindicationSchema, createAdminTokenSchema, createAppointmentSchema, createCalendarEventSchema, createClinicAdminSchema, createClinicGroupSchema, createClinicReviewSchema, createClinicSchema, createDefaultClinicGroupSchema, createDocumentTemplateSchema, createDraftPractitionerSchema, createPatientLocationInfoSchema, createPatientMedicalInfoSchema, createPatientProfileSchema, createPatientSensitiveInfoSchema, createPractitionerReviewSchema, createPractitionerSchema, createPractitionerTokenSchema, createProcedureReviewSchema, createReviewSchema, createUserOptionsSchema, doctorInfoSchema, documentElementSchema, documentElementWithoutIdSchema, documentTemplateSchema, emailSchema, emergencyContactSchema, gamificationSchema, getFirebaseApp, getFirebaseAuth, getFirebaseDB, getFirebaseInstance, initializeFirebase, locationDataSchema, medicationSchema, notificationSchema, passwordSchema, patientClinicSchema, patientDoctorSchema, patientLocationInfoSchema, patientMedicalInfoSchema, patientProfileInfoSchema, patientProfileSchema, patientSensitiveInfoSchema, postRequirementNotificationSchema, practitionerBasicInfoSchema, practitionerCertificationSchema, practitionerClinicWorkingHoursSchema, practitionerProfileInfoSchema, practitionerReviewInfoSchema, practitionerReviewSchema, practitionerSchema, practitionerSignupSchema, practitionerTokenSchema, practitionerWorkingHoursSchema, preRequirementNotificationSchema, procedureCategorizationSchema, procedureInfoSchema, procedureReviewInfoSchema, procedureReviewSchema, procedureSummaryInfoSchema, requesterInfoSchema, reviewSchema, searchAppointmentsSchema, searchPatientsSchema, syncedCalendarEventSchema, timeSlotSchema, timestampSchema, updateAllergySchema, updateAppointmentSchema, updateBlockingConditionSchema, updateCalendarEventSchema, updateClinicAdminSchema, updateClinicGroupSchema, updateClinicSchema, updateContraindicationSchema, updateDocumentTemplateSchema, updateMedicationSchema, updatePatientMedicalInfoSchema, updateVitalStatsSchema, userRoleSchema, userRolesSchema, userSchema, vitalStatsSchema, workingHoursSchema };
package/dist/index.js CHANGED
@@ -190,6 +190,7 @@ __export(index_exports, {
190
190
  practitionerReviewInfoSchema: () => practitionerReviewInfoSchema,
191
191
  practitionerReviewSchema: () => practitionerReviewSchema,
192
192
  practitionerSchema: () => practitionerSchema,
193
+ practitionerSignupSchema: () => practitionerSignupSchema,
193
194
  practitionerTokenSchema: () => practitionerTokenSchema,
194
195
  practitionerWorkingHoursSchema: () => practitionerWorkingHoursSchema,
195
196
  preRequirementNotificationSchema: () => preRequirementNotificationSchema,
@@ -3104,6 +3105,22 @@ var createPractitionerTokenSchema = import_zod11.z.object({
3104
3105
  clinicId: import_zod11.z.string().min(1),
3105
3106
  expiresAt: import_zod11.z.date().optional()
3106
3107
  });
3108
+ var practitionerSignupSchema = import_zod11.z.object({
3109
+ email: import_zod11.z.string().email(),
3110
+ password: import_zod11.z.string().min(8),
3111
+ firstName: import_zod11.z.string().min(2).max(50),
3112
+ lastName: import_zod11.z.string().min(2).max(50),
3113
+ token: import_zod11.z.string().optional(),
3114
+ profileData: import_zod11.z.object({
3115
+ basicInfo: import_zod11.z.object({
3116
+ phoneNumber: import_zod11.z.string().optional(),
3117
+ profileImageUrl: import_zod11.z.string().optional(),
3118
+ gender: import_zod11.z.enum(["male", "female", "other"]).optional(),
3119
+ bio: import_zod11.z.string().optional()
3120
+ }).optional(),
3121
+ certification: import_zod11.z.any().optional()
3122
+ }).optional()
3123
+ });
3107
3124
 
3108
3125
  // src/validations/clinic.schema.ts
3109
3126
  var clinicContactInfoSchema = import_zod12.z.object({
@@ -3911,8 +3928,8 @@ var PractitionerService = class extends BaseService {
3911
3928
  if (!clinic) {
3912
3929
  throw new Error(`Clinic ${clinicId} not found`);
3913
3930
  }
3914
- const clinics = data.clinics || [clinicId];
3915
- if (data.clinics) {
3931
+ const clinicsToAdd = /* @__PURE__ */ new Set([clinicId]);
3932
+ if (data.clinics && data.clinics.length > 0) {
3916
3933
  for (const cId of data.clinics) {
3917
3934
  if (cId !== clinicId) {
3918
3935
  const otherClinic = await this.getClinicService().getClinic(cId);
@@ -3920,8 +3937,10 @@ var PractitionerService = class extends BaseService {
3920
3937
  throw new Error(`Clinic ${cId} not found`);
3921
3938
  }
3922
3939
  }
3940
+ clinicsToAdd.add(cId);
3923
3941
  }
3924
3942
  }
3943
+ const clinics = Array.from(clinicsToAdd);
3925
3944
  const defaultReviewInfo = {
3926
3945
  totalReviews: 0,
3927
3946
  averageRating: 0,
@@ -3933,7 +3952,21 @@ var PractitionerService = class extends BaseService {
3933
3952
  recommendationPercentage: 0
3934
3953
  };
3935
3954
  const practitionerId = this.generateId();
3936
- const clinicsInfo = validatedData.clinicsInfo || [];
3955
+ const clinicsInfo = [];
3956
+ for (const cId of clinics) {
3957
+ const clinicData = await this.getClinicService().getClinic(cId);
3958
+ if (clinicData) {
3959
+ clinicsInfo.push({
3960
+ id: clinicData.id,
3961
+ name: clinicData.name,
3962
+ location: clinicData.location,
3963
+ contactInfo: clinicData.contactInfo,
3964
+ featuredPhoto: clinicData.coverPhoto || "",
3965
+ description: clinicData.description || null
3966
+ });
3967
+ }
3968
+ }
3969
+ const finalClinicsInfo = validatedData.clinicsInfo && validatedData.clinicsInfo.length > 0 ? validatedData.clinicsInfo : clinicsInfo;
3937
3970
  const proceduresInfo = [];
3938
3971
  const practitionerData = {
3939
3972
  id: practitionerId,
@@ -3943,7 +3976,7 @@ var PractitionerService = class extends BaseService {
3943
3976
  certification: validatedData.certification,
3944
3977
  clinics,
3945
3978
  clinicWorkingHours: validatedData.clinicWorkingHours || [],
3946
- clinicsInfo,
3979
+ clinicsInfo: finalClinicsInfo,
3947
3980
  procedures: [],
3948
3981
  proceduresInfo,
3949
3982
  reviewInfo: defaultReviewInfo,
@@ -6949,6 +6982,234 @@ var AuthService = class extends BaseService {
6949
6982
  throw error;
6950
6983
  }
6951
6984
  }
6985
+ /**
6986
+ * Registers a new practitioner user with email and password
6987
+ * Can either create a new practitioner profile or claim an existing draft profile with a token
6988
+ *
6989
+ * @param data - Practitioner signup data containing either new profile details or token for claiming draft profile
6990
+ * @returns Object containing the created user and practitioner profile
6991
+ */
6992
+ async signUpPractitioner(data) {
6993
+ var _a, _b, _c, _d;
6994
+ try {
6995
+ console.log("[AUTH] Starting practitioner signup process", {
6996
+ email: data.email,
6997
+ hasToken: !!data.token
6998
+ });
6999
+ try {
7000
+ await practitionerSignupSchema.parseAsync(data);
7001
+ console.log("[AUTH] Practitioner signup data validation passed");
7002
+ } catch (validationError) {
7003
+ console.error(
7004
+ "[AUTH] Validation error in signUpPractitioner:",
7005
+ validationError
7006
+ );
7007
+ throw validationError;
7008
+ }
7009
+ console.log("[AUTH] Creating Firebase user");
7010
+ let firebaseUser;
7011
+ try {
7012
+ const result = await (0, import_auth5.createUserWithEmailAndPassword)(
7013
+ this.auth,
7014
+ data.email,
7015
+ data.password
7016
+ );
7017
+ firebaseUser = result.user;
7018
+ console.log("[AUTH] Firebase user created successfully", {
7019
+ uid: firebaseUser.uid
7020
+ });
7021
+ } catch (firebaseError) {
7022
+ console.error("[AUTH] Firebase user creation failed:", firebaseError);
7023
+ throw firebaseError;
7024
+ }
7025
+ console.log("[AUTH] Creating user with PRACTITIONER role");
7026
+ let user;
7027
+ try {
7028
+ user = await this.userService.createUser(
7029
+ firebaseUser,
7030
+ ["practitioner" /* PRACTITIONER */],
7031
+ {
7032
+ skipProfileCreation: true
7033
+ // We'll create the profile separately
7034
+ }
7035
+ );
7036
+ console.log("[AUTH] User with PRACTITIONER role created successfully", {
7037
+ userId: user.uid
7038
+ });
7039
+ } catch (userCreationError) {
7040
+ console.error("[AUTH] User creation failed:", userCreationError);
7041
+ throw userCreationError;
7042
+ }
7043
+ console.log("[AUTH] Initializing practitioner service");
7044
+ const practitionerService = new PractitionerService(
7045
+ this.db,
7046
+ this.auth,
7047
+ this.app
7048
+ );
7049
+ let practitioner = null;
7050
+ if (data.token) {
7051
+ console.log("[AUTH] Token provided, attempting to claim draft profile");
7052
+ try {
7053
+ practitioner = await practitionerService.validateTokenAndClaimProfile(
7054
+ data.token,
7055
+ firebaseUser.uid
7056
+ );
7057
+ if (!practitioner) {
7058
+ throw new Error("Invalid or expired invitation token");
7059
+ }
7060
+ console.log("[AUTH] Successfully claimed draft profile", {
7061
+ practitionerId: practitioner.id
7062
+ });
7063
+ await this.userService.updateUser(firebaseUser.uid, {
7064
+ practitionerProfile: practitioner.id
7065
+ });
7066
+ console.log(
7067
+ "[AUTH] User updated with practitioner profile reference"
7068
+ );
7069
+ } catch (tokenError) {
7070
+ console.error("[AUTH] Failed to claim draft profile:", tokenError);
7071
+ throw tokenError;
7072
+ }
7073
+ } else {
7074
+ console.log("[AUTH] Creating new practitioner profile");
7075
+ if (!data.profileData) {
7076
+ data.profileData = {};
7077
+ }
7078
+ const basicInfo = {
7079
+ firstName: data.firstName,
7080
+ lastName: data.lastName,
7081
+ email: data.email,
7082
+ phoneNumber: ((_a = data.profileData.basicInfo) == null ? void 0 : _a.phoneNumber) || "",
7083
+ profileImageUrl: ((_b = data.profileData.basicInfo) == null ? void 0 : _b.profileImageUrl) || "",
7084
+ gender: ((_c = data.profileData.basicInfo) == null ? void 0 : _c.gender) || "other",
7085
+ // Default to "other" if not provided
7086
+ bio: ((_d = data.profileData.basicInfo) == null ? void 0 : _d.bio) || "",
7087
+ title: "Practitioner",
7088
+ // Default title
7089
+ dateOfBirth: /* @__PURE__ */ new Date(),
7090
+ // Default to today
7091
+ languages: ["English"]
7092
+ // Default language
7093
+ };
7094
+ const certification = data.profileData.certification || {
7095
+ level: "aesthetician" /* AESTHETICIAN */,
7096
+ specialties: [],
7097
+ licenseNumber: "Pending",
7098
+ issuingAuthority: "Pending",
7099
+ issueDate: /* @__PURE__ */ new Date(),
7100
+ verificationStatus: "pending"
7101
+ };
7102
+ const createPractitionerData = {
7103
+ userRef: firebaseUser.uid,
7104
+ basicInfo,
7105
+ certification,
7106
+ status: "active" /* ACTIVE */,
7107
+ isActive: true,
7108
+ isVerified: false
7109
+ };
7110
+ try {
7111
+ practitioner = await practitionerService.createPractitioner(
7112
+ createPractitionerData
7113
+ );
7114
+ console.log("[AUTH] Practitioner profile created successfully", {
7115
+ practitionerId: practitioner.id
7116
+ });
7117
+ await this.userService.updateUser(firebaseUser.uid, {
7118
+ practitionerProfile: practitioner.id
7119
+ });
7120
+ console.log(
7121
+ "[AUTH] User updated with practitioner profile reference"
7122
+ );
7123
+ } catch (createError) {
7124
+ console.error(
7125
+ "[AUTH] Failed to create practitioner profile:",
7126
+ createError
7127
+ );
7128
+ throw createError;
7129
+ }
7130
+ }
7131
+ console.log("[AUTH] Practitioner signup completed successfully", {
7132
+ userId: user.uid,
7133
+ practitionerId: (practitioner == null ? void 0 : practitioner.id) || "unknown"
7134
+ });
7135
+ return {
7136
+ user,
7137
+ practitioner
7138
+ };
7139
+ } catch (error) {
7140
+ if (error instanceof import_zod18.z.ZodError) {
7141
+ console.error(
7142
+ "[AUTH] Zod validation error in signUpPractitioner:",
7143
+ JSON.stringify(error.errors, null, 2)
7144
+ );
7145
+ throw AUTH_ERRORS.VALIDATION_ERROR;
7146
+ }
7147
+ const firebaseError = error;
7148
+ if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
7149
+ console.error("[AUTH] Email already in use:", data.email);
7150
+ throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
7151
+ }
7152
+ console.error("[AUTH] Unhandled error in signUpPractitioner:", error);
7153
+ throw error;
7154
+ }
7155
+ }
7156
+ /**
7157
+ * Signs in a user with email and password specifically for practitioner role
7158
+ * @param email - User's email
7159
+ * @param password - User's password
7160
+ * @returns Object containing the user and practitioner profile
7161
+ * @throws {AUTH_ERRORS.INVALID_ROLE} If user doesn't have practitioner role
7162
+ * @throws {AUTH_ERRORS.NOT_FOUND} If practitioner profile is not found
7163
+ */
7164
+ async signInPractitioner(email, password) {
7165
+ var _a;
7166
+ try {
7167
+ console.log("[AUTH] Starting practitioner signin process", {
7168
+ email
7169
+ });
7170
+ const practitionerService = new PractitionerService(
7171
+ this.db,
7172
+ this.auth,
7173
+ this.app
7174
+ );
7175
+ const { user: firebaseUser } = await (0, import_auth5.signInWithEmailAndPassword)(
7176
+ this.auth,
7177
+ email,
7178
+ password
7179
+ );
7180
+ const user = await this.userService.getOrCreateUser(firebaseUser);
7181
+ console.log("[AUTH] User retrieved", { uid: user.uid });
7182
+ if (!((_a = user.roles) == null ? void 0 : _a.includes("practitioner" /* PRACTITIONER */))) {
7183
+ console.error("[AUTH] User is not a practitioner:", user.uid);
7184
+ throw AUTH_ERRORS.INVALID_ROLE;
7185
+ }
7186
+ if (!user.practitionerProfile) {
7187
+ console.error("[AUTH] User has no practitioner profile:", user.uid);
7188
+ throw AUTH_ERRORS.NOT_FOUND;
7189
+ }
7190
+ const practitioner = await practitionerService.getPractitioner(
7191
+ user.practitionerProfile
7192
+ );
7193
+ if (!practitioner) {
7194
+ console.error(
7195
+ "[AUTH] Practitioner profile not found:",
7196
+ user.practitionerProfile
7197
+ );
7198
+ throw AUTH_ERRORS.NOT_FOUND;
7199
+ }
7200
+ console.log("[AUTH] Practitioner signin completed successfully", {
7201
+ userId: user.uid,
7202
+ practitionerId: practitioner.id
7203
+ });
7204
+ return {
7205
+ user,
7206
+ practitioner
7207
+ };
7208
+ } catch (error) {
7209
+ console.error("[AUTH] Error in signInPractitioner:", error);
7210
+ throw error;
7211
+ }
7212
+ }
6952
7213
  };
6953
7214
 
6954
7215
  // src/services/notifications/notification.service.ts
@@ -13562,6 +13823,7 @@ var RequirementType = /* @__PURE__ */ ((RequirementType2) => {
13562
13823
  practitionerReviewInfoSchema,
13563
13824
  practitionerReviewSchema,
13564
13825
  practitionerSchema,
13826
+ practitionerSignupSchema,
13565
13827
  practitionerTokenSchema,
13566
13828
  practitionerWorkingHoursSchema,
13567
13829
  preRequirementNotificationSchema,
package/dist/index.mjs CHANGED
@@ -2989,6 +2989,22 @@ var createPractitionerTokenSchema = z11.object({
2989
2989
  clinicId: z11.string().min(1),
2990
2990
  expiresAt: z11.date().optional()
2991
2991
  });
2992
+ var practitionerSignupSchema = z11.object({
2993
+ email: z11.string().email(),
2994
+ password: z11.string().min(8),
2995
+ firstName: z11.string().min(2).max(50),
2996
+ lastName: z11.string().min(2).max(50),
2997
+ token: z11.string().optional(),
2998
+ profileData: z11.object({
2999
+ basicInfo: z11.object({
3000
+ phoneNumber: z11.string().optional(),
3001
+ profileImageUrl: z11.string().optional(),
3002
+ gender: z11.enum(["male", "female", "other"]).optional(),
3003
+ bio: z11.string().optional()
3004
+ }).optional(),
3005
+ certification: z11.any().optional()
3006
+ }).optional()
3007
+ });
2992
3008
 
2993
3009
  // src/validations/clinic.schema.ts
2994
3010
  var clinicContactInfoSchema = z12.object({
@@ -3813,8 +3829,8 @@ var PractitionerService = class extends BaseService {
3813
3829
  if (!clinic) {
3814
3830
  throw new Error(`Clinic ${clinicId} not found`);
3815
3831
  }
3816
- const clinics = data.clinics || [clinicId];
3817
- if (data.clinics) {
3832
+ const clinicsToAdd = /* @__PURE__ */ new Set([clinicId]);
3833
+ if (data.clinics && data.clinics.length > 0) {
3818
3834
  for (const cId of data.clinics) {
3819
3835
  if (cId !== clinicId) {
3820
3836
  const otherClinic = await this.getClinicService().getClinic(cId);
@@ -3822,8 +3838,10 @@ var PractitionerService = class extends BaseService {
3822
3838
  throw new Error(`Clinic ${cId} not found`);
3823
3839
  }
3824
3840
  }
3841
+ clinicsToAdd.add(cId);
3825
3842
  }
3826
3843
  }
3844
+ const clinics = Array.from(clinicsToAdd);
3827
3845
  const defaultReviewInfo = {
3828
3846
  totalReviews: 0,
3829
3847
  averageRating: 0,
@@ -3835,7 +3853,21 @@ var PractitionerService = class extends BaseService {
3835
3853
  recommendationPercentage: 0
3836
3854
  };
3837
3855
  const practitionerId = this.generateId();
3838
- const clinicsInfo = validatedData.clinicsInfo || [];
3856
+ const clinicsInfo = [];
3857
+ for (const cId of clinics) {
3858
+ const clinicData = await this.getClinicService().getClinic(cId);
3859
+ if (clinicData) {
3860
+ clinicsInfo.push({
3861
+ id: clinicData.id,
3862
+ name: clinicData.name,
3863
+ location: clinicData.location,
3864
+ contactInfo: clinicData.contactInfo,
3865
+ featuredPhoto: clinicData.coverPhoto || "",
3866
+ description: clinicData.description || null
3867
+ });
3868
+ }
3869
+ }
3870
+ const finalClinicsInfo = validatedData.clinicsInfo && validatedData.clinicsInfo.length > 0 ? validatedData.clinicsInfo : clinicsInfo;
3839
3871
  const proceduresInfo = [];
3840
3872
  const practitionerData = {
3841
3873
  id: practitionerId,
@@ -3845,7 +3877,7 @@ var PractitionerService = class extends BaseService {
3845
3877
  certification: validatedData.certification,
3846
3878
  clinics,
3847
3879
  clinicWorkingHours: validatedData.clinicWorkingHours || [],
3848
- clinicsInfo,
3880
+ clinicsInfo: finalClinicsInfo,
3849
3881
  procedures: [],
3850
3882
  proceduresInfo,
3851
3883
  reviewInfo: defaultReviewInfo,
@@ -6906,6 +6938,234 @@ var AuthService = class extends BaseService {
6906
6938
  throw error;
6907
6939
  }
6908
6940
  }
6941
+ /**
6942
+ * Registers a new practitioner user with email and password
6943
+ * Can either create a new practitioner profile or claim an existing draft profile with a token
6944
+ *
6945
+ * @param data - Practitioner signup data containing either new profile details or token for claiming draft profile
6946
+ * @returns Object containing the created user and practitioner profile
6947
+ */
6948
+ async signUpPractitioner(data) {
6949
+ var _a, _b, _c, _d;
6950
+ try {
6951
+ console.log("[AUTH] Starting practitioner signup process", {
6952
+ email: data.email,
6953
+ hasToken: !!data.token
6954
+ });
6955
+ try {
6956
+ await practitionerSignupSchema.parseAsync(data);
6957
+ console.log("[AUTH] Practitioner signup data validation passed");
6958
+ } catch (validationError) {
6959
+ console.error(
6960
+ "[AUTH] Validation error in signUpPractitioner:",
6961
+ validationError
6962
+ );
6963
+ throw validationError;
6964
+ }
6965
+ console.log("[AUTH] Creating Firebase user");
6966
+ let firebaseUser;
6967
+ try {
6968
+ const result = await createUserWithEmailAndPassword(
6969
+ this.auth,
6970
+ data.email,
6971
+ data.password
6972
+ );
6973
+ firebaseUser = result.user;
6974
+ console.log("[AUTH] Firebase user created successfully", {
6975
+ uid: firebaseUser.uid
6976
+ });
6977
+ } catch (firebaseError) {
6978
+ console.error("[AUTH] Firebase user creation failed:", firebaseError);
6979
+ throw firebaseError;
6980
+ }
6981
+ console.log("[AUTH] Creating user with PRACTITIONER role");
6982
+ let user;
6983
+ try {
6984
+ user = await this.userService.createUser(
6985
+ firebaseUser,
6986
+ ["practitioner" /* PRACTITIONER */],
6987
+ {
6988
+ skipProfileCreation: true
6989
+ // We'll create the profile separately
6990
+ }
6991
+ );
6992
+ console.log("[AUTH] User with PRACTITIONER role created successfully", {
6993
+ userId: user.uid
6994
+ });
6995
+ } catch (userCreationError) {
6996
+ console.error("[AUTH] User creation failed:", userCreationError);
6997
+ throw userCreationError;
6998
+ }
6999
+ console.log("[AUTH] Initializing practitioner service");
7000
+ const practitionerService = new PractitionerService(
7001
+ this.db,
7002
+ this.auth,
7003
+ this.app
7004
+ );
7005
+ let practitioner = null;
7006
+ if (data.token) {
7007
+ console.log("[AUTH] Token provided, attempting to claim draft profile");
7008
+ try {
7009
+ practitioner = await practitionerService.validateTokenAndClaimProfile(
7010
+ data.token,
7011
+ firebaseUser.uid
7012
+ );
7013
+ if (!practitioner) {
7014
+ throw new Error("Invalid or expired invitation token");
7015
+ }
7016
+ console.log("[AUTH] Successfully claimed draft profile", {
7017
+ practitionerId: practitioner.id
7018
+ });
7019
+ await this.userService.updateUser(firebaseUser.uid, {
7020
+ practitionerProfile: practitioner.id
7021
+ });
7022
+ console.log(
7023
+ "[AUTH] User updated with practitioner profile reference"
7024
+ );
7025
+ } catch (tokenError) {
7026
+ console.error("[AUTH] Failed to claim draft profile:", tokenError);
7027
+ throw tokenError;
7028
+ }
7029
+ } else {
7030
+ console.log("[AUTH] Creating new practitioner profile");
7031
+ if (!data.profileData) {
7032
+ data.profileData = {};
7033
+ }
7034
+ const basicInfo = {
7035
+ firstName: data.firstName,
7036
+ lastName: data.lastName,
7037
+ email: data.email,
7038
+ phoneNumber: ((_a = data.profileData.basicInfo) == null ? void 0 : _a.phoneNumber) || "",
7039
+ profileImageUrl: ((_b = data.profileData.basicInfo) == null ? void 0 : _b.profileImageUrl) || "",
7040
+ gender: ((_c = data.profileData.basicInfo) == null ? void 0 : _c.gender) || "other",
7041
+ // Default to "other" if not provided
7042
+ bio: ((_d = data.profileData.basicInfo) == null ? void 0 : _d.bio) || "",
7043
+ title: "Practitioner",
7044
+ // Default title
7045
+ dateOfBirth: /* @__PURE__ */ new Date(),
7046
+ // Default to today
7047
+ languages: ["English"]
7048
+ // Default language
7049
+ };
7050
+ const certification = data.profileData.certification || {
7051
+ level: "aesthetician" /* AESTHETICIAN */,
7052
+ specialties: [],
7053
+ licenseNumber: "Pending",
7054
+ issuingAuthority: "Pending",
7055
+ issueDate: /* @__PURE__ */ new Date(),
7056
+ verificationStatus: "pending"
7057
+ };
7058
+ const createPractitionerData = {
7059
+ userRef: firebaseUser.uid,
7060
+ basicInfo,
7061
+ certification,
7062
+ status: "active" /* ACTIVE */,
7063
+ isActive: true,
7064
+ isVerified: false
7065
+ };
7066
+ try {
7067
+ practitioner = await practitionerService.createPractitioner(
7068
+ createPractitionerData
7069
+ );
7070
+ console.log("[AUTH] Practitioner profile created successfully", {
7071
+ practitionerId: practitioner.id
7072
+ });
7073
+ await this.userService.updateUser(firebaseUser.uid, {
7074
+ practitionerProfile: practitioner.id
7075
+ });
7076
+ console.log(
7077
+ "[AUTH] User updated with practitioner profile reference"
7078
+ );
7079
+ } catch (createError) {
7080
+ console.error(
7081
+ "[AUTH] Failed to create practitioner profile:",
7082
+ createError
7083
+ );
7084
+ throw createError;
7085
+ }
7086
+ }
7087
+ console.log("[AUTH] Practitioner signup completed successfully", {
7088
+ userId: user.uid,
7089
+ practitionerId: (practitioner == null ? void 0 : practitioner.id) || "unknown"
7090
+ });
7091
+ return {
7092
+ user,
7093
+ practitioner
7094
+ };
7095
+ } catch (error) {
7096
+ if (error instanceof z18.ZodError) {
7097
+ console.error(
7098
+ "[AUTH] Zod validation error in signUpPractitioner:",
7099
+ JSON.stringify(error.errors, null, 2)
7100
+ );
7101
+ throw AUTH_ERRORS.VALIDATION_ERROR;
7102
+ }
7103
+ const firebaseError = error;
7104
+ if (firebaseError.code === "auth/email-already-in-use" /* EMAIL_ALREADY_IN_USE */) {
7105
+ console.error("[AUTH] Email already in use:", data.email);
7106
+ throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
7107
+ }
7108
+ console.error("[AUTH] Unhandled error in signUpPractitioner:", error);
7109
+ throw error;
7110
+ }
7111
+ }
7112
+ /**
7113
+ * Signs in a user with email and password specifically for practitioner role
7114
+ * @param email - User's email
7115
+ * @param password - User's password
7116
+ * @returns Object containing the user and practitioner profile
7117
+ * @throws {AUTH_ERRORS.INVALID_ROLE} If user doesn't have practitioner role
7118
+ * @throws {AUTH_ERRORS.NOT_FOUND} If practitioner profile is not found
7119
+ */
7120
+ async signInPractitioner(email, password) {
7121
+ var _a;
7122
+ try {
7123
+ console.log("[AUTH] Starting practitioner signin process", {
7124
+ email
7125
+ });
7126
+ const practitionerService = new PractitionerService(
7127
+ this.db,
7128
+ this.auth,
7129
+ this.app
7130
+ );
7131
+ const { user: firebaseUser } = await signInWithEmailAndPassword(
7132
+ this.auth,
7133
+ email,
7134
+ password
7135
+ );
7136
+ const user = await this.userService.getOrCreateUser(firebaseUser);
7137
+ console.log("[AUTH] User retrieved", { uid: user.uid });
7138
+ if (!((_a = user.roles) == null ? void 0 : _a.includes("practitioner" /* PRACTITIONER */))) {
7139
+ console.error("[AUTH] User is not a practitioner:", user.uid);
7140
+ throw AUTH_ERRORS.INVALID_ROLE;
7141
+ }
7142
+ if (!user.practitionerProfile) {
7143
+ console.error("[AUTH] User has no practitioner profile:", user.uid);
7144
+ throw AUTH_ERRORS.NOT_FOUND;
7145
+ }
7146
+ const practitioner = await practitionerService.getPractitioner(
7147
+ user.practitionerProfile
7148
+ );
7149
+ if (!practitioner) {
7150
+ console.error(
7151
+ "[AUTH] Practitioner profile not found:",
7152
+ user.practitionerProfile
7153
+ );
7154
+ throw AUTH_ERRORS.NOT_FOUND;
7155
+ }
7156
+ console.log("[AUTH] Practitioner signin completed successfully", {
7157
+ userId: user.uid,
7158
+ practitionerId: practitioner.id
7159
+ });
7160
+ return {
7161
+ user,
7162
+ practitioner
7163
+ };
7164
+ } catch (error) {
7165
+ console.error("[AUTH] Error in signInPractitioner:", error);
7166
+ throw error;
7167
+ }
7168
+ }
6909
7169
  };
6910
7170
 
6911
7171
  // src/services/notifications/notification.service.ts
@@ -13715,6 +13975,7 @@ export {
13715
13975
  practitionerReviewInfoSchema,
13716
13976
  practitionerReviewSchema,
13717
13977
  practitionerSchema,
13978
+ practitionerSignupSchema,
13718
13979
  practitionerTokenSchema,
13719
13980
  practitionerWorkingHoursSchema,
13720
13981
  preRequirementNotificationSchema,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.5.52",
4
+ "version": "1.6.1",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -61,6 +61,16 @@ import { clinicAdminSignupSchema } from "../validations/clinic.schema";
61
61
  import { ClinicGroupService } from "./clinic/clinic-group.service";
62
62
  import { ClinicAdminService } from "./clinic/clinic-admin.service";
63
63
  import { ClinicService } from "./clinic/clinic.service";
64
+ import {
65
+ Practitioner,
66
+ CreatePractitionerData,
67
+ PractitionerStatus,
68
+ PractitionerBasicInfo,
69
+ PractitionerCertification,
70
+ } from "../types/practitioner";
71
+ import { PractitionerService } from "./practitioner/practitioner.service";
72
+ import { practitionerSignupSchema } from "../validations/practitioner.schema";
73
+ import { CertificationLevel } from "../backoffice/types/static/certification.types";
64
74
 
65
75
  export class AuthService extends BaseService {
66
76
  private googleProvider = new GoogleAuthProvider();
@@ -858,4 +868,289 @@ export class AuthService extends BaseService {
858
868
  throw error;
859
869
  }
860
870
  }
871
+
872
+ /**
873
+ * Registers a new practitioner user with email and password
874
+ * Can either create a new practitioner profile or claim an existing draft profile with a token
875
+ *
876
+ * @param data - Practitioner signup data containing either new profile details or token for claiming draft profile
877
+ * @returns Object containing the created user and practitioner profile
878
+ */
879
+ async signUpPractitioner(data: {
880
+ email: string;
881
+ password: string;
882
+ firstName: string;
883
+ lastName: string;
884
+ token?: string;
885
+ profileData?: Partial<CreatePractitionerData>;
886
+ }): Promise<{
887
+ user: User;
888
+ practitioner: Practitioner;
889
+ }> {
890
+ try {
891
+ console.log("[AUTH] Starting practitioner signup process", {
892
+ email: data.email,
893
+ hasToken: !!data.token,
894
+ });
895
+
896
+ // Validate data
897
+ try {
898
+ await practitionerSignupSchema.parseAsync(data);
899
+ console.log("[AUTH] Practitioner signup data validation passed");
900
+ } catch (validationError) {
901
+ console.error(
902
+ "[AUTH] Validation error in signUpPractitioner:",
903
+ validationError
904
+ );
905
+ throw validationError;
906
+ }
907
+
908
+ // Create Firebase user
909
+ console.log("[AUTH] Creating Firebase user");
910
+ let firebaseUser;
911
+ try {
912
+ const result = await createUserWithEmailAndPassword(
913
+ this.auth,
914
+ data.email,
915
+ data.password
916
+ );
917
+ firebaseUser = result.user;
918
+ console.log("[AUTH] Firebase user created successfully", {
919
+ uid: firebaseUser.uid,
920
+ });
921
+ } catch (firebaseError) {
922
+ console.error("[AUTH] Firebase user creation failed:", firebaseError);
923
+ throw firebaseError;
924
+ }
925
+
926
+ // Create user with PRACTITIONER role
927
+ console.log("[AUTH] Creating user with PRACTITIONER role");
928
+ let user;
929
+ try {
930
+ user = await this.userService.createUser(
931
+ firebaseUser,
932
+ [UserRole.PRACTITIONER],
933
+ {
934
+ skipProfileCreation: true, // We'll create the profile separately
935
+ }
936
+ );
937
+ console.log("[AUTH] User with PRACTITIONER role created successfully", {
938
+ userId: user.uid,
939
+ });
940
+ } catch (userCreationError) {
941
+ console.error("[AUTH] User creation failed:", userCreationError);
942
+ throw userCreationError;
943
+ }
944
+
945
+ // Initialize practitioner service
946
+ console.log("[AUTH] Initializing practitioner service");
947
+ const practitionerService = new PractitionerService(
948
+ this.db,
949
+ this.auth,
950
+ this.app
951
+ );
952
+
953
+ let practitioner: Practitioner | null = null;
954
+
955
+ // Check if we're claiming an existing draft profile with a token
956
+ if (data.token) {
957
+ console.log("[AUTH] Token provided, attempting to claim draft profile");
958
+
959
+ try {
960
+ // Validate token and claim the profile
961
+ practitioner = await practitionerService.validateTokenAndClaimProfile(
962
+ data.token,
963
+ firebaseUser.uid
964
+ );
965
+
966
+ if (!practitioner) {
967
+ throw new Error("Invalid or expired invitation token");
968
+ }
969
+
970
+ console.log("[AUTH] Successfully claimed draft profile", {
971
+ practitionerId: practitioner.id,
972
+ });
973
+
974
+ // Link the practitioner profile to the user
975
+ await this.userService.updateUser(firebaseUser.uid, {
976
+ practitionerProfile: practitioner.id,
977
+ });
978
+ console.log(
979
+ "[AUTH] User updated with practitioner profile reference"
980
+ );
981
+ } catch (tokenError) {
982
+ console.error("[AUTH] Failed to claim draft profile:", tokenError);
983
+ throw tokenError;
984
+ }
985
+ } else {
986
+ console.log("[AUTH] Creating new practitioner profile");
987
+
988
+ if (!data.profileData) {
989
+ data.profileData = {};
990
+ }
991
+
992
+ // We need to create a full PractitionerBasicInfo object
993
+ const basicInfo: PractitionerBasicInfo = {
994
+ firstName: data.firstName,
995
+ lastName: data.lastName,
996
+ email: data.email,
997
+ phoneNumber: data.profileData.basicInfo?.phoneNumber || "",
998
+ profileImageUrl: data.profileData.basicInfo?.profileImageUrl || "",
999
+ gender: data.profileData.basicInfo?.gender || "other", // Default to "other" if not provided
1000
+ bio: data.profileData.basicInfo?.bio || "",
1001
+ title: "Practitioner", // Default title
1002
+ dateOfBirth: new Date(), // Default to today
1003
+ languages: ["English"], // Default language
1004
+ };
1005
+
1006
+ // Basic certification information with placeholders
1007
+ const certification: PractitionerCertification = data.profileData
1008
+ .certification || {
1009
+ level: CertificationLevel.AESTHETICIAN,
1010
+ specialties: [],
1011
+ licenseNumber: "Pending",
1012
+ issuingAuthority: "Pending",
1013
+ issueDate: new Date(),
1014
+ verificationStatus: "pending",
1015
+ };
1016
+
1017
+ // Create basic profile data
1018
+ const createPractitionerData: CreatePractitionerData = {
1019
+ userRef: firebaseUser.uid,
1020
+ basicInfo,
1021
+ certification,
1022
+ status: PractitionerStatus.ACTIVE,
1023
+ isActive: true,
1024
+ isVerified: false,
1025
+ };
1026
+
1027
+ try {
1028
+ practitioner = await practitionerService.createPractitioner(
1029
+ createPractitionerData
1030
+ );
1031
+ console.log("[AUTH] Practitioner profile created successfully", {
1032
+ practitionerId: practitioner.id,
1033
+ });
1034
+
1035
+ // Link the practitioner profile to the user
1036
+ await this.userService.updateUser(firebaseUser.uid, {
1037
+ practitionerProfile: practitioner.id,
1038
+ });
1039
+ console.log(
1040
+ "[AUTH] User updated with practitioner profile reference"
1041
+ );
1042
+ } catch (createError) {
1043
+ console.error(
1044
+ "[AUTH] Failed to create practitioner profile:",
1045
+ createError
1046
+ );
1047
+ throw createError;
1048
+ }
1049
+ }
1050
+
1051
+ console.log("[AUTH] Practitioner signup completed successfully", {
1052
+ userId: user.uid,
1053
+ practitionerId: practitioner?.id || "unknown",
1054
+ });
1055
+
1056
+ return {
1057
+ user,
1058
+ practitioner,
1059
+ };
1060
+ } catch (error) {
1061
+ if (error instanceof z.ZodError) {
1062
+ console.error(
1063
+ "[AUTH] Zod validation error in signUpPractitioner:",
1064
+ JSON.stringify(error.errors, null, 2)
1065
+ );
1066
+ throw AUTH_ERRORS.VALIDATION_ERROR;
1067
+ }
1068
+
1069
+ const firebaseError = error as FirebaseError;
1070
+ if (firebaseError.code === FirebaseErrorCode.EMAIL_ALREADY_IN_USE) {
1071
+ console.error("[AUTH] Email already in use:", data.email);
1072
+ throw AUTH_ERRORS.EMAIL_ALREADY_EXISTS;
1073
+ }
1074
+
1075
+ console.error("[AUTH] Unhandled error in signUpPractitioner:", error);
1076
+ throw error;
1077
+ }
1078
+ }
1079
+
1080
+ /**
1081
+ * Signs in a user with email and password specifically for practitioner role
1082
+ * @param email - User's email
1083
+ * @param password - User's password
1084
+ * @returns Object containing the user and practitioner profile
1085
+ * @throws {AUTH_ERRORS.INVALID_ROLE} If user doesn't have practitioner role
1086
+ * @throws {AUTH_ERRORS.NOT_FOUND} If practitioner profile is not found
1087
+ */
1088
+ async signInPractitioner(
1089
+ email: string,
1090
+ password: string
1091
+ ): Promise<{
1092
+ user: User;
1093
+ practitioner: Practitioner;
1094
+ }> {
1095
+ try {
1096
+ console.log("[AUTH] Starting practitioner signin process", {
1097
+ email: email,
1098
+ });
1099
+
1100
+ // Initialize required service
1101
+ const practitionerService = new PractitionerService(
1102
+ this.db,
1103
+ this.auth,
1104
+ this.app
1105
+ );
1106
+
1107
+ // Sign in with email/password
1108
+ const { user: firebaseUser } = await signInWithEmailAndPassword(
1109
+ this.auth,
1110
+ email,
1111
+ password
1112
+ );
1113
+
1114
+ // Get or create user
1115
+ const user = await this.userService.getOrCreateUser(firebaseUser);
1116
+ console.log("[AUTH] User retrieved", { uid: user.uid });
1117
+
1118
+ // Check if user has practitioner role
1119
+ if (!user.roles?.includes(UserRole.PRACTITIONER)) {
1120
+ console.error("[AUTH] User is not a practitioner:", user.uid);
1121
+ throw AUTH_ERRORS.INVALID_ROLE;
1122
+ }
1123
+
1124
+ // Check and get practitioner profile
1125
+ if (!user.practitionerProfile) {
1126
+ console.error("[AUTH] User has no practitioner profile:", user.uid);
1127
+ throw AUTH_ERRORS.NOT_FOUND;
1128
+ }
1129
+
1130
+ // Get practitioner profile
1131
+ const practitioner = await practitionerService.getPractitioner(
1132
+ user.practitionerProfile
1133
+ );
1134
+ if (!practitioner) {
1135
+ console.error(
1136
+ "[AUTH] Practitioner profile not found:",
1137
+ user.practitionerProfile
1138
+ );
1139
+ throw AUTH_ERRORS.NOT_FOUND;
1140
+ }
1141
+
1142
+ console.log("[AUTH] Practitioner signin completed successfully", {
1143
+ userId: user.uid,
1144
+ practitionerId: practitioner.id,
1145
+ });
1146
+
1147
+ return {
1148
+ user,
1149
+ practitioner,
1150
+ };
1151
+ } catch (error) {
1152
+ console.error("[AUTH] Error in signInPractitioner:", error);
1153
+ throw error;
1154
+ }
1155
+ }
861
1156
  }
@@ -176,21 +176,28 @@ export class PractitionerService extends BaseService {
176
176
  throw new Error(`Clinic ${clinicId} not found`);
177
177
  }
178
178
 
179
- // Priprema podataka za kreiranje profila
180
- const clinics = data.clinics || [clinicId];
179
+ // Make sure the primary clinic (clinicId) is always included
180
+ // Merge the clinics array with the primary clinicId, avoiding duplicates
181
+ const clinicsToAdd = new Set<string>([clinicId]);
181
182
 
182
- // Provera da li sve dodatno navedene klinike postoje
183
- if (data.clinics) {
183
+ // Add additional clinics if provided
184
+ if (data.clinics && data.clinics.length > 0) {
184
185
  for (const cId of data.clinics) {
186
+ // Verify each additional clinic exists
185
187
  if (cId !== clinicId) {
188
+ // Skip checking the primary clinic again
186
189
  const otherClinic = await this.getClinicService().getClinic(cId);
187
190
  if (!otherClinic) {
188
191
  throw new Error(`Clinic ${cId} not found`);
189
192
  }
190
193
  }
194
+ clinicsToAdd.add(cId);
191
195
  }
192
196
  }
193
197
 
198
+ // Convert Set to Array
199
+ const clinics = Array.from(clinicsToAdd);
200
+
194
201
  // Initialize default review info for new practitioners
195
202
  const defaultReviewInfo: PractitionerReviewInfo = {
196
203
  totalReviews: 0,
@@ -206,7 +213,30 @@ export class PractitionerService extends BaseService {
206
213
  // Generate ID for the new practitioner
207
214
  const practitionerId = this.generateId();
208
215
 
209
- const clinicsInfo = validatedData.clinicsInfo || [];
216
+ // Create clinicsInfo from the merged clinics array
217
+ const clinicsInfo: ClinicInfo[] = [];
218
+
219
+ // Populate clinicsInfo for each clinic
220
+ for (const cId of clinics) {
221
+ const clinicData = await this.getClinicService().getClinic(cId);
222
+ if (clinicData) {
223
+ clinicsInfo.push({
224
+ id: clinicData.id,
225
+ name: clinicData.name,
226
+ location: clinicData.location,
227
+ contactInfo: clinicData.contactInfo,
228
+ featuredPhoto: clinicData.coverPhoto || "",
229
+ description: clinicData.description || null,
230
+ });
231
+ }
232
+ }
233
+
234
+ // Use provided clinicsInfo if available, otherwise use the ones we just created
235
+ const finalClinicsInfo =
236
+ validatedData.clinicsInfo && validatedData.clinicsInfo.length > 0
237
+ ? validatedData.clinicsInfo
238
+ : clinicsInfo;
239
+
210
240
  const proceduresInfo: ProcedureSummaryInfo[] = [];
211
241
 
212
242
  const practitionerData: Omit<Practitioner, "createdAt" | "updatedAt"> & {
@@ -219,7 +249,7 @@ export class PractitionerService extends BaseService {
219
249
  certification: validatedData.certification,
220
250
  clinics: clinics,
221
251
  clinicWorkingHours: validatedData.clinicWorkingHours || [],
222
- clinicsInfo: clinicsInfo,
252
+ clinicsInfo: finalClinicsInfo,
223
253
  procedures: [],
224
254
  proceduresInfo: proceduresInfo,
225
255
  reviewInfo: defaultReviewInfo,
@@ -189,3 +189,27 @@ export const createPractitionerTokenSchema = z.object({
189
189
  clinicId: z.string().min(1),
190
190
  expiresAt: z.date().optional(),
191
191
  });
192
+
193
+ /**
194
+ * Simplified schema for practitioner signup
195
+ */
196
+ export const practitionerSignupSchema = z.object({
197
+ email: z.string().email(),
198
+ password: z.string().min(8),
199
+ firstName: z.string().min(2).max(50),
200
+ lastName: z.string().min(2).max(50),
201
+ token: z.string().optional(),
202
+ profileData: z
203
+ .object({
204
+ basicInfo: z
205
+ .object({
206
+ phoneNumber: z.string().optional(),
207
+ profileImageUrl: z.string().optional(),
208
+ gender: z.enum(["male", "female", "other"]).optional(),
209
+ bio: z.string().optional(),
210
+ })
211
+ .optional(),
212
+ certification: z.any().optional(),
213
+ })
214
+ .optional(),
215
+ });