@insforge/react 0.2.10 → 0.3.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.
@@ -6,6 +6,7 @@ var jsxRuntime = require('react/jsx-runtime');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
8
  var lucideReact = require('lucide-react');
9
+ var zod = require('zod');
9
10
 
10
11
  var InsforgeContext = react.createContext(
11
12
  void 0
@@ -685,6 +686,7 @@ function AuthVerificationCodeInput({
685
686
  email,
686
687
  onChange,
687
688
  disabled = false,
689
+ onComplete,
688
690
  appearance = {}
689
691
  }) {
690
692
  const inputRefs = react.useRef([]);
@@ -698,6 +700,9 @@ function AuthVerificationCodeInput({
698
700
  if (digit && index < length - 1) {
699
701
  inputRefs.current[index + 1]?.focus();
700
702
  }
703
+ if (digit && index === length - 1 && updatedValue.length === length && onComplete) {
704
+ onComplete(updatedValue);
705
+ }
701
706
  };
702
707
  const handleKeyDown = (index, e) => {
703
708
  if (e.key === "Backspace") {
@@ -718,6 +723,9 @@ function AuthVerificationCodeInput({
718
723
  if (/^\d+$/.test(pastedData) && pastedData.length === length) {
719
724
  onChange(pastedData);
720
725
  inputRefs.current[length - 1]?.focus();
726
+ if (onComplete) {
727
+ onComplete(pastedData);
728
+ }
721
729
  }
722
730
  };
723
731
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
@@ -758,6 +766,125 @@ function AuthVerificationCodeInput({
758
766
  )) })
759
767
  ] });
760
768
  }
