@blackcode_sa/metaestetics-api 1.5.0 → 1.5.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.js CHANGED
@@ -71,8 +71,11 @@ __export(index_exports, {
71
71
  PatientService: () => PatientService,
72
72
  PracticeType: () => PracticeType,
73
73
  PractitionerService: () => PractitionerService,
74
+ PractitionerStatus: () => PractitionerStatus,
75
+ PractitionerTokenStatus: () => PractitionerTokenStatus,
74
76
  PricingMeasure: () => PricingMeasure,
75
77
  ProcedureFamily: () => ProcedureFamily,
78
+ REGISTER_TOKENS_COLLECTION: () => REGISTER_TOKENS_COLLECTION,
76
79
  SYNCED_CALENDARS_COLLECTION: () => SYNCED_CALENDARS_COLLECTION,
77
80
  SubscriptionModel: () => SubscriptionModel,
78
81
  SyncedCalendarProvider: () => SyncedCalendarProvider,
@@ -117,11 +120,13 @@ __export(index_exports, {
117
120
  createClinicSchema: () => createClinicSchema,
118
121
  createDefaultClinicGroupSchema: () => createDefaultClinicGroupSchema,
119
122
  createDocumentTemplateSchema: () => createDocumentTemplateSchema,
123
+ createDraftPractitionerSchema: () => createDraftPractitionerSchema,
120
124
  createPatientLocationInfoSchema: () => createPatientLocationInfoSchema,
121
125
  createPatientMedicalInfoSchema: () => createPatientMedicalInfoSchema,
122
126
  createPatientProfileSchema: () => createPatientProfileSchema,
123
127
  createPatientSensitiveInfoSchema: () => createPatientSensitiveInfoSchema,
124
128
  createPractitionerSchema: () => createPractitionerSchema,
129
+ createPractitionerTokenSchema: () => createPractitionerTokenSchema,
125
130
  createUserOptionsSchema: () => createUserOptionsSchema,
126
131
  doctorInfoSchema: () => doctorInfoSchema,
127
132
  documentElementSchema: () => documentElementSchema,
@@ -154,6 +159,7 @@ __export(index_exports, {
154
159
  practitionerProfileInfoSchema: () => practitionerProfileInfoSchema,
155
160
  practitionerReviewSchema: () => practitionerReviewSchema,
156
161
  practitionerSchema: () => practitionerSchema,
162
+ practitionerTokenSchema: () => practitionerTokenSchema,
157
163
  practitionerWorkingHoursSchema: () => practitionerWorkingHoursSchema,
158
164
  preRequirementNotificationSchema: () => preRequirementNotificationSchema,
159
165
  procedureCategorizationSchema: () => procedureCategorizationSchema,
@@ -3135,6 +3141,19 @@ var import_firestore13 = require("firebase/firestore");
3135
3141
 
3136
3142
  // src/types/practitioner/index.ts
3137
3143
  var PRACTITIONERS_COLLECTION = "practitioners";
3144
+ var REGISTER_TOKENS_COLLECTION = "register_tokens";
3145
+ var PractitionerStatus = /* @__PURE__ */ ((PractitionerStatus2) => {
3146
+ PractitionerStatus2["DRAFT"] = "draft";
3147
+ PractitionerStatus2["ACTIVE"] = "active";
3148
+ return PractitionerStatus2;
3149
+ })(PractitionerStatus || {});
3150
+ var PractitionerTokenStatus = /* @__PURE__ */ ((PractitionerTokenStatus2) => {
3151
+ PractitionerTokenStatus2["ACTIVE"] = "active";
3152
+ PractitionerTokenStatus2["USED"] = "used";
3153
+ PractitionerTokenStatus2["EXPIRED"] = "expired";
3154
+ PractitionerTokenStatus2["REVOKED"] = "revoked";
3155
+ return PractitionerTokenStatus2;
3156
+ })(PractitionerTokenStatus || {});
3138
3157
 
3139
3158
  // src/validations/practitioner.schema.ts
3140
3159
  var import_zod10 = require("zod");
@@ -3246,6 +3265,7 @@ var practitionerSchema = import_zod10.z.object({
3246
3265
  clinicWorkingHours: import_zod10.z.array(practitionerClinicWorkingHoursSchema),
3247
3266
  isActive: import_zod10.z.boolean(),
3248
3267
  isVerified: import_zod10.z.boolean(),
3268
+ status: import_zod10.z.nativeEnum(PractitionerStatus),
3249
3269
  createdAt: import_zod10.z.instanceof(import_firestore12.Timestamp),
3250
3270
  updatedAt: import_zod10.z.instanceof(import_firestore12.Timestamp)
3251
3271
  });
@@ -3256,7 +3276,35 @@ var createPractitionerSchema = import_zod10.z.object({
3256
3276
  clinics: import_zod10.z.array(import_zod10.z.string()).optional(),
3257
3277
  clinicWorkingHours: import_zod10.z.array(practitionerClinicWorkingHoursSchema).optional(),
3258
3278
  isActive: import_zod10.z.boolean(),
3259
- isVerified: import_zod10.z.boolean()
3279
+ isVerified: import_zod10.z.boolean(),
3280
+ status: import_zod10.z.nativeEnum(PractitionerStatus).optional()
3281
+ });
3282
+ var createDraftPractitionerSchema = import_zod10.z.object({
3283
+ basicInfo: practitionerBasicInfoSchema,
3284
+ certification: practitionerCertificationSchema,
3285
+ clinics: import_zod10.z.array(import_zod10.z.string()).optional(),
3286
+ clinicWorkingHours: import_zod10.z.array(practitionerClinicWorkingHoursSchema).optional(),
3287
+ isActive: import_zod10.z.boolean().optional().default(false),
3288
+ isVerified: import_zod10.z.boolean().optional().default(false)
3289
+ });
3290
+ var practitionerTokenSchema = import_zod10.z.object({
3291
+ id: import_zod10.z.string().min(1),
3292
+ token: import_zod10.z.string().min(6),
3293
+ practitionerId: import_zod10.z.string().min(1),
3294
+ email: import_zod10.z.string().email(),
3295
+ clinicId: import_zod10.z.string().min(1),
3296
+ status: import_zod10.z.nativeEnum(PractitionerTokenStatus),
3297
+ createdBy: import_zod10.z.string().min(1),
3298
+ createdAt: import_zod10.z.instanceof(import_firestore12.Timestamp),
3299
+ expiresAt: import_zod10.z.instanceof(import_firestore12.Timestamp),
3300
+ usedBy: import_zod10.z.string().optional(),
3301
+ usedAt: import_zod10.z.instanceof(import_firestore12.Timestamp).optional()
3302
+ });
3303
+ var createPractitionerTokenSchema = import_zod10.z.object({
3304
+ practitionerId: import_zod10.z.string().min(1),
3305
+ email: import_zod10.z.string().email(),
3306
+ clinicId: import_zod10.z.string().min(1),
3307
+ expiresAt: import_zod10.z.date().optional()
3260
3308
  });
3261
3309
 
3262
3310
  // src/services/practitioner/practitioner.service.ts
@@ -3307,6 +3355,7 @@ var PractitionerService = class extends BaseService {
3307
3355
  clinicWorkingHours: validatedData.clinicWorkingHours || [],
3308
3356
  isActive: validatedData.isActive,
3309
3357
  isVerified: validatedData.isVerified,
3358
+ status: validatedData.status || "active" /* ACTIVE */,
3310
3359
  createdAt: (0, import_firestore13.serverTimestamp)(),
3311
3360
  updatedAt: (0, import_firestore13.serverTimestamp)()
3312
3361
  };
@@ -3331,6 +3380,200 @@ var PractitionerService = class extends BaseService {
3331
3380
  throw error;
3332
3381
  }
3333
3382
  }
3383
+ /**
3384
+ * Kreira novi draft profil zdravstvenog radnika bez povezanog korisnika
3385
+ * Koristi se od strane administratora klinike za kreiranje profila i kasnije pozivanje
3386
+ * @param data Podaci za kreiranje draft profila
3387
+ * @param createdBy ID administratora koji kreira profil
3388
+ * @param clinicId ID klinike za koju se kreira profil
3389
+ * @returns Objekt koji sadrži kreirani draft profil i token za registraciju
3390
+ */
3391
+ async createDraftPractitioner(data, createdBy, clinicId) {
3392
+ try {
3393
+ const validatedData = createDraftPractitionerSchema.parse(data);
3394
+ const clinic = await this.getClinicService().getClinic(clinicId);
3395
+ if (!clinic) {
3396
+ throw new Error(`Clinic ${clinicId} not found`);
3397
+ }
3398
+ const clinics = data.clinics || [clinicId];
3399
+ if (data.clinics) {
3400
+ for (const cId of data.clinics) {
3401
+ if (cId !== clinicId) {
3402
+ const otherClinic = await this.getClinicService().getClinic(cId);
3403
+ if (!otherClinic) {
3404
+ throw new Error(`Clinic ${cId} not found`);
3405
+ }
3406
+ }
3407
+ }
3408
+ }
3409
+ const practitionerId = this.generateId();
3410
+ const practitionerData = {
3411
+ id: practitionerId,
3412
+ userRef: "",
3413
+ // Prazno - biće popunjeno kada korisnik kreira nalog
3414
+ basicInfo: validatedData.basicInfo,
3415
+ certification: validatedData.certification,
3416
+ clinics,
3417
+ clinicWorkingHours: validatedData.clinicWorkingHours || [],
3418
+ isActive: validatedData.isActive !== void 0 ? validatedData.isActive : false,
3419
+ isVerified: validatedData.isVerified !== void 0 ? validatedData.isVerified : false,
3420
+ status: "draft" /* DRAFT */,
3421
+ createdAt: (0, import_firestore13.serverTimestamp)(),
3422
+ updatedAt: (0, import_firestore13.serverTimestamp)()
3423
+ };
3424
+ practitionerSchema.parse({
3425
+ ...practitionerData,
3426
+ userRef: "temp-for-validation",
3427
+ createdAt: import_firestore13.Timestamp.now(),
3428
+ updatedAt: import_firestore13.Timestamp.now()
3429
+ });
3430
+ await (0, import_firestore13.setDoc)(
3431
+ (0, import_firestore13.doc)(this.db, PRACTITIONERS_COLLECTION, practitionerData.id),
3432
+ practitionerData
3433
+ );
3434
+ const savedPractitioner = await this.getPractitioner(practitionerData.id);
3435
+ if (!savedPractitioner) {
3436
+ throw new Error("Failed to create draft practitioner profile");
3437
+ }
3438
+ const tokenString = this.generateId().slice(0, 6).toUpperCase();
3439
+ const expiration = new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3);
3440
+ const token = {
3441
+ id: this.generateId(),
3442
+ token: tokenString,
3443
+ practitionerId,
3444
+ email: practitionerData.basicInfo.email,
3445
+ clinicId,
3446
+ status: "active" /* ACTIVE */,
3447
+ createdBy,
3448
+ createdAt: import_firestore13.Timestamp.now(),
3449
+ expiresAt: import_firestore13.Timestamp.fromDate(expiration)
3450
+ };
3451
+ practitionerTokenSchema.parse(token);
3452
+ const tokenPath = `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
3453
+ await (0, import_firestore13.setDoc)((0, import_firestore13.doc)(this.db, tokenPath), token);
3454
+ return { practitioner: savedPractitioner, token };
3455
+ } catch (error) {
3456
+ if (error instanceof import_zod11.z.ZodError) {
3457
+ throw new Error("Invalid practitioner data: " + error.message);
3458
+ }
3459
+ throw error;
3460
+ }
3461
+ }
3462
+ /**
3463
+ * Creates a token for inviting practitioner to claim their profile
3464
+ * @param data Data for creating token
3465
+ * @param createdBy ID of the user creating the token
3466
+ * @returns Created token
3467
+ */
3468
+ async createPractitionerToken(data, createdBy) {
3469
+ try {
3470
+ const validatedData = createPractitionerTokenSchema.parse(data);
3471
+ const practitioner = await this.getPractitioner(
3472
+ validatedData.practitionerId
3473
+ );
3474
+ if (!practitioner) {
3475
+ throw new Error("Practitioner not found");
3476
+ }
3477
+ if (practitioner.status !== "draft" /* DRAFT */) {
3478
+ throw new Error(
3479
+ "Can only create tokens for practitioners in DRAFT status"
3480
+ );
3481
+ }
3482
+ const clinic = await this.getClinicService().getClinic(
3483
+ validatedData.clinicId
3484
+ );
3485
+ if (!clinic) {
3486
+ throw new Error(`Clinic ${validatedData.clinicId} not found`);
3487
+ }
3488
+ if (!practitioner.clinics.includes(validatedData.clinicId)) {
3489
+ throw new Error("Practitioner is not associated with this clinic");
3490
+ }
3491
+ const expiration = validatedData.expiresAt || new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3);
3492
+ const tokenString = this.generateId().slice(0, 6).toUpperCase();
3493
+ const token = {
3494
+ id: this.generateId(),
3495
+ token: tokenString,
3496
+ practitionerId: validatedData.practitionerId,
3497
+ email: validatedData.email,
3498
+ clinicId: validatedData.clinicId,
3499
+ status: "active" /* ACTIVE */,
3500
+ createdBy,
3501
+ createdAt: import_firestore13.Timestamp.now(),
3502
+ expiresAt: import_firestore13.Timestamp.fromDate(expiration)
3503
+ };
3504
+ practitionerTokenSchema.parse(token);
3505
+ const tokenPath = `${PRACTITIONERS_COLLECTION}/${validatedData.practitionerId}/${REGISTER_TOKENS_COLLECTION}/${token.id}`;
3506
+ await (0, import_firestore13.setDoc)((0, import_firestore13.doc)(this.db, tokenPath), token);
3507
+ return token;
3508
+ } catch (error) {
3509
+ if (error instanceof import_zod11.z.ZodError) {
3510
+ throw new Error("Invalid token data: " + error.message);
3511
+ }
3512
+ throw error;
3513
+ }
3514
+ }
3515
+ /**
3516
+ * Gets active tokens for a practitioner
3517
+ * @param practitionerId ID of the practitioner
3518
+ * @returns Array of active tokens
3519
+ */
3520
+ async getPractitionerActiveTokens(practitionerId) {
3521
+ const tokensRef = (0, import_firestore13.collection)(
3522
+ this.db,
3523
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
3524
+ );
3525
+ const q = (0, import_firestore13.query)(
3526
+ tokensRef,
3527
+ (0, import_firestore13.where)("status", "==", "active" /* ACTIVE */),
3528
+ (0, import_firestore13.where)("expiresAt", ">", import_firestore13.Timestamp.now())
3529
+ );
3530
+ const querySnapshot = await (0, import_firestore13.getDocs)(q);
3531
+ return querySnapshot.docs.map((doc20) => doc20.data());
3532
+ }
3533
+ /**
3534
+ * Gets a token by its string value and validates it
3535
+ * @param tokenString The token string to find
3536
+ * @returns The token if found and valid, null otherwise
3537
+ */
3538
+ async validateToken(tokenString) {
3539
+ const practitionersRef = (0, import_firestore13.collection)(this.db, PRACTITIONERS_COLLECTION);
3540
+ const practitionersSnapshot = await (0, import_firestore13.getDocs)(practitionersRef);
3541
+ for (const practitionerDoc of practitionersSnapshot.docs) {
3542
+ const practitionerId = practitionerDoc.id;
3543
+ const tokensRef = (0, import_firestore13.collection)(
3544
+ this.db,
3545
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}`
3546
+ );
3547
+ const q = (0, import_firestore13.query)(
3548
+ tokensRef,
3549
+ (0, import_firestore13.where)("token", "==", tokenString),
3550
+ (0, import_firestore13.where)("status", "==", "active" /* ACTIVE */),
3551
+ (0, import_firestore13.where)("expiresAt", ">", import_firestore13.Timestamp.now())
3552
+ );
3553
+ const tokenSnapshot = await (0, import_firestore13.getDocs)(q);
3554
+ if (!tokenSnapshot.empty) {
3555
+ return tokenSnapshot.docs[0].data();
3556
+ }
3557
+ }
3558
+ return null;
3559
+ }
3560
+ /**
3561
+ * Marks a token as used
3562
+ * @param tokenId ID of the token
3563
+ * @param practitionerId ID of the practitioner
3564
+ * @param userId ID of the user using the token
3565
+ */
3566
+ async markTokenAsUsed(tokenId, practitionerId, userId) {
3567
+ const tokenRef = (0, import_firestore13.doc)(
3568
+ this.db,
3569
+ `${PRACTITIONERS_COLLECTION}/${practitionerId}/${REGISTER_TOKENS_COLLECTION}/${tokenId}`
3570
+ );
3571
+ await (0, import_firestore13.updateDoc)(tokenRef, {
3572
+ status: "used" /* USED */,
3573
+ usedBy: userId,
3574
+ usedAt: import_firestore13.Timestamp.now()
3575
+ });
3576
+ }
3334
3577
  /**
3335
3578
  * Dohvata zdravstvenog radnika po ID-u
3336
3579
  */
@@ -3364,7 +3607,20 @@ var PractitionerService = class extends BaseService {
3364
3607
  const q = (0, import_firestore13.query)(
3365
3608
  (0, import_firestore13.collection)(this.db, PRACTITIONERS_COLLECTION),
3366
3609
  (0, import_firestore13.where)("clinics", "array-contains", clinicId),
3367
- (0, import_firestore13.where)("isActive", "==", true)
3610
+ (0, import_firestore13.where)("isActive", "==", true),
3611
+ (0, import_firestore13.where)("status", "==", "active" /* ACTIVE */)
3612
+ );
3613
+ const querySnapshot = await (0, import_firestore13.getDocs)(q);
3614
+ return querySnapshot.docs.map((doc20) => doc20.data());
3615
+ }
3616
+ /**
3617
+ * Dohvata sve draft zdravstvene radnike za određenu kliniku
3618
+ */
3619
+ async getDraftPractitionersByClinic(clinicId) {
3620
+ const q = (0, import_firestore13.query)(
3621
+ (0, import_firestore13.collection)(this.db, PRACTITIONERS_COLLECTION),
3622
+ (0, import_firestore13.where)("clinics", "array-contains", clinicId),
3623
+ (0, import_firestore13.where)("status", "==", "draft" /* DRAFT */)
3368
3624
  );
3369
3625
  const querySnapshot = await (0, import_firestore13.getDocs)(q);
3370
3626
  return querySnapshot.docs.map((doc20) => doc20.data());
@@ -3473,6 +3729,35 @@ var PractitionerService = class extends BaseService {
3473
3729
  }
3474
3730
  await (0, import_firestore13.deleteDoc)((0, import_firestore13.doc)(this.db, PRACTITIONERS_COLLECTION, practitionerId));
3475
3731
  }
3732
+ /**
3733
+ * Validates a registration token and claims the associated draft practitioner profile
3734
+ * @param tokenString The token provided by the practitioner
3735
+ * @param userId The ID of the user claiming the profile
3736
+ * @returns The claimed practitioner profile or null if token is invalid
3737
+ */
3738
+ async validateTokenAndClaimProfile(tokenString, userId) {
3739
+ const token = await this.validateToken(tokenString);
3740
+ if (!token) {
3741
+ return null;
3742
+ }
3743
+ const practitioner = await this.getPractitioner(token.practitionerId);
3744
+ if (!practitioner) {
3745
+ return null;
3746
+ }
3747
+ if (practitioner.status !== "draft" /* DRAFT */) {
3748
+ throw new Error("This practitioner profile has already been claimed");
3749
+ }
3750
+ const existingPractitioner = await this.getPractitionerByUserRef(userId);
3751
+ if (existingPractitioner) {
3752
+ throw new Error("User already has a practitioner profile");
3753
+ }
3754
+ const updatedPractitioner = await this.updatePractitioner(practitioner.id, {
3755
+ userRef: userId,
3756
+ status: "active" /* ACTIVE */
3757
+ });
3758
+ await this.markTokenAsUsed(token.id, token.practitionerId, userId);
3759
+ return updatedPractitioner;
3760
+ }
3476
3761
  };
3477
3762
 
3478
3763
  // src/services/user.service.ts
@@ -9080,8 +9365,11 @@ var notificationSchema = import_zod18.z.discriminatedUnion("notificationType", [
9080
9365
  PatientService,
9081
9366
  PracticeType,
9082
9367
  PractitionerService,
9368
+ PractitionerStatus,
9369
+ PractitionerTokenStatus,
9083
9370
  PricingMeasure,
9084
9371
  ProcedureFamily,
9372
+ REGISTER_TOKENS_COLLECTION,
9085
9373
  SYNCED_CALENDARS_COLLECTION,
9086
9374
  SubscriptionModel,
9087
9375
  SyncedCalendarProvider,
@@ -9126,11 +9414,13 @@ var notificationSchema = import_zod18.z.discriminatedUnion("notificationType", [
9126
9414
  createClinicSchema,
9127
9415
  createDefaultClinicGroupSchema,
9128
9416
  createDocumentTemplateSchema,
9417
+ createDraftPractitionerSchema,
9129
9418
  createPatientLocationInfoSchema,
9130
9419
  createPatientMedicalInfoSchema,
9131
9420
  createPatientProfileSchema,
9132
9421
  createPatientSensitiveInfoSchema,
9133
9422
  createPractitionerSchema,
9423
+ createPractitionerTokenSchema,
9134
9424
  createUserOptionsSchema,
9135
9425
  doctorInfoSchema,
9136
9426
  documentElementSchema,
@@ -9163,6 +9453,7 @@ var notificationSchema = import_zod18.z.discriminatedUnion("notificationType", [
9163
9453
  practitionerProfileInfoSchema,
9164
9454
  practitionerReviewSchema,
9165
9455
  practitionerSchema,
9456
+ practitionerTokenSchema,
9166
9457
  practitionerWorkingHoursSchema,
9167
9458
  preRequirementNotificationSchema,
9168
9459
  procedureCategorizationSchema,