@blackcode_sa/metaestetics-api 1.13.21 → 1.14.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.mjs CHANGED
@@ -11331,6 +11331,9 @@ var PractitionerService = class extends BaseService {
11331
11331
  */
11332
11332
  async processBasicInfo(basicInfo, practitionerId) {
11333
11333
  const processedBasicInfo = { ...basicInfo };
11334
+ if (processedBasicInfo.email) {
11335
+ processedBasicInfo.email = processedBasicInfo.email.toLowerCase().trim();
11336
+ }
11334
11337
  if (basicInfo.profileImageUrl) {
11335
11338
  const uploadedUrl = await this.handleProfilePhotoUpload(
11336
11339
  basicInfo.profileImageUrl,
@@ -11738,6 +11741,260 @@ var PractitionerService = class extends BaseService {
11738
11741
  return null;
11739
11742
  }
11740
11743
  }
11744
+ /**
11745
+ * Finds all draft practitioner profiles by email address
11746
+ * Used when a doctor signs in with Google to show all clinic invitations
11747
+ *
11748
+ * @param email - Email address to search for
11749
+ * @returns Array of draft practitioner profiles with clinic information
11750
+ *
11751
+ * @remarks
11752
+ * Requires Firestore composite index on:
11753
+ * - Collection: practitioners
11754
+ * - Fields: basicInfo.email (Ascending), status (Ascending), userRef (Ascending)
11755
+ */
11756
+ async getDraftProfilesByEmail(email) {
11757
+ try {
11758
+ const normalizedEmail = email.toLowerCase().trim();
11759
+ console.log("[PRACTITIONER] Searching for all draft practitioners by email", {
11760
+ email: normalizedEmail,
11761
+ originalEmail: email
11762
+ });
11763
+ const q = query13(
11764
+ collection13(this.db, PRACTITIONERS_COLLECTION),
11765
+ where13("basicInfo.email", "==", normalizedEmail),
11766
+ where13("status", "==", "draft" /* DRAFT */),
11767
+ where13("userRef", "==", "")
11768
+ );
11769
+ const querySnapshot = await getDocs13(q);
11770
+ if (querySnapshot.empty) {
11771
+ console.log("[PRACTITIONER] No draft practitioners found for email", {
11772
+ email: normalizedEmail,
11773
+ originalEmail: email
11774
+ });
11775
+ const debugQ = query13(
11776
+ collection13(this.db, PRACTITIONERS_COLLECTION),
11777
+ where13("basicInfo.email", "==", normalizedEmail),
11778
+ limit7(5)
11779
+ );
11780
+ const debugSnapshot = await getDocs13(debugQ);
11781
+ console.log("[PRACTITIONER] Debug: Found practitioners with this email (any status):", {
11782
+ count: debugSnapshot.size,
11783
+ practitioners: debugSnapshot.docs.map((doc47) => {
11784
+ var _a;
11785
+ return {
11786
+ id: doc47.id,
11787
+ email: (_a = doc47.data().basicInfo) == null ? void 0 : _a.email,
11788
+ status: doc47.data().status,
11789
+ userRef: doc47.data().userRef
11790
+ };
11791
+ })
11792
+ });
11793
+ return [];
11794
+ }
11795
+ const draftPractitioners = querySnapshot.docs.map(
11796
+ (doc47) => doc47.data()
11797
+ );
11798
+ console.log("[PRACTITIONER] Found draft practitioners", {
11799
+ email: normalizedEmail,
11800
+ count: draftPractitioners.length,
11801
+ practitionerIds: draftPractitioners.map((p) => p.id)
11802
+ });
11803
+ return draftPractitioners;
11804
+ } catch (error) {
11805
+ console.error(
11806
+ "[PRACTITIONER] Error finding draft practitioners by email:",
11807
+ error
11808
+ );
11809
+ return [];
11810
+ }
11811
+ }
11812
+ /**
11813
+ * Claims a draft practitioner profile and links it to a user account
11814
+ * Used when a doctor selects which clinic(s) to join after Google Sign-In
11815
+ *
11816
+ * @param practitionerId - ID of the draft practitioner profile to claim
11817
+ * @param userId - ID of the user account to link the profile to
11818
+ * @returns The claimed practitioner profile
11819
+ */
11820
+ async claimDraftProfileWithGoogle(practitionerId, userId) {
11821
+ try {
11822
+ console.log("[PRACTITIONER] Claiming draft profile with Google", {
11823
+ practitionerId,
11824
+ userId
11825
+ });
11826
+ const practitioner = await this.getPractitioner(practitionerId);
11827
+ if (!practitioner) {
11828
+ throw new Error(`Practitioner ${practitionerId} not found`);
11829
+ }
11830
+ if (practitioner.status !== "draft" /* DRAFT */) {
11831
+ throw new Error("This practitioner profile has already been claimed");
11832
+ }
11833
+ const existingPractitioner = await this.getPractitionerByUserRef(userId);
11834
+ if (existingPractitioner) {
11835
+ console.log("[PRACTITIONER] User already has profile, merging clinics");
11836
+ const mergedClinics = Array.from(/* @__PURE__ */ new Set([
11837
+ ...existingPractitioner.clinics,
11838
+ ...practitioner.clinics
11839
+ ]));
11840
+ const mergedWorkingHours = [...existingPractitioner.clinicWorkingHours];
11841
+ for (const workingHours of practitioner.clinicWorkingHours) {
11842
+ if (!mergedWorkingHours.find((wh) => wh.clinicId === workingHours.clinicId)) {
11843
+ mergedWorkingHours.push(workingHours);
11844
+ }
11845
+ }
11846
+ const mergedClinicsInfo = [...existingPractitioner.clinicsInfo];
11847
+ for (const clinicInfo of practitioner.clinicsInfo) {
11848
+ if (!mergedClinicsInfo.find((ci) => ci.id === clinicInfo.id)) {
11849
+ mergedClinicsInfo.push(clinicInfo);
11850
+ }
11851
+ }
11852
+ const updatedPractitioner2 = await this.updatePractitioner(existingPractitioner.id, {
11853
+ clinics: mergedClinics,
11854
+ clinicWorkingHours: mergedWorkingHours,
11855
+ clinicsInfo: mergedClinicsInfo
11856
+ });
11857
+ await deleteDoc4(doc20(this.db, PRACTITIONERS_COLLECTION, practitionerId));
11858
+ const activeTokens2 = await this.getPractitionerActiveTokens(practitionerId);
11859
+ for (const token of activeTokens2) {
11860
+ await this.markTokenAsUsed(token.id, practitionerId, userId);
11861
+ }
11862
+ return updatedPractitioner2;
11863
+ }
11864
+ const updatedPractitioner = await this.updatePractitioner(practitioner.id, {
11865
+ userRef: userId,
11866
+ status: "active" /* ACTIVE */
11867
+ });
11868
+ const activeTokens = await this.getPractitionerActiveTokens(practitionerId);
11869
+ for (const token of activeTokens) {
11870
+ await this.markTokenAsUsed(token.id, practitionerId, userId);
11871
+ }
11872
+ console.log("[PRACTITIONER] Draft profile claimed successfully", {
11873
+ practitionerId: updatedPractitioner.id,
11874
+ userId
11875
+ });
11876
+ return updatedPractitioner;
11877
+ } catch (error) {
11878
+ console.error(
11879
+ "[PRACTITIONER] Error claiming draft profile with Google:",
11880
+ error
11881
+ );
11882
+ throw error;
11883
+ }
11884
+ }
11885
+ /**
11886
+ * Claims multiple draft practitioner profiles and merges them into one profile
11887
+ * Used when a doctor selects multiple clinics to join after Google Sign-In
11888
+ *
11889
+ * @param practitionerIds - Array of draft practitioner profile IDs to claim
11890
+ * @param userId - ID of the user account to link the profiles to
11891
+ * @returns The claimed practitioner profile (first one becomes main, others merged)
11892
+ */
11893
+ async claimMultipleDraftProfilesWithGoogle(practitionerIds, userId) {
11894
+ try {
11895
+ if (practitionerIds.length === 0) {
11896
+ throw new Error("No practitioner IDs provided");
11897
+ }
11898
+ console.log("[PRACTITIONER] Claiming multiple draft profiles with Google", {
11899
+ practitionerIds,
11900
+ userId,
11901
+ count: practitionerIds.length
11902
+ });
11903
+ const draftProfiles = await Promise.all(
11904
+ practitionerIds.map((id) => this.getPractitioner(id))
11905
+ );
11906
+ const validDrafts = draftProfiles.filter((p) => {
11907
+ if (!p) return false;
11908
+ if (p.status !== "draft" /* DRAFT */) {
11909
+ throw new Error(`Practitioner ${p.id} has already been claimed`);
11910
+ }
11911
+ return true;
11912
+ });
11913
+ if (validDrafts.length === 0) {
11914
+ throw new Error("No valid draft profiles found");
11915
+ }
11916
+ const existingPractitioner = await this.getPractitionerByUserRef(userId);
11917
+ if (existingPractitioner) {
11918
+ let mergedClinics2 = new Set(existingPractitioner.clinics);
11919
+ let mergedWorkingHours2 = [...existingPractitioner.clinicWorkingHours];
11920
+ let mergedClinicsInfo2 = [...existingPractitioner.clinicsInfo];
11921
+ for (const draft of validDrafts) {
11922
+ draft.clinics.forEach((clinicId) => mergedClinics2.add(clinicId));
11923
+ for (const workingHours of draft.clinicWorkingHours) {
11924
+ if (!mergedWorkingHours2.find((wh) => wh.clinicId === workingHours.clinicId)) {
11925
+ mergedWorkingHours2.push(workingHours);
11926
+ }
11927
+ }
11928
+ for (const clinicInfo of draft.clinicsInfo) {
11929
+ if (!mergedClinicsInfo2.find((ci) => ci.id === clinicInfo.id)) {
11930
+ mergedClinicsInfo2.push(clinicInfo);
11931
+ }
11932
+ }
11933
+ }
11934
+ const updatedPractitioner2 = await this.updatePractitioner(existingPractitioner.id, {
11935
+ clinics: Array.from(mergedClinics2),
11936
+ clinicWorkingHours: mergedWorkingHours2,
11937
+ clinicsInfo: mergedClinicsInfo2
11938
+ });
11939
+ for (const draft of validDrafts) {
11940
+ await deleteDoc4(doc20(this.db, PRACTITIONERS_COLLECTION, draft.id));
11941
+ const activeTokens = await this.getPractitionerActiveTokens(draft.id);
11942
+ for (const token of activeTokens) {
11943
+ await this.markTokenAsUsed(token.id, draft.id, userId);
11944
+ }
11945
+ }
11946
+ return updatedPractitioner2;
11947
+ }
11948
+ const mainDraft = validDrafts[0];
11949
+ const otherDrafts = validDrafts.slice(1);
11950
+ let mergedClinics = new Set(mainDraft.clinics);
11951
+ let mergedWorkingHours = [...mainDraft.clinicWorkingHours];
11952
+ let mergedClinicsInfo = [...mainDraft.clinicsInfo];
11953
+ for (const draft of otherDrafts) {
11954
+ draft.clinics.forEach((clinicId) => mergedClinics.add(clinicId));
11955
+ for (const workingHours of draft.clinicWorkingHours) {
11956
+ if (!mergedWorkingHours.find((wh) => wh.clinicId === workingHours.clinicId)) {
11957
+ mergedWorkingHours.push(workingHours);
11958
+ }
11959
+ }
11960
+ for (const clinicInfo of draft.clinicsInfo) {
11961
+ if (!mergedClinicsInfo.find((ci) => ci.id === clinicInfo.id)) {
11962
+ mergedClinicsInfo.push(clinicInfo);
11963
+ }
11964
+ }
11965
+ }
11966
+ const updatedPractitioner = await this.updatePractitioner(mainDraft.id, {
11967
+ userRef: userId,
11968
+ status: "active" /* ACTIVE */,
11969
+ clinics: Array.from(mergedClinics),
11970
+ clinicWorkingHours: mergedWorkingHours,
11971
+ clinicsInfo: mergedClinicsInfo
11972
+ });
11973
+ const mainActiveTokens = await this.getPractitionerActiveTokens(mainDraft.id);
11974
+ for (const token of mainActiveTokens) {
11975
+ await this.markTokenAsUsed(token.id, mainDraft.id, userId);
11976
+ }
11977
+ for (const draft of otherDrafts) {
11978
+ await deleteDoc4(doc20(this.db, PRACTITIONERS_COLLECTION, draft.id));
11979
+ const activeTokens = await this.getPractitionerActiveTokens(draft.id);
11980
+ for (const token of activeTokens) {
11981
+ await this.markTokenAsUsed(token.id, draft.id, userId);
11982
+ }
11983
+ }
11984
+ console.log("[PRACTITIONER] Multiple draft profiles claimed successfully", {
11985
+ practitionerId: updatedPractitioner.id,
11986
+ userId,
11987
+ mergedCount: validDrafts.length
11988
+ });
11989
+ return updatedPractitioner;
11990
+ } catch (error) {
11991
+ console.error(
11992
+ "[PRACTITIONER] Error claiming multiple draft profiles with Google:",
11993
+ error
11994
+ );
11995
+ throw error;
11996
+ }
11997
+ }
11741
11998
  /**
11742
11999
  * Dohvata sve zdravstvene radnike za određenu kliniku sa statusom ACTIVE
11743
12000
  */
@@ -15874,6 +16131,226 @@ var AuthService = class extends BaseService {
15874
16131
  throw handleFirebaseError(error);
15875
16132
  }
15876
16133
  }
16134
+ /**
16135
+ * Signs up or signs in a practitioner with Google authentication.
16136
+ * Checks for existing practitioner account or draft profiles.
16137
+ *
16138
+ * @param idToken - The Google ID token obtained from the mobile app
16139
+ * @returns Object containing user, practitioner (if exists), and draft profiles (if any)
16140
+ */
16141
+ async signUpPractitionerWithGoogle(idToken) {
16142
+ var _a, _b;
16143
+ try {
16144
+ console.log("[AUTH] Starting practitioner Google Sign-In/Sign-Up");
16145
+ let email;
16146
+ try {
16147
+ const payloadBase64 = idToken.split(".")[1];
16148
+ const payloadJson = globalThis.atob ? globalThis.atob(payloadBase64) : Buffer.from(payloadBase64, "base64").toString("utf8");
16149
+ const payload = JSON.parse(payloadJson);
16150
+ email = payload.email;
16151
+ } catch (decodeError) {
16152
+ console.error("[AUTH] Failed to decode email from Google ID token:", decodeError);
16153
+ throw new AuthError(
16154
+ "Unable to read email from Google token. Please try again.",
16155
+ "AUTH/INVALID_GOOGLE_TOKEN",
16156
+ 400
16157
+ );
16158
+ }
16159
+ if (!email) {
16160
+ throw new AuthError(
16161
+ "Unable to read email from Google token. Please try again.",
16162
+ "AUTH/INVALID_GOOGLE_TOKEN",
16163
+ 400
16164
+ );
16165
+ }
16166
+ const normalizedEmail = email.toLowerCase().trim();
16167
+ console.log("[AUTH] Extracted email from Google token:", normalizedEmail);
16168
+ const methods = await fetchSignInMethodsForEmail2(this.auth, normalizedEmail);
16169
+ const hasGoogleMethod = methods.includes(GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD);
16170
+ const hasEmailMethod = methods.includes(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD);
16171
+ const practitionerService = new PractitionerService(this.db, this.auth, this.app);
16172
+ if (hasGoogleMethod) {
16173
+ console.log("[AUTH] User exists with Google provider, signing in");
16174
+ const credential2 = GoogleAuthProvider.credential(idToken);
16175
+ const { user: firebaseUser2 } = await signInWithCredential(this.auth, credential2);
16176
+ let existingUser2 = null;
16177
+ try {
16178
+ existingUser2 = await this.userService.getUserById(firebaseUser2.uid);
16179
+ console.log("[AUTH] User document found:", existingUser2.uid);
16180
+ } catch (userError) {
16181
+ console.log("[AUTH] User document not found in Firestore, checking for draft profiles", {
16182
+ errorCode: userError == null ? void 0 : userError.code,
16183
+ errorMessage: userError == null ? void 0 : userError.message,
16184
+ errorType: (_a = userError == null ? void 0 : userError.constructor) == null ? void 0 : _a.name,
16185
+ isAuthError: userError instanceof AuthError
16186
+ });
16187
+ const practitionerService2 = new PractitionerService(this.db, this.auth, this.app);
16188
+ const draftProfiles3 = await practitionerService2.getDraftProfilesByEmail(normalizedEmail);
16189
+ console.log("[AUTH] Draft profiles check result:", {
16190
+ email: normalizedEmail,
16191
+ draftProfilesCount: draftProfiles3.length,
16192
+ draftProfileIds: draftProfiles3.map((p) => p.id)
16193
+ });
16194
+ if (draftProfiles3.length === 0) {
16195
+ console.log("[AUTH] No draft profiles found, signing out and throwing error");
16196
+ try {
16197
+ await firebaseSignOut(this.auth);
16198
+ } catch (signOutError) {
16199
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16200
+ }
16201
+ throw new AuthError(
16202
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16203
+ "AUTH/NO_DRAFT_PROFILES",
16204
+ 404
16205
+ );
16206
+ }
16207
+ console.log("[AUTH] Draft profiles found, creating user document");
16208
+ existingUser2 = await this.userService.createUser(firebaseUser2, ["practitioner" /* PRACTITIONER */], {
16209
+ skipProfileCreation: true
16210
+ });
16211
+ console.log("[AUTH] Created user document for existing Firebase user with draft profiles:", existingUser2.uid);
16212
+ }
16213
+ if (!existingUser2) {
16214
+ await firebaseSignOut(this.auth);
16215
+ throw new AuthError(
16216
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16217
+ "AUTH/NO_DRAFT_PROFILES",
16218
+ 404
16219
+ );
16220
+ }
16221
+ let practitioner2 = null;
16222
+ if (existingUser2.practitionerProfile) {
16223
+ practitioner2 = await practitionerService.getPractitioner(existingUser2.practitionerProfile);
16224
+ }
16225
+ const draftProfiles2 = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16226
+ return {
16227
+ user: existingUser2,
16228
+ practitioner: practitioner2,
16229
+ draftProfiles: draftProfiles2
16230
+ };
16231
+ }
16232
+ if (hasEmailMethod && !hasGoogleMethod) {
16233
+ console.log("[AUTH] User exists with email/password only");
16234
+ throw new AuthError(
16235
+ "An account with this email already exists. Please sign in with your email and password, then link your Google account in settings.",
16236
+ "AUTH/EMAIL_ALREADY_EXISTS",
16237
+ 409
16238
+ );
16239
+ }
16240
+ console.log("[AUTH] Signing in with Google credential");
16241
+ const credential = GoogleAuthProvider.credential(idToken);
16242
+ let firebaseUser;
16243
+ try {
16244
+ const result = await signInWithCredential(this.auth, credential);
16245
+ firebaseUser = result.user;
16246
+ } catch (error) {
16247
+ if (error.code === "auth/account-exists-with-different-credential") {
16248
+ throw new AuthError(
16249
+ "An account with this email already exists. Please sign in with your email and password, then link your Google account in settings.",
16250
+ "AUTH/EMAIL_ALREADY_EXISTS",
16251
+ 409
16252
+ );
16253
+ }
16254
+ throw error;
16255
+ }
16256
+ let existingUser = null;
16257
+ try {
16258
+ const existingUserDoc = await this.userService.getUserById(firebaseUser.uid);
16259
+ if (existingUserDoc) {
16260
+ existingUser = existingUserDoc;
16261
+ console.log("[AUTH] Found existing User document");
16262
+ }
16263
+ } catch (error) {
16264
+ console.error("[AUTH] Error checking for existing user:", error);
16265
+ }
16266
+ console.log("[AUTH] Checking for draft profiles");
16267
+ let draftProfiles = [];
16268
+ try {
16269
+ draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16270
+ console.log("[AUTH] Draft profiles check complete", { count: draftProfiles.length });
16271
+ } catch (draftCheckError) {
16272
+ console.error("[AUTH] Error checking draft profiles:", draftCheckError);
16273
+ try {
16274
+ await firebaseSignOut(this.auth);
16275
+ } catch (signOutError) {
16276
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16277
+ }
16278
+ throw new AuthError(
16279
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16280
+ "AUTH/NO_DRAFT_PROFILES",
16281
+ 404
16282
+ );
16283
+ }
16284
+ let user;
16285
+ if (existingUser) {
16286
+ user = existingUser;
16287
+ console.log("[AUTH] Using existing user account");
16288
+ } else {
16289
+ if (draftProfiles.length === 0) {
16290
+ console.log("[AUTH] No draft profiles found, signing out and throwing error");
16291
+ try {
16292
+ await firebaseSignOut(this.auth);
16293
+ } catch (signOutError) {
16294
+ console.warn("[AUTH] Error signing out Firebase user (non-critical):", signOutError);
16295
+ }
16296
+ const noDraftError = new AuthError(
16297
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16298
+ "AUTH/NO_DRAFT_PROFILES",
16299
+ 404
16300
+ );
16301
+ console.log("[AUTH] Throwing NO_DRAFT_PROFILES error:", noDraftError.code);
16302
+ throw noDraftError;
16303
+ }
16304
+ user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
16305
+ skipProfileCreation: true
16306
+ });
16307
+ console.log("[AUTH] Created new user account with draft profiles available");
16308
+ }
16309
+ let practitioner = null;
16310
+ if (user.practitionerProfile) {
16311
+ practitioner = await practitionerService.getPractitioner(user.practitionerProfile);
16312
+ }
16313
+ console.log("[AUTH] Google Sign-In complete", {
16314
+ userId: user.uid,
16315
+ hasPractitioner: !!practitioner,
16316
+ draftProfilesCount: draftProfiles.length
16317
+ });
16318
+ return {
16319
+ user,
16320
+ practitioner,
16321
+ draftProfiles
16322
+ };
16323
+ } catch (error) {
16324
+ console.error("[AUTH] Error in signUpPractitionerWithGoogle:", error);
16325
+ console.error("[AUTH] Error type:", (_b = error == null ? void 0 : error.constructor) == null ? void 0 : _b.name);
16326
+ console.error("[AUTH] Error instanceof AuthError:", error instanceof AuthError);
16327
+ console.error("[AUTH] Error code:", error == null ? void 0 : error.code);
16328
+ console.error("[AUTH] Error message:", error == null ? void 0 : error.message);
16329
+ if (error instanceof AuthError) {
16330
+ console.log("[AUTH] Preserving AuthError:", error.code);
16331
+ throw error;
16332
+ }
16333
+ const errorMessage = (error == null ? void 0 : error.message) || (error == null ? void 0 : error.toString()) || "";
16334
+ if (errorMessage.includes("NO_DRAFT_PROFILES") || errorMessage.includes("clinic invitation")) {
16335
+ console.log("[AUTH] Detected clinic invitation error in message, converting to AuthError");
16336
+ throw new AuthError(
16337
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16338
+ "AUTH/NO_DRAFT_PROFILES",
16339
+ 404
16340
+ );
16341
+ }
16342
+ const wrappedError = handleFirebaseError(error);
16343
+ console.log("[AUTH] Wrapped error:", wrappedError.message);
16344
+ if (wrappedError.message.includes("permissions") || wrappedError.message.includes("Account creation failed")) {
16345
+ throw new AuthError(
16346
+ "No clinic invitation found for this email. Please contact your clinic administrator to receive an invitation, or use the token provided by your clinic.",
16347
+ "AUTH/NO_DRAFT_PROFILES",
16348
+ 404
16349
+ );
16350
+ }
16351
+ throw wrappedError;
16352
+ }
16353
+ }
15877
16354
  /**
15878
16355
  * Links a Google account to the currently signed-in user using an ID token.
15879
16356
  * This is used to upgrade an anonymous user or to allow an existing user
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.13.21",
4
+ "version": "1.14.1",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -78,10 +78,21 @@ export const practitionerInvitationTemplate = `
78
78
 
79
79
  <p>This token will expire on <strong>{{expirationDate}}</strong>.</p>
80
80
 
81
- <p>To create your account:</p>
81
+ <p><strong>You have two options to create your account:</strong></p>
82
+
83
+ <p><strong>Option 1: Sign in with Google (Recommended)</strong></p>
84
+ <ol>
85
+ <li>Open the MetaEsthetics Doctor App</li>
86
+ <li>Click "Sign in with Google" on the login screen</li>
87
+ <li>Select your Google account (use the email address: {{practitionerEmail}})</li>
88
+ <li>You'll see an invitation to join {{clinicName}} - simply select it and join!</li>
89
+ </ol>
90
+
91
+ <p><strong>Option 2: Use Email/Password with Token</strong></p>
82
92
  <ol>
83
93
  <li>Visit {{registrationUrl}}</li>
84
- <li>Enter your email and create a password</li>
94
+ <li>Click "Claim Existing Profile with Token"</li>
95
+ <li>Enter your email ({{practitionerEmail}}) and create a password</li>
85
96
  <li>When prompted, enter the token above</li>
86
97
  </ol>
87
98