769
+ function AuthEmailVerificationStep({
770
+ email,
771
+ description,
772
+ method = "code",
773
+ onVerifyCode
774
+ }) {
775
+ const { baseUrl } = useInsforge();
776
+ const [insforge] = react.useState(() => sdk.createClient({ baseUrl }));
777
+ const [resendDisabled, setResendDisabled] = react.useState(true);
778
+ const [resendCountdown, setResendCountdown] = react.useState(60);
779
+ const [isSending, setIsSending] = react.useState(false);
780
+ const [verificationCode, setVerificationCode] = react.useState("");
781
+ const [isVerifying, setIsVerifying] = react.useState(false);
782
+ const [verificationError, setVerificationError] = react.useState("");
783
+ const defaultDescription = method === "code" ? "We've sent a 6-digit verification code to {email}. Please enter it below to verify your account. The code will expire in 10 minutes." : "We've sent a verification link to {email}. Please check your email and click the link to verify your account. The link will expire in 10 minutes.";
784
+ react.useEffect(() => {
785
+ const sendInitialEmail = async () => {
786
+ try {
787
+ if (method === "code") {
788
+ await insforge.auth.sendVerificationCode({ email });
789
+ } else {
790
+ await insforge.auth.sendVerificationLink({ email });
791
+ }
792
+ } catch {
793
+ }
794
+ };
795
+ void sendInitialEmail();
796
+ }, [email, method, insforge.auth]);
797
+ react.useEffect(() => {
798
+ if (resendCountdown > 0) {
799
+ const timer = setInterval(() => {
800
+ setResendCountdown((prev) => {
801
+ if (prev <= 1) {
802
+ setResendDisabled(false);
803
+ return 0;
804
+ }
805
+ return prev - 1;
806
+ });
807
+ }, 1e3);
808
+ return () => clearInterval(timer);
809
+ }
810
+ }, [resendCountdown]);
811
+ const handleResend = async () => {
812
+ setResendDisabled(true);
813
+ setResendCountdown(60);
814
+ setIsSending(true);
815
+ setVerificationError("");
816
+ try {
817
+ if (method === "code") {
818
+ await insforge.auth.sendVerificationCode({ email });
819
+ } else {
820
+ await insforge.auth.sendVerificationLink({ email });
821
+ }
822
+ } catch {
823
+ setResendDisabled(false);
824
+ setResendCountdown(0);
825
+ } finally {
826
+ setIsSending(false);
827
+ }
828
+ };
829
+ const handleVerifyCode = async (code) => {
830
+ if (!onVerifyCode) {
831
+ return;
832
+ }
833
+ setIsVerifying(true);
834
+ setVerificationError("");
835
+ try {
836
+ await onVerifyCode(code);
837
+ } catch (error) {
838
+ setVerificationError(
839
+ error instanceof Error ? error.message : "Invalid verification code. Please try again."
840
+ );
841
+ setVerificationCode("");
842
+ } finally {
843
+ setIsVerifying(false);
844
+ }
845
+ };
846
+ const displayDescription = description || defaultDescription;
847
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6 items-stretch", children: [
848
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed", children: displayDescription.split("{email}").map((part, index, array) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
849
+ part,
850
+ index < array.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-black dark:text-white", children: email })
851
+ ] }, index)) }),
852
+ verificationError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-3 py-2 pr-2 bg-red-50 border-2 border-red-600 rounded", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
853
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-6 h-6 text-red-500 shrink-0", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
854
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 flex-1", children: verificationError })
855
+ ] }) }),
856
+ method === "code" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-neutral-100 dark:bg-neutral-800 rounded-lg px-4 pt-4 pb-6 flex flex-col gap-4", children: [
857
+ /* @__PURE__ */ jsxRuntime.jsx(
858
+ AuthVerificationCodeInput,
859
+ {
860
+ value: verificationCode,
861
+ onChange: setVerificationCode,
862
+ email,
863
+ disabled: isVerifying,
864
+ onComplete: (code) => {
865
+ void handleVerifyCode(code);
866
+ }
867
+ }
868
+ ),
869
+ isVerifying && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: "Verifying..." })
870
+ ] }),
871
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full text-sm text-center text-neutral-600 dark:text-neutral-400", children: [
872
+ "Didn't receive the email?",
873
+ " ",
874
+ /* @__PURE__ */ jsxRuntime.jsx(
875
+ "button",
876
+ {
877
+ onClick: () => {
878
+ void handleResend();
879
+ },
880
+ disabled: resendDisabled || isSending,
881
+ className: "text-black dark:text-white font-medium transition-colors disabled:cursor-not-allowed cursor-pointer hover:underline disabled:no-underline disabled:opacity-50",
882
+ children: isSending ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
883
+ }
884
+ )
885
+ ] })
886
+ ] });
887
+ }
761
888
  function SignInForm({
762
889
  email,
763
890
  password,
@@ -784,7 +911,11 @@ function SignInForm({
784
911
  signUpText = "Don't have an account?",
785
912
  signUpLinkText = "Sign Up Now",
786
913
  signUpUrl = "/sign-up",
787
- dividerText = "or"
914
+ dividerText = "or",
915
+ // Email verification step props
916
+ showVerificationStep = false,
917
+ onVerifyCode,
918
+ verificationDescription
788
919
  }) {
789
920
  return /* @__PURE__ */ jsxRuntime.jsxs(
790
921
  AuthContainer,
@@ -797,8 +928,8 @@ function SignInForm({
797
928
  /* @__PURE__ */ jsxRuntime.jsx(
798
929
  AuthHeader,
799
930
  {
800
- title,
801
- subtitle,
931
+ title: showVerificationStep ? "Verify Your Email" : title,
932
+ subtitle: showVerificationStep ? "" : subtitle,
802
933
  appearance: {
803
934
  containerClassName: appearance.header?.container,
804
935
  titleClassName: appearance.header?.title,
@@ -813,7 +944,14 @@ function SignInForm({
813
944
  className: appearance.errorBanner
814
945
  }
815
946
  ),
816
- /* @__PURE__ */ jsxRuntime.jsxs(
947
+ showVerificationStep ? /* @__PURE__ */ jsxRuntime.jsx(
948
+ AuthEmailVerificationStep,
949
+ {
950
+ email,
951
+ description: verificationDescription,
952
+ onVerifyCode
953
+ }
954
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
817
955
  "form",
818
956
  {
819
957
  onSubmit,
@@ -872,39 +1010,41 @@ function SignInForm({
872
1010
  ]
873
1011
  }
874
1012
  ),
875
- /* @__PURE__ */ jsxRuntime.jsx(
876
- AuthLink,
877
- {
878
- text: signUpText,
879
- linkText: signUpLinkText,
880
- href: signUpUrl,
881
- appearance: {
882
- containerClassName: appearance.link?.container,
883
- linkClassName: appearance.link?.link
884
- }
885
- }
886
- ),
887
- availableProviders.length > 0 && onOAuthClick && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
888
- /* @__PURE__ */ jsxRuntime.jsx(
889
- AuthDivider,
890
- {
891
- text: dividerText,
892
- className: appearance.divider
893
- }
894
- ),
1013
+ !showVerificationStep && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
895
1014
  /* @__PURE__ */ jsxRuntime.jsx(
896
- AuthOAuthProviders,
1015
+ AuthLink,
897
1016
  {
898
- providers: availableProviders,
899
- onClick: onOAuthClick,
900
- disabled: loading || oauthLoading !== null,
901
- loading: oauthLoading,
1017
+ text: signUpText,
1018
+ linkText: signUpLinkText,
1019
+ href: signUpUrl,
902
1020
  appearance: {
903
- containerClassName: appearance.oauth?.container,
904
- buttonClassName: appearance.oauth?.button
1021
+ containerClassName: appearance.link?.container,
1022
+ linkClassName: appearance.link?.link
905
1023
  }
906
1024
  }
907
- )
1025
+ ),
1026
+ availableProviders.length > 0 && onOAuthClick && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1027
+ /* @__PURE__ */ jsxRuntime.jsx(
1028
+ AuthDivider,
1029
+ {
1030
+ text: dividerText,
1031
+ className: appearance.divider
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsxRuntime.jsx(
1035
+ AuthOAuthProviders,
1036
+ {
1037
+ providers: availableProviders,
1038
+ onClick: onOAuthClick,
1039
+ disabled: loading || oauthLoading !== null,
1040
+ loading: oauthLoading,
1041
+ appearance: {
1042
+ containerClassName: appearance.oauth?.container,
1043
+ buttonClassName: appearance.oauth?.button
1044
+ }
1045
+ }
1046
+ )
1047
+ ] })
908
1048
  ] })
909
1049
  ]
910
1050
  }
@@ -922,6 +1062,7 @@ function SignIn({
922
1062
  const [password, setPassword] = react.useState("");
923
1063
  const [error, setError] = react.useState("");
924
1064
  const [loading, setLoading] = react.useState(false);
1065
+ const [step, setStep] = react.useState("form");
925
1066
  const [oauthLoading, setOauthLoading] = react.useState(
926
1067
  null
927
1068
  );
@@ -933,6 +1074,11 @@ function SignIn({
933
1074
  try {
934
1075
  const result = await signIn(email, password);
935
1076
  if ("error" in result) {
1077
+ if (result.statusCode === 403) {
1078
+ setStep("awaiting-verification");
1079
+ setLoading(false);
1080
+ return;
1081
+ }
936
1082
  throw new Error(result.error);
937
1083
  }
938
1084
  const { user, accessToken } = result;
@@ -947,6 +1093,21 @@ function SignIn({
947
1093
  setLoading(false);
948
1094
  }
949
1095
  }
1096
+ async function handleVerifyCode(code) {
1097
+ try {
1098
+ const result = await insforge.auth.verifyEmail({ email, otp: code });
1099
+ if (result.error) {
1100
+ throw new Error(result.error.message || "Verification failed");
1101
+ }
1102
+ if (result.data?.accessToken) {
1103
+ if (onSuccess && result.data.user) {
1104
+ onSuccess(result.data.user, result.data.accessToken);
1105
+ }
1106
+ }
1107
+ } catch (err) {
1108
+ throw new Error(err.message || "Invalid verification code");
1109
+ }
1110
+ }
950
1111
  async function handleOAuth(provider) {
951
1112
  try {
952
1113
  setOauthLoading(provider);
@@ -979,6 +1140,8 @@ function SignIn({
979
1140
  availableProviders: emailConfig?.oAuthProviders || [],
980
1141
  onOAuthClick: handleOAuth,
981
1142
  emailAuthConfig: emailConfig,
1143
+ showVerificationStep: step === "awaiting-verification",
1144
+ onVerifyCode: handleVerifyCode,
982
1145
  ...uiProps
983
1146
  }
984
1147
  );
@@ -1007,7 +1170,11 @@ function SignUpForm({
1007
1170
  signInText = "Already have an account?",
1008
1171
  signInLinkText = "Login Now",
1009
1172
  signInUrl = "/sign-in",
1010
- dividerText = "or"
1173
+ dividerText = "or",
1174
+ // Email verification step props
1175
+ showVerificationStep = false,
1176
+ onVerifyCode,
1177
+ verificationDescription
1011
1178
  }) {
1012
1179
  return /* @__PURE__ */ jsxRuntime.jsxs(
1013
1180
  AuthContainer,
@@ -1020,8 +1187,8 @@ function SignUpForm({
1020
1187
  /* @__PURE__ */ jsxRuntime.jsx(
1021
1188
  AuthHeader,
1022
1189
  {
1023
- title,
1024
- subtitle,
1190
+ title: showVerificationStep ? "Verify Your Email" : title,
1191
+ subtitle: showVerificationStep ? "" : subtitle,
1025
1192
  appearance: {
1026
1193
  containerClassName: appearance.header?.container,
1027
1194
  titleClassName: appearance.header?.title,
@@ -1036,7 +1203,14 @@ function SignUpForm({
1036
1203
  className: appearance.errorBanner
1037
1204
  }
1038
1205
  ),
1039
- /* @__PURE__ */ jsxRuntime.jsxs(
1206
+ showVerificationStep ? /* @__PURE__ */ jsxRuntime.jsx(
1207
+ AuthEmailVerificationStep,
1208
+ {
1209
+ email,
1210
+ description: verificationDescription,
1211
+ onVerifyCode
1212
+ }
1213
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1040
1214
  "form",
1041
1215
  {
1042
1216
  onSubmit,
@@ -1093,44 +1267,74 @@ function SignUpForm({
1093
1267
  ]
1094
1268
  }
1095
1269
  ),
1096
- /* @__PURE__ */ jsxRuntime.jsx(
1097
- AuthLink,
1098
- {
1099
- text: signInText,
1100
- linkText: signInLinkText,
1101
- href: signInUrl,
1102
- appearance: {
1103
- containerClassName: appearance.link?.container,
1104
- linkClassName: appearance.link?.link
1105
- }
1106
- }
1107
- ),
1108
- availableProviders.length > 0 && onOAuthClick && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1109
- /* @__PURE__ */ jsxRuntime.jsx(
1110
- AuthDivider,
1111
- {
1112
- text: dividerText,
1113
- className: appearance.divider
1114
- }
1115
- ),
1270
+ !showVerificationStep && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1116
1271
  /* @__PURE__ */ jsxRuntime.jsx(
1117
- AuthOAuthProviders,
1272
+ AuthLink,
1118
1273
  {
1119
- providers: availableProviders,
1120
- onClick: onOAuthClick,
1121
- disabled: loading || oauthLoading !== null,
1122
- loading: oauthLoading,
1274
+ text: signInText,
1275
+ linkText: signInLinkText,
1276
+ href: signInUrl,
1123
1277
  appearance: {
1124
- containerClassName: appearance.oauth?.container,
1125
- buttonClassName: appearance.oauth?.button
1278
+ containerClassName: appearance.link?.container,
1279
+ linkClassName: appearance.link?.link
1126
1280
  }
1127
1281
  }
1128
- )
1282
+ ),
1283
+ availableProviders.length > 0 && onOAuthClick && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1284
+ /* @__PURE__ */ jsxRuntime.jsx(
1285
+ AuthDivider,
1286
+ {
1287
+ text: dividerText,
1288
+ className: appearance.divider
1289
+ }
1290
+ ),
1291
+ /* @__PURE__ */ jsxRuntime.jsx(
1292
+ AuthOAuthProviders,
1293
+ {
1294
+ providers: availableProviders,
1295
+ onClick: onOAuthClick,
1296
+ disabled: loading || oauthLoading !== null,
1297
+ loading: oauthLoading,
1298
+ appearance: {
1299
+ containerClassName: appearance.oauth?.container,
1300
+ buttonClassName: appearance.oauth?.button
1301
+ }
1302
+ }
1303
+ )
1304
+ ] })
1129
1305
  ] })
1130
1306
  ]
1131
1307
  }
1132
1308
  );
1133
1309
  }
1310
+ var emailSchema = zod.z.string().min(1, "Email is required").email("Invalid email address");
1311
+ function createPasswordSchema(options) {
1312
+ const {
1313
+ minLength = 6,
1314
+ requireUppercase = false,
1315
+ requireLowercase = false,
1316
+ requireNumber = false,
1317
+ requireSpecialChar = false
1318
+ } = options || {};
1319
+ let schema = zod.z.string().min(minLength, `Password must be at least ${minLength} characters`);
1320
+ if (requireUppercase) {
1321
+ schema = schema.regex(/[A-Z]/, "Password must contain at least one uppercase letter");
1322
+ }
1323
+ if (requireLowercase) {
1324
+ schema = schema.regex(/[a-z]/, "Password must contain at least one lowercase letter");
1325
+ }
1326
+ if (requireNumber) {
1327
+ schema = schema.regex(/\d/, "Password must contain at least one number");
1328
+ }
1329
+ if (requireSpecialChar) {
1330
+ schema = schema.regex(
1331
+ /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/,
1332
+ "Password must contain at least one special character"
1333
+ );
1334
+ }
1335
+ return schema;
1336
+ }
1337
+ createPasswordSchema();
1134
1338
  function SignUp({
1135
1339
  afterSignUpUrl,
1136
1340
  onSuccess,
@@ -1143,6 +1347,7 @@ function SignUp({
1143
1347
  const [password, setPassword] = react.useState("");
1144
1348
  const [error, setError] = react.useState("");
1145
1349
  const [loading, setLoading] = react.useState(false);
1350
+ const [step, setStep] = react.useState("form");
1146
1351
  const [oauthLoading, setOauthLoading] = react.useState(
1147
1352
  null
1148
1353
  );
@@ -1151,19 +1356,46 @@ function SignUp({
1151
1356
  e.preventDefault();
1152
1357
  setLoading(true);
1153
1358
  setError("");
1154
- if (emailConfig && !validatePasswordStrength(password, emailConfig)) {
1155
- setError("Password does not meet all requirements");
1359
+ if (!emailConfig) {
1360
+ setError("Configuration not loaded. Please refresh the page.");
1361
+ setLoading(false);
1362
+ return;
1363
+ }
1364
+ const emailValidation = emailSchema.safeParse(email);
1365
+ if (!emailValidation.success) {
1366
+ const firstError = emailValidation.error.issues[0];
1367
+ setError(firstError.message);
1368
+ setLoading(false);
1369
+ return;
1370
+ }
1371
+ const passwordZodSchema = createPasswordSchema({
1372
+ minLength: emailConfig.passwordMinLength,
1373
+ requireUppercase: emailConfig.requireUppercase,
1374
+ requireLowercase: emailConfig.requireLowercase,
1375
+ requireNumber: emailConfig.requireNumber,
1376
+ requireSpecialChar: emailConfig.requireSpecialChar
1377
+ });
1378
+ const passwordValidation = passwordZodSchema.safeParse(password);
1379
+ if (!passwordValidation.success) {
1380
+ const firstError = passwordValidation.error.issues[0];
1381
+ setError(firstError.message);
1156
1382
  setLoading(false);
1157
1383
  return;
1158
1384
  }
1159
1385
  try {
1160
- const result = await signUp(email, password);
1386
+ const result = await signUp(emailValidation.data, password);
1161
1387
  if ("error" in result) {
1162
1388
  throw new Error(result.error);
1163
1389
  }
1164
- const { user, accessToken } = result;
1165
- if (onSuccess) {
1166
- if (user) onSuccess(user, accessToken || "");
1390
+ if (result.requiresEmailVerification && !result.accessToken) {
1391
+ setStep("awaiting-verification");
1392
+ setLoading(false);
1393
+ return;
1394
+ }
1395
+ if (result.accessToken && result.user) {
1396
+ if (onSuccess) {
1397
+ onSuccess(result.user, result.accessToken);
1398
+ }
1167
1399
  }
1168
1400
  } catch (err) {
1169
1401
  const errorMessage = err.message || "Sign up failed";
@@ -1173,6 +1405,21 @@ function SignUp({
1173
1405
  setLoading(false);
1174
1406
  }
1175
1407
  }
1408
+ async function handleVerifyCode(code) {
1409
+ try {
1410
+ const result = await insforge.auth.verifyEmail({ email, otp: code });
1411
+ if (result.error) {
1412
+ throw new Error(result.error.message || "Verification failed");
1413
+ }
1414
+ if (result.data?.accessToken) {
1415
+ if (onSuccess && result.data.user) {
1416
+ onSuccess(result.data.user, result.data.accessToken);
1417
+ }
1418
+ }
1419
+ } catch (err) {
1420
+ throw new Error(err.message || "Invalid verification code");
1421
+ }
1422
+ }
1176
1423
  async function handleOAuth(provider) {
1177
1424
  try {
1178
1425
  setOauthLoading(provider);
@@ -1205,81 +1452,691 @@ function SignUp({
1205
1452
  availableProviders: emailConfig?.oAuthProviders || [],
1206
1453
  onOAuthClick: handleOAuth,
1207
1454
  emailAuthConfig: emailConfig,
1455
+ showVerificationStep: step === "awaiting-verification",
1456
+ onVerifyCode: handleVerifyCode,
1208
1457
  ...uiProps
1209
1458
  }
1210
1459
  );
1211
1460
  }
1212
- function UserButton({
1213
- afterSignOutUrl = "/",
1214
- mode = "detailed",
1215
- appearance = {}
1461
+ function ForgotPasswordForm({
1462
+ email,
1463
+ onEmailChange,
1464
+ onSubmit,
1465
+ error,
1466
+ loading = false,
1467
+ success = false,
1468
+ appearance = {},
1469
+ title = "Forgot Password?",
1470
+ subtitle = "Enter your email address and we'll send you a code to reset your password.",
1471
+ emailLabel = "Email",
1472
+ emailPlaceholder = "example@email.com",
1473
+ submitButtonText = "Send Reset Code",
1474
+ loadingButtonText = "Sending...",
1475
+ backToSignInText = "Remember your password?",
1476
+ backToSignInUrl = "/sign-in",
1477
+ successTitle = "Check Your Email",
1478
+ successMessage
1216
1479
  }) {
1217
- const { user, signOut } = useInsforge();
1218
- const [isOpen, setIsOpen] = react.useState(false);
1219
- const [imageError, setImageError] = react.useState(false);
1220
- const dropdownRef = react.useRef(null);
1221
- react.useEffect(() => {
1222
- setImageError(false);
1223
- const avatarUrl = user?.avatarUrl;
1224
- if (!avatarUrl) return;
1225
- const checkImageUrl = async () => {
1226
- try {
1227
- const response = await fetch(avatarUrl, {
1228
- method: "HEAD",
1229
- cache: "no-cache"
1230
- });
1231
- if (!response.ok) {
1232
- setImageError(true);
1233
- }
1234
- } catch (error) {
1235
- setImageError(true);
1236
- }
1237
- };
1238
- checkImageUrl();
1239
- }, [user?.avatarUrl]);
1240
- react.useEffect(() => {
1241
- function handleClickOutside(event) {
1242
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
1243
- setIsOpen(false);
1480
+ if (success) {
1481
+ return /* @__PURE__ */ jsxRuntime.jsx(
1482
+ AuthContainer,
1483
+ {
1484
+ appearance: {
1485
+ containerClassName: appearance.container,
1486
+ cardClassName: appearance.card
1487
+ },
1488
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
1489
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-8 h-8 text-green-600 dark:text-green-400", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7" }) }) }),
1490
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white text-center", children: successTitle }),
1491
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: successMessage || `We've sent a password reset link to ${email}. Please check your email and follow the instructions.` }),
1492
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: backToSignInUrl, className: "mt-4 text-black dark:text-white font-medium", children: "Back to Sign In" })
1493
+ ] })
1244
1494
  }
1245
- }
1246
- if (isOpen) {
1247
- document.addEventListener("mousedown", handleClickOutside);
1248
- }
1249
- return () => {
1250
- document.removeEventListener("mousedown", handleClickOutside);
1251
- };
1252
- }, [isOpen]);
1253
- async function handleSignOut() {
1254
- await signOut();
1255
- setIsOpen(false);
1256
- window.location.href = afterSignOutUrl;
1495
+ );
1257
1496
  }
1258
- if (!user) return null;
1259
- const initials = user.name ? user.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
1260
1497
  return /* @__PURE__ */ jsxRuntime.jsxs(
1261
- "div",
1498
+ AuthContainer,
1262
1499
  {
1263
- className: cn("relative inline-block", appearance.containerClassName),
1264
- ref: dropdownRef,
1500
+ appearance: {
1501
+ containerClassName: appearance.container,
1502
+ cardClassName: appearance.card
1503
+ },
1265
1504
  children: [
1505
+ /* @__PURE__ */ jsxRuntime.jsx(
1506
+ AuthHeader,
1507
+ {
1508
+ title,
1509
+ subtitle,
1510
+ appearance: {
1511
+ containerClassName: appearance.header?.container,
1512
+ titleClassName: appearance.header?.title,
1513
+ subtitleClassName: appearance.header?.subtitle
1514
+ }
1515
+ }
1516
+ ),
1517
+ /* @__PURE__ */ jsxRuntime.jsx(
1518
+ AuthErrorBanner,
1519
+ {
1520
+ error: error || "",
1521
+ className: appearance.errorBanner
1522
+ }
1523
+ ),
1266
1524
  /* @__PURE__ */ jsxRuntime.jsxs(
1267
- "button",
1525
+ "form",
1268
1526
  {
1269
- className: cn(
1270
- "p-1 bg-transparent border-0 rounded-full cursor-pointer transition-all duration-200",
1271
- "flex items-center justify-center gap-2",
1272
- "hover:bg-black/5",
1273
- mode === "detailed" && "rounded-lg p-2",
1274
- appearance.buttonClassName
1275
- ),
1276
- onClick: () => setIsOpen(!isOpen),
1277
- "aria-expanded": isOpen,
1278
- "aria-haspopup": "true",
1527
+ onSubmit,
1528
+ noValidate: true,
1529
+ className: appearance.form?.container || "flex flex-col items-stretch justify-center gap-6",
1279
1530
  children: [
1280
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-10 h-10 bg-blue-500 rounded-full", children: user.avatarUrl && !imageError ? /* @__PURE__ */ jsxRuntime.jsx(
1281
- "img",
1282
- {
1531
+ /* @__PURE__ */ jsxRuntime.jsx(
1532
+ AuthFormField,
1533
+ {
1534
+ id: "email",
1535
+ type: "email",
1536
+ label: emailLabel,
1537
+ placeholder: emailPlaceholder,
1538
+ value: email,
1539
+ onChange: (e) => onEmailChange(e.target.value),
1540
+ required: true,
1541
+ autoComplete: "email",
1542
+ appearance: {
1543
+ containerClassName: appearance.form?.emailField?.container,
1544
+ labelClassName: appearance.form?.emailField?.label,
1545
+ inputClassName: appearance.form?.emailField?.input
1546
+ }
1547
+ }
1548
+ ),
1549
+ /* @__PURE__ */ jsxRuntime.jsx(
1550
+ AuthSubmitButton,
1551
+ {
1552
+ isLoading: loading,
1553
+ disabled: loading,
1554
+ className: appearance.button,
1555
+ children: loading ? loadingButtonText : submitButtonText
1556
+ }
1557
+ )
1558
+ ]
1559
+ }
1560
+ ),
1561
+ /* @__PURE__ */ jsxRuntime.jsx(
1562
+ AuthLink,
1563
+ {
1564
+ text: backToSignInText,
1565
+ linkText: "Back to Sign In",
1566
+ href: backToSignInUrl,
1567
+ appearance: {
1568
+ containerClassName: appearance.link?.container,
1569
+ linkClassName: appearance.link?.link
1570
+ }
1571
+ }
1572
+ )
1573
+ ]
1574
+ }
1575
+ );
1576
+ }
1577
+ function ResetPasswordForm({
1578
+ newPassword,
1579
+ confirmPassword,
1580
+ onNewPasswordChange,
1581
+ onConfirmPasswordChange,
1582
+ onSubmit,
1583
+ error,
1584
+ loading = false,
1585
+ emailAuthConfig,
1586
+ appearance = {},
1587
+ title = "Reset Password",
1588
+ subtitle = "Enter your new password below.",
1589
+ newPasswordLabel = "New Password",
1590
+ newPasswordPlaceholder = "\u2022\u2022\u2022\u2022\u2022\u2022",
1591
+ confirmPasswordLabel = "Confirm Password",
1592
+ confirmPasswordPlaceholder = "\u2022\u2022\u2022\u2022\u2022\u2022",
1593
+ submitButtonText = "Reset Password",
1594
+ loadingButtonText = "Resetting...",
1595
+ backToSignInText = "",
1596
+ backToSignInUrl = "/sign-in"
1597
+ }) {
1598
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1599
+ AuthContainer,
1600
+ {
1601
+ appearance: {
1602
+ containerClassName: appearance.container,
1603
+ cardClassName: appearance.card
1604
+ },
1605
+ children: [
1606
+ /* @__PURE__ */ jsxRuntime.jsx(
1607
+ AuthHeader,
1608
+ {
1609
+ title,
1610
+ subtitle,
1611
+ appearance: {
1612
+ containerClassName: appearance.header?.container,
1613
+ titleClassName: appearance.header?.title,
1614
+ subtitleClassName: appearance.header?.subtitle
1615
+ }
1616
+ }
1617
+ ),
1618
+ /* @__PURE__ */ jsxRuntime.jsx(
1619
+ AuthErrorBanner,
1620
+ {
1621
+ error: error || "",
1622
+ className: appearance.errorBanner
1623
+ }
1624
+ ),
1625
+ /* @__PURE__ */ jsxRuntime.jsxs(
1626
+ "form",
1627
+ {
1628
+ onSubmit,
1629
+ noValidate: true,
1630
+ className: appearance.form?.container || "flex flex-col items-stretch justify-center gap-6",
1631
+ children: [
1632
+ /* @__PURE__ */ jsxRuntime.jsx(
1633
+ AuthPasswordField,
1634
+ {
1635
+ id: "newPassword",
1636
+ label: newPasswordLabel,
1637
+ placeholder: newPasswordPlaceholder,
1638
+ value: newPassword,
1639
+ onChange: (e) => onNewPasswordChange(e.target.value),
1640
+ required: true,
1641
+ autoComplete: "new-password",
1642
+ showStrengthIndicator: true,
1643
+ emailAuthConfig,
1644
+ appearance: {
1645
+ containerClassName: appearance.form?.newPasswordField?.container,
1646
+ labelClassName: appearance.form?.newPasswordField?.label,
1647
+ inputClassName: appearance.form?.newPasswordField?.input
1648
+ }
1649
+ }
1650
+ ),
1651
+ /* @__PURE__ */ jsxRuntime.jsx(
1652
+ AuthPasswordField,
1653
+ {
1654
+ id: "confirmPassword",
1655
+ label: confirmPasswordLabel,
1656
+ placeholder: confirmPasswordPlaceholder,
1657
+ value: confirmPassword,
1658
+ onChange: (e) => onConfirmPasswordChange(e.target.value),
1659
+ required: true,
1660
+ autoComplete: "new-password",
1661
+ emailAuthConfig,
1662
+ appearance: {
1663
+ containerClassName: appearance.form?.confirmPasswordField?.container,
1664
+ labelClassName: appearance.form?.confirmPasswordField?.label,
1665
+ inputClassName: appearance.form?.confirmPasswordField?.input
1666
+ }
1667
+ }
1668
+ ),
1669
+ /* @__PURE__ */ jsxRuntime.jsx(
1670
+ AuthSubmitButton,
1671
+ {
1672
+ isLoading: loading,
1673
+ disabled: loading,
1674
+ className: appearance.button,
1675
+ children: loading ? loadingButtonText : submitButtonText
1676
+ }
1677
+ )
1678
+ ]
1679
+ }
1680
+ ),
1681
+ /* @__PURE__ */ jsxRuntime.jsx(
1682
+ AuthLink,
1683
+ {
1684
+ text: backToSignInText,
1685
+ linkText: "Back to Sign In",
1686
+ href: backToSignInUrl,
1687
+ appearance: {
1688
+ containerClassName: appearance.link?.container,
1689
+ linkClassName: appearance.link?.link
1690
+ }
1691
+ }
1692
+ )
1693
+ ]
1694
+ }
1695
+ );
1696
+ }
1697
+ function ResetPassword({
1698
+ token,
1699
+ backToSignInUrl = "/sign-in",
1700
+ onSuccess,
1701
+ onError,
1702
+ ...uiProps
1703
+ }) {
1704
+ const { resetPassword } = useInsforge();
1705
+ const { emailConfig } = usePublicAuthConfig();
1706
+ const [newPassword, setNewPassword] = react.useState("");
1707
+ const [confirmPassword, setConfirmPassword] = react.useState("");
1708
+ const [error, setError] = react.useState("");
1709
+ const [loading, setLoading] = react.useState(false);
1710
+ async function handleSubmit(e) {
1711
+ e.preventDefault();
1712
+ setLoading(true);
1713
+ setError("");
1714
+ if (!emailConfig) {
1715
+ setError("Configuration not loaded. Please refresh the page.");
1716
+ setLoading(false);
1717
+ return;
1718
+ }
1719
+ if (newPassword !== confirmPassword) {
1720
+ setError("Passwords do not match");
1721
+ setLoading(false);
1722
+ return;
1723
+ }
1724
+ if (!token) {
1725
+ setError("Reset token is missing");
1726
+ setLoading(false);
1727
+ return;
1728
+ }
1729
+ const passwordZodSchema = createPasswordSchema({
1730
+ minLength: emailConfig.passwordMinLength,
1731
+ requireUppercase: emailConfig.requireUppercase,
1732
+ requireLowercase: emailConfig.requireLowercase,
1733
+ requireNumber: emailConfig.requireNumber,
1734
+ requireSpecialChar: emailConfig.requireSpecialChar
1735
+ });
1736
+ const passwordValidation = passwordZodSchema.safeParse(newPassword);
1737
+ if (!passwordValidation.success) {
1738
+ const firstError = passwordValidation.error.issues[0];
1739
+ setError(firstError.message);
1740
+ setLoading(false);
1741
+ return;
1742
+ }
1743
+ try {
1744
+ const result = await resetPassword(token, newPassword);
1745
+ if (result?.message) {
1746
+ if (onSuccess) {
1747
+ onSuccess(result.redirectTo);
1748
+ }
1749
+ } else {
1750
+ const errorMessage = "Failed to reset password";
1751
+ setError(errorMessage);
1752
+ if (onError) {
1753
+ onError(new Error(errorMessage));
1754
+ }
1755
+ }
1756
+ } catch (err) {
1757
+ const errorMessage = err.message || "Failed to reset password";
1758
+ setError(errorMessage);
1759
+ if (onError) {
1760
+ onError(new Error(errorMessage));
1761
+ }
1762
+ } finally {
1763
+ setLoading(false);
1764
+ }
1765
+ }
1766
+ if (!emailConfig) {
1767
+ return null;
1768
+ }
1769
+ return /* @__PURE__ */ jsxRuntime.jsx(
1770
+ ResetPasswordForm,
1771
+ {
1772
+ newPassword,
1773
+ confirmPassword,
1774
+ onNewPasswordChange: setNewPassword,
1775
+ onConfirmPasswordChange: setConfirmPassword,
1776
+ onSubmit: handleSubmit,
1777
+ error,
1778
+ loading,
1779
+ emailAuthConfig: emailConfig,
1780
+ backToSignInUrl,
1781
+ ...uiProps
1782
+ }
1783
+ );
1784
+ }
1785
+ function ForgotPassword({
1786
+ backToSignInUrl = "/sign-in",
1787
+ onSuccess,
1788
+ onError,
1789
+ ...uiProps
1790
+ }) {
1791
+ const { sendPasswordResetCode, baseUrl } = useInsforge();
1792
+ const { emailConfig } = usePublicAuthConfig();
1793
+ const [insforge] = react.useState(() => sdk.createClient({ baseUrl }));
1794
+ const [step, setStep] = react.useState("email");
1795
+ const [email, setEmail] = react.useState("");
1796
+ const [verificationCode, setVerificationCode] = react.useState("");
1797
+ const [resetToken, setResetToken] = react.useState("");
1798
+ const [error, setError] = react.useState("");
1799
+ const [loading, setLoading] = react.useState(false);
1800
+ const [success, setSuccess] = react.useState(false);
1801
+ const [resendDisabled, setResendDisabled] = react.useState(true);
1802
+ const [resendCountdown, setResendCountdown] = react.useState(60);
1803
+ const [isSendingCode, setIsSendingCode] = react.useState(false);
1804
+ const [isVerifyingCode, setIsVerifyingCode] = react.useState(false);
1805
+ react.useEffect(() => {
1806
+ if (resendCountdown > 0 && step === "code") {
1807
+ const timer = setInterval(() => {
1808
+ setResendCountdown((prev) => {
1809
+ if (prev <= 1) {
1810
+ setResendDisabled(false);
1811
+ return 0;
1812
+ }
1813
+ return prev - 1;
1814
+ });
1815
+ }, 1e3);
1816
+ return () => clearInterval(timer);
1817
+ }
1818
+ }, [resendCountdown, step]);
1819
+ async function handleEmailSubmit(e) {
1820
+ e.preventDefault();
1821
+ setLoading(true);
1822
+ setError("");
1823
+ const emailValidation = emailSchema.safeParse(email);
1824
+ if (!emailValidation.success) {
1825
+ const firstError = emailValidation.error.issues[0];
1826
+ setError(firstError.message);
1827
+ setLoading(false);
1828
+ return;
1829
+ }
1830
+ try {
1831
+ const result = await sendPasswordResetCode(emailValidation.data);
1832
+ if (result?.success) {
1833
+ if (emailConfig?.resetPasswordMethod === "link") {
1834
+ setSuccess(true);
1835
+ if (onSuccess) {
1836
+ onSuccess();
1837
+ }
1838
+ } else {
1839
+ setStep("code");
1840
+ setResendDisabled(true);
1841
+ setResendCountdown(60);
1842
+ }
1843
+ } else {
1844
+ const errorMessage = result?.message || "Failed to send reset code";
1845
+ setError(errorMessage);
1846
+ if (onError) {
1847
+ onError(new Error(errorMessage));
1848
+ }
1849
+ }
1850
+ } catch (err) {
1851
+ const errorMessage = err.message || "Failed to send reset code";
1852
+ setError(errorMessage);
1853
+ if (onError) {
1854
+ onError(new Error(errorMessage));
1855
+ }
1856
+ } finally {
1857
+ setLoading(false);
1858
+ }
1859
+ }
1860
+ async function handleVerifyCode(code) {
1861
+ setIsVerifyingCode(true);
1862
+ setError("");
1863
+ setVerificationCode(code);
1864
+ try {
1865
+ const result = await insforge.auth.verifyResetPasswordCode({ email, code });
1866
+ if (result.error) {
1867
+ throw new Error(result.error.message || "Failed to verify code");
1868
+ }
1869
+ if (result.data) {
1870
+ setResetToken(result.data.resetToken);
1871
+ setStep("password");
1872
+ }
1873
+ } catch (err) {
1874
+ setError(err.message || "Invalid verification code");
1875
+ setVerificationCode("");
1876
+ } finally {
1877
+ setIsVerifyingCode(false);
1878
+ }
1879
+ }
1880
+ const handleResendCode = react.useCallback(async () => {
1881
+ setResendDisabled(true);
1882
+ setResendCountdown(60);
1883
+ setIsSendingCode(true);
1884
+ setError("");
1885
+ try {
1886
+ await sendPasswordResetCode(email);
1887
+ } catch (err) {
1888
+ setError(err.message || "Failed to resend code");
1889
+ setResendDisabled(false);
1890
+ setResendCountdown(0);
1891
+ } finally {
1892
+ setIsSendingCode(false);
1893
+ }
1894
+ }, [email, sendPasswordResetCode]);
1895
+ function handlePasswordResetSuccess() {
1896
+ if (onSuccess) {
1897
+ onSuccess();
1898
+ }
1899
+ }
1900
+ if (!emailConfig) {
1901
+ return null;
1902
+ }
1903
+ if (step === "email") {
1904
+ return /* @__PURE__ */ jsxRuntime.jsx(
1905
+ ForgotPasswordForm,
1906
+ {
1907
+ email,
1908
+ onEmailChange: setEmail,
1909
+ onSubmit: handleEmailSubmit,
1910
+ error,
1911
+ loading,
1912
+ success,
1913
+ backToSignInUrl,
1914
+ ...uiProps
1915
+ }
1916
+ );
1917
+ }
1918
+ if (step === "code") {
1919
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1920
+ AuthContainer,
1921
+ {
1922
+ appearance: {
1923
+ containerClassName: uiProps.appearance?.container,
1924
+ cardClassName: uiProps.appearance?.card
1925
+ },
1926
+ children: [
1927
+ /* @__PURE__ */ jsxRuntime.jsx(
1928
+ AuthHeader,
1929
+ {
1930
+ title: "Enter Reset Code",
1931
+ subtitle: `We've sent a 6-digit verification code to ${email}. Please enter it below to reset your password. The code will expire in 10 minutes.`,
1932
+ appearance: {
1933
+ containerClassName: uiProps.appearance?.header?.container,
1934
+ titleClassName: uiProps.appearance?.header?.title,
1935
+ subtitleClassName: uiProps.appearance?.header?.subtitle
1936
+ }
1937
+ }
1938
+ ),
1939
+ /* @__PURE__ */ jsxRuntime.jsx(AuthErrorBanner, { error, className: uiProps.appearance?.errorBanner }),
1940
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col gap-6 items-center", children: [
1941
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-neutral-100 dark:bg-neutral-800 rounded-lg px-4 pt-4 pb-6 flex flex-col gap-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 mt-2", children: [
1942
+ /* @__PURE__ */ jsxRuntime.jsx(
1943
+ AuthVerificationCodeInput,
1944
+ {
1945
+ value: verificationCode,
1946
+ onChange: setVerificationCode,
1947
+ email,
1948
+ disabled: isVerifyingCode,
1949
+ onComplete: (code) => {
1950
+ void handleVerifyCode(code);
1951
+ }
1952
+ }
1953
+ ),
1954
+ isVerifyingCode && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: "Verifying..." })
1955
+ ] }) }),
1956
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full text-sm text-center text-neutral-600 dark:text-neutral-400", children: [
1957
+ "Didn't receive the email?",
1958
+ " ",
1959
+ /* @__PURE__ */ jsxRuntime.jsx(
1960
+ "button",
1961
+ {
1962
+ onClick: () => {
1963
+ void handleResendCode();
1964
+ },
1965
+ disabled: resendDisabled || isSendingCode,
1966
+ className: "text-black dark:text-white font-medium transition-colors disabled:cursor-not-allowed cursor-pointer hover:underline disabled:no-underline disabled:opacity-50",
1967
+ children: isSendingCode ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
1968
+ }
1969
+ )
1970
+ ] })
1971
+ ] }),
1972
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: backToSignInUrl, className: "text-black dark:text-white font-medium", children: "Back to Sign In" }) })
1973
+ ]
1974
+ }
1975
+ );
1976
+ }
1977
+ return /* @__PURE__ */ jsxRuntime.jsx(
1978
+ ResetPassword,
1979
+ {
1980
+ token: resetToken,
1981
+ backToSignInUrl,
1982
+ onSuccess: handlePasswordResetSuccess,
1983
+ onError,
1984
+ appearance: uiProps.appearance
1985
+ }
1986
+ );
1987
+ }
1988
+ function VerifyEmailStatus({
1989
+ status,
1990
+ error,
1991
+ appearance = {},
1992
+ verifyingTitle = "Verifying your email...",
1993
+ successTitle = "Email Verified!",
1994
+ successMessage = "Your email has been verified successfully. You can close this page and return to your app.",
1995
+ errorTitle = "Verification Failed"
1996
+ }) {
1997
+ if (status === "verifying") {
1998
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col items-center justify-center gap-6", children: [
1999
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white", children: verifyingTitle }),
2000
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-black dark:border-white" })
2001
+ ] }) });
2002
+ }
2003
+ if (status === "error") {
2004
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex flex-col items-stretch justify-center gap-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start justify-center gap-2", children: [
2005
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold text-black dark:text-white", children: errorTitle }),
2006
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed", children: [
2007
+ error || "The verification link is invalid or has expired.",
2008
+ " Please try again or contact support if the problem persists. You can close this page and return to your app."
2009
+ ] })
2010
+ ] }) }) });
2011
+ }
2012
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex flex-col items-stretch justify-center gap-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
2013
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-8 h-8 text-green-600 dark:text-green-400", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7" }) }) }),
2014
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white text-center", children: successTitle }),
2015
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: successMessage })
2016
+ ] }) }) });
2017
+ }
2018
+ function VerifyEmail({
2019
+ token,
2020
+ onSuccess,
2021
+ onError,
2022
+ ...uiProps
2023
+ }) {
2024
+ const { verifyEmail } = useInsforge();
2025
+ const [status, setStatus] = react.useState("verifying");
2026
+ const [error, setError] = react.useState("");
2027
+ react.useEffect(() => {
2028
+ const verifyEmailFn = async () => {
2029
+ if (!token) {
2030
+ const errorMessage = "Invalid verification link. Missing required token.";
2031
+ setError(errorMessage);
2032
+ setStatus("error");
2033
+ if (onError) {
2034
+ onError(new Error(errorMessage));
2035
+ }
2036
+ return;
2037
+ }
2038
+ try {
2039
+ const result = await verifyEmail(token);
2040
+ if (!result?.accessToken) {
2041
+ const errorMessage = result ? "Verification succeeded but no access token received" : "Email verification failed";
2042
+ setError(errorMessage);
2043
+ setStatus("error");
2044
+ if (onError) {
2045
+ onError(new Error(errorMessage));
2046
+ }
2047
+ return;
2048
+ }
2049
+ setStatus("success");
2050
+ if (onSuccess) {
2051
+ onSuccess({
2052
+ accessToken: result.accessToken,
2053
+ user: result.user
2054
+ });
2055
+ }
2056
+ } catch (err) {
2057
+ const errorMessage = err.message || "Email verification failed";
2058
+ setError(errorMessage);
2059
+ setStatus("error");
2060
+ if (onError) {
2061
+ onError(new Error(errorMessage));
2062
+ }
2063
+ }
2064
+ };
2065
+ void verifyEmailFn();
2066
+ }, [token, verifyEmail, onSuccess, onError]);
2067
+ return /* @__PURE__ */ jsxRuntime.jsx(VerifyEmailStatus, { status, error, ...uiProps });
2068
+ }
2069
+ function UserButton({
2070
+ afterSignOutUrl = "/",
2071
+ mode = "detailed",
2072
+ appearance = {}
2073
+ }) {
2074
+ const { user, signOut } = useInsforge();
2075
+ const [isOpen, setIsOpen] = react.useState(false);
2076
+ const [imageError, setImageError] = react.useState(false);
2077
+ const dropdownRef = react.useRef(null);
2078
+ react.useEffect(() => {
2079
+ setImageError(false);
2080
+ const avatarUrl = user?.avatarUrl;
2081
+ if (!avatarUrl) return;
2082
+ const checkImageUrl = async () => {
2083
+ try {
2084
+ const response = await fetch(avatarUrl, {
2085
+ method: "HEAD",
2086
+ cache: "no-cache"
2087
+ });
2088
+ if (!response.ok) {
2089
+ setImageError(true);
2090
+ }
2091
+ } catch (error) {
2092
+ setImageError(true);
2093
+ }
2094
+ };
2095
+ checkImageUrl();
2096
+ }, [user?.avatarUrl]);
2097
+ react.useEffect(() => {
2098
+ function handleClickOutside(event) {
2099
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
2100
+ setIsOpen(false);
2101
+ }
2102
+ }
2103
+ if (isOpen) {
2104
+ document.addEventListener("mousedown", handleClickOutside);
2105
+ }
2106
+ return () => {
2107
+ document.removeEventListener("mousedown", handleClickOutside);
2108
+ };
2109
+ }, [isOpen]);
2110
+ async function handleSignOut() {
2111
+ await signOut();
2112
+ setIsOpen(false);
2113
+ window.location.href = afterSignOutUrl;
2114
+ }
2115
+ if (!user) return null;
2116
+ const initials = user.name ? user.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
2117
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2118
+ "div",
2119
+ {
2120
+ className: cn("relative inline-block", appearance.containerClassName),
2121
+ ref: dropdownRef,
2122
+ children: [
2123
+ /* @__PURE__ */ jsxRuntime.jsxs(
2124
+ "button",
2125
+ {
2126
+ className: cn(
2127
+ "p-1 bg-transparent border-0 rounded-full cursor-pointer transition-all duration-200",
2128
+ "flex items-center justify-center gap-2",
2129
+ "hover:bg-black/5",
2130
+ mode === "detailed" && "rounded-lg p-2",
2131
+ appearance.buttonClassName
2132
+ ),
2133
+ onClick: () => setIsOpen(!isOpen),
2134
+ "aria-expanded": isOpen,
2135
+ "aria-haspopup": "true",
2136
+ children: [
2137
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-10 h-10 bg-blue-500 rounded-full", children: user.avatarUrl && !imageError ? /* @__PURE__ */ jsxRuntime.jsx(
2138
+ "img",
2139
+ {
1283
2140
  src: user.avatarUrl,
1284
2141
  alt: user.email,
1285
2142
  onError: () => setImageError(true),
@@ -1446,271 +2303,11 @@ function InsforgeCallback({
1446
2303
  ] }) });
1447
2304
  return loadingComponent || defaultLoading;
1448
2305
  }
1449
- function ForgotPasswordForm({
1450
- email,
1451
- onEmailChange,
1452
- onSubmit,
1453
- error,
1454
- loading = false,
1455
- success = false,
1456
- appearance = {},
1457
- title = "Forgot Password?",
1458
- subtitle = "Enter your email address and we'll send you a code to reset your password.",
1459
- emailLabel = "Email",
1460
- emailPlaceholder = "example@email.com",
1461
- submitButtonText = "Send Reset Code",
1462
- loadingButtonText = "Sending...",
1463
- backToSignInText = "Remember your password?",
1464
- backToSignInUrl = "/sign-in",
1465
- successTitle = "Check Your Email",
1466
- successMessage
1467
- }) {
1468
- if (success) {
1469
- return /* @__PURE__ */ jsxRuntime.jsx(
1470
- AuthContainer,
1471
- {
1472
- appearance: {
1473
- containerClassName: appearance.container,
1474
- cardClassName: appearance.card
1475
- },
1476
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
1477
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-8 h-8 text-green-600 dark:text-green-400", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7" }) }) }),
1478
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white text-center", children: successTitle }),
1479
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: successMessage || `We've sent a password reset link to ${email}. Please check your email and follow the instructions.` }),
1480
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: backToSignInUrl, className: "mt-4 text-black dark:text-white font-medium", children: "Back to Sign In" })
1481
- ] })
1482
- }
1483
- );
1484
- }
1485
- return /* @__PURE__ */ jsxRuntime.jsxs(
1486
- AuthContainer,
1487
- {
1488
- appearance: {
1489
- containerClassName: appearance.container,
1490
- cardClassName: appearance.card
1491
- },
1492
- children: [
1493
- /* @__PURE__ */ jsxRuntime.jsx(
1494
- AuthHeader,
1495
- {
1496
- title,
1497
- subtitle,
1498
- appearance: {
1499
- containerClassName: appearance.header?.container,
1500
- titleClassName: appearance.header?.title,
1501
- subtitleClassName: appearance.header?.subtitle
1502
- }
1503
- }
1504
- ),
1505
- /* @__PURE__ */ jsxRuntime.jsx(
1506
- AuthErrorBanner,
1507
- {
1508
- error: error || "",
1509
- className: appearance.errorBanner
1510
- }
1511
- ),
1512
- /* @__PURE__ */ jsxRuntime.jsxs(
1513
- "form",
1514
- {
1515
- onSubmit,
1516
- noValidate: true,
1517
- className: appearance.form?.container || "flex flex-col items-stretch justify-center gap-6",
1518
- children: [
1519
- /* @__PURE__ */ jsxRuntime.jsx(
1520
- AuthFormField,
1521
- {
1522
- id: "email",
1523
- type: "email",
1524
- label: emailLabel,
1525
- placeholder: emailPlaceholder,
1526
- value: email,
1527
- onChange: (e) => onEmailChange(e.target.value),
1528
- required: true,
1529
- autoComplete: "email",
1530
- appearance: {
1531
- containerClassName: appearance.form?.emailField?.container,
1532
- labelClassName: appearance.form?.emailField?.label,
1533
- inputClassName: appearance.form?.emailField?.input
1534
- }
1535
- }
1536
- ),
1537
- /* @__PURE__ */ jsxRuntime.jsx(
1538
- AuthSubmitButton,
1539
- {
1540
- isLoading: loading,
1541
- disabled: loading,
1542
- className: appearance.button,
1543
- children: loading ? loadingButtonText : submitButtonText
1544
- }
1545
- )
1546
- ]
1547
- }
1548
- ),
1549
- /* @__PURE__ */ jsxRuntime.jsx(
1550
- AuthLink,
1551
- {
1552
- text: backToSignInText,
1553
- linkText: "Back to Sign In",
1554
- href: backToSignInUrl,
1555
- appearance: {
1556
- containerClassName: appearance.link?.container,
1557
- linkClassName: appearance.link?.link
1558
- }
1559
- }
1560
- )
1561
- ]
1562
- }
1563
- );
1564
- }
1565
- function ResetPasswordForm({
1566
- newPassword,
1567
- confirmPassword,
1568
- onNewPasswordChange,
1569
- onConfirmPasswordChange,
1570
- onSubmit,
1571
- error,
1572
- loading = false,
1573
- emailAuthConfig,
1574
- appearance = {},
1575
- title = "Reset Password",
1576
- subtitle = "Enter your new password below.",
1577
- newPasswordLabel = "New Password",
1578
- newPasswordPlaceholder = "\u2022\u2022\u2022\u2022\u2022\u2022",
1579
- confirmPasswordLabel = "Confirm Password",
1580
- confirmPasswordPlaceholder = "\u2022\u2022\u2022\u2022\u2022\u2022",
1581
- submitButtonText = "Reset Password",
1582
- loadingButtonText = "Resetting...",
1583
- backToSignInText = "",
1584
- backToSignInUrl = "/sign-in"
1585
- }) {
1586
- return /* @__PURE__ */ jsxRuntime.jsxs(
1587
- AuthContainer,
1588
- {
1589
- appearance: {
1590
- containerClassName: appearance.container,
1591
- cardClassName: appearance.card
1592
- },
1593
- children: [
1594
- /* @__PURE__ */ jsxRuntime.jsx(
1595
- AuthHeader,
1596
- {
1597
- title,
1598
- subtitle,
1599
- appearance: {
1600
- containerClassName: appearance.header?.container,
1601
- titleClassName: appearance.header?.title,
1602
- subtitleClassName: appearance.header?.subtitle
1603
- }
1604
- }
1605
- ),
1606
- /* @__PURE__ */ jsxRuntime.jsx(
1607
- AuthErrorBanner,
1608
- {
1609
- error: error || "",
1610
- className: appearance.errorBanner
1611
- }
1612
- ),
1613
- /* @__PURE__ */ jsxRuntime.jsxs(
1614
- "form",
1615
- {
1616
- onSubmit,
1617
- noValidate: true,
1618
- className: appearance.form?.container || "flex flex-col items-stretch justify-center gap-6",
1619
- children: [
1620
- /* @__PURE__ */ jsxRuntime.jsx(
1621
- AuthPasswordField,
1622
- {
1623
- id: "newPassword",
1624
- label: newPasswordLabel,
1625
- placeholder: newPasswordPlaceholder,
1626
- value: newPassword,
1627
- onChange: (e) => onNewPasswordChange(e.target.value),
1628
- required: true,
1629
- autoComplete: "new-password",
1630
- showStrengthIndicator: true,
1631
- emailAuthConfig,
1632
- appearance: {
1633
- containerClassName: appearance.form?.newPasswordField?.container,
1634
- labelClassName: appearance.form?.newPasswordField?.label,
1635
- inputClassName: appearance.form?.newPasswordField?.input
1636
- }
1637
- }
1638
- ),
1639
- /* @__PURE__ */ jsxRuntime.jsx(
1640
- AuthPasswordField,
1641
- {
1642
- id: "confirmPassword",
1643
- label: confirmPasswordLabel,
1644
- placeholder: confirmPasswordPlaceholder,
1645
- value: confirmPassword,
1646
- onChange: (e) => onConfirmPasswordChange(e.target.value),
1647
- required: true,
1648
- autoComplete: "new-password",
1649
- emailAuthConfig,
1650
- appearance: {
1651
- containerClassName: appearance.form?.confirmPasswordField?.container,
1652
- labelClassName: appearance.form?.confirmPasswordField?.label,
1653
- inputClassName: appearance.form?.confirmPasswordField?.input
1654
- }
1655
- }
1656
- ),
1657
- /* @__PURE__ */ jsxRuntime.jsx(
1658
- AuthSubmitButton,
1659
- {
1660
- isLoading: loading,
1661
- disabled: loading,
1662
- className: appearance.button,
1663
- children: loading ? loadingButtonText : submitButtonText
1664
- }
1665
- )
1666
- ]
1667
- }
1668
- ),
1669
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: appearance.backToSignIn || "text-center text-sm text-gray-600 dark:text-gray-400", children: [
1670
- backToSignInText && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1671
- backToSignInText,
1672
- " "
1673
- ] }),
1674
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: backToSignInUrl, className: "text-black dark:text-white font-medium", children: "Back to Sign In" })
1675
- ] })
1676
- ]
1677
- }
1678
- );
1679
- }
1680
- function VerifyEmailStatus({
1681
- status,
1682
- error,
1683
- appearance = {},
1684
- verifyingTitle = "Verifying your email...",
1685
- successTitle = "Email Verified!",
1686
- successMessage = "Your email has been verified successfully. You can close this page and return to your app.",
1687
- errorTitle = "Verification Failed"
1688
- }) {
1689
- if (status === "verifying") {
1690
- return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col items-center justify-center gap-6", children: [
1691
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white", children: verifyingTitle }),
1692
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-black dark:border-white" })
1693
- ] }) });
1694
- }
1695
- if (status === "error") {
1696
- return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex flex-col items-stretch justify-center gap-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start justify-center gap-2", children: [
1697
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold text-black dark:text-white", children: errorTitle }),
1698
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed", children: [
1699
- error || "The verification link is invalid or has expired.",
1700
- " Please try again or contact support if the problem persists. You can close this page and return to your app."
1701
- ] })
1702
- ] }) }) });
1703
- }
1704
- return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex flex-col items-stretch justify-center gap-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
1705
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-8 h-8 text-green-600 dark:text-green-400", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7" }) }) }),
1706
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white text-center", children: successTitle }),
1707
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: successMessage })
1708
- ] }) }) });
1709
- }
1710
2306
 
1711
2307
  exports.AuthBranding = AuthBranding;
1712
2308
  exports.AuthContainer = AuthContainer;
1713
2309
  exports.AuthDivider = AuthDivider;
2310
+ exports.AuthEmailVerificationStep = AuthEmailVerificationStep;
1714
2311
  exports.AuthErrorBanner = AuthErrorBanner;
1715
2312
  exports.AuthFormField = AuthFormField;
1716
2313
  exports.AuthHeader = AuthHeader;
@@ -1721,9 +2318,11 @@ exports.AuthPasswordField = AuthPasswordField;
1721
2318
  exports.AuthPasswordStrengthIndicator = AuthPasswordStrengthIndicator;
1722
2319
  exports.AuthSubmitButton = AuthSubmitButton;
1723
2320
  exports.AuthVerificationCodeInput = AuthVerificationCodeInput;
2321
+ exports.ForgotPassword = ForgotPassword;
1724
2322
  exports.ForgotPasswordForm = ForgotPasswordForm;
1725
2323
  exports.InsforgeCallback = InsforgeCallback;
1726
2324
  exports.Protect = Protect;
2325
+ exports.ResetPassword = ResetPassword;
1727
2326
  exports.ResetPasswordForm = ResetPasswordForm;
1728
2327
  exports.SignIn = SignIn;
1729
2328
  exports.SignInForm = SignInForm;
@@ -1732,6 +2331,7 @@ exports.SignUpForm = SignUpForm;
1732
2331
  exports.SignedIn = SignedIn;
1733
2332
  exports.SignedOut = SignedOut;
1734
2333
  exports.UserButton = UserButton;
2334
+ exports.VerifyEmail = VerifyEmail;
1735
2335
  exports.VerifyEmailStatus = VerifyEmailStatus;
1736
2336
  exports.validatePasswordStrength = validatePasswordStrength;
1737
2337
  //# sourceMappingURL=components.js.map