@insforge/react 0.2.10 → 0.3.0

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,126 @@ function AuthVerificationCodeInput({
758
766
  )) })
759
767
  ] });
760
768
  }
769
+ function AuthEmailVerificationStep({
770
+ email,
771
+ title = "Verify Your Email",
772
+ description,
773
+ method = "code",
774
+ onVerifyCode
775
+ }) {
776
+ const { baseUrl } = useInsforge();
777
+ const [insforge] = react.useState(() => sdk.createClient({ baseUrl }));
778
+ const [resendDisabled, setResendDisabled] = react.useState(true);
779
+ const [resendCountdown, setResendCountdown] = react.useState(60);
780
+ const [isSending, setIsSending] = react.useState(false);
781
+ const [verificationCode, setVerificationCode] = react.useState("");
782
+ const [isVerifying, setIsVerifying] = react.useState(false);
783
+ const [verificationError, setVerificationError] = react.useState("");
784
+ 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.";
785
+ react.useEffect(() => {
786
+ const sendInitialEmail = async () => {
787
+ try {
788
+ if (method === "code") {
789
+ await insforge.auth.sendVerificationCode({ email });
790
+ } else {
791
+ await insforge.auth.sendVerificationLink({ email });
792
+ }
793
+ } catch {
794
+ }
795
+ };
796
+ void sendInitialEmail();
797
+ }, [email, method, insforge.auth]);
798
+ react.useEffect(() => {
799
+ if (resendCountdown > 0) {
800
+ const timer = setInterval(() => {
801
+ setResendCountdown((prev) => {
802
+ if (prev <= 1) {
803
+ setResendDisabled(false);
804
+ return 0;
805
+ }
806
+ return prev - 1;
807
+ });
808
+ }, 1e3);
809
+ return () => clearInterval(timer);
810
+ }
811
+ }, [resendCountdown]);
812
+ const handleResend = async () => {
813
+ setResendDisabled(true);
814
+ setResendCountdown(60);
815
+ setIsSending(true);
816
+ setVerificationError("");
817
+ try {
818
+ if (method === "code") {
819
+ await insforge.auth.sendVerificationCode({ email });
820
+ } else {
821
+ await insforge.auth.sendVerificationLink({ email });
822
+ }
823
+ } catch {
824
+ setResendDisabled(false);
825
+ setResendCountdown(0);
826
+ } finally {
827
+ setIsSending(false);
828
+ }
829
+ };
830
+ const handleVerifyCode = async (code) => {
831
+ if (!onVerifyCode) {
832
+ return;
833
+ }
834
+ setIsVerifying(true);
835
+ setVerificationError("");
836
+ try {
837
+ await onVerifyCode(code);
838
+ } catch (error) {
839
+ setVerificationError(
840
+ error instanceof Error ? error.message : "Invalid verification code. Please try again."
841
+ );
842
+ setVerificationCode("");
843
+ } finally {
844
+ setIsVerifying(false);
845
+ }
846
+ };
847
+ const displayDescription = description || defaultDescription;
848
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col gap-6 items-center", children: [
849
+ /* @__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: [
850
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-black dark:text-white", children: title }),
851
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-neutral-600 dark:text-neutral-400 text-sm leading-relaxed", children: displayDescription.split("{email}").map((part, index, array) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
852
+ part,
853
+ index < array.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-black dark:text-white", children: email })
854
+ ] }, index)) }),
855
+ method === "code" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 mt-2", children: [
856
+ /* @__PURE__ */ jsxRuntime.jsx(
857
+ AuthVerificationCodeInput,
858
+ {
859
+ value: verificationCode,
860
+ onChange: setVerificationCode,
861
+ email,
862
+ disabled: isVerifying,
863
+ onComplete: (code) => {
864
+ void handleVerifyCode(code);
865
+ }
866
+ }
867
+ ),
868
+ verificationError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-400 text-center", children: verificationError }),
869
+ isVerifying && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: "Verifying..." })
870
+ ] })
871
+ ] }),
872
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full text-sm text-center text-neutral-600 dark:text-neutral-400", children: [
873
+ "Didn't receive the email?",
874
+ " ",
875
+ /* @__PURE__ */ jsxRuntime.jsx(
876
+ "button",
877
+ {
878
+ onClick: () => {
879
+ void handleResend();
880
+ },
881
+ disabled: resendDisabled || isSending,
882
+ className: "text-black dark:text-white font-medium transition-colors disabled:cursor-not-allowed cursor-pointer hover:underline disabled:no-underline disabled:opacity-50",
883
+ children: isSending ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
884
+ }
885
+ )
886
+ ] })
887
+ ] });
888
+ }
761
889
  function SignInForm({
762
890
  email,
763
891
  password,
@@ -922,6 +1050,7 @@ function SignIn({
922
1050
  const [password, setPassword] = react.useState("");
923
1051
  const [error, setError] = react.useState("");
924
1052
  const [loading, setLoading] = react.useState(false);
1053
+ const [step, setStep] = react.useState("form");
925
1054
  const [oauthLoading, setOauthLoading] = react.useState(
926
1055
  null
927
1056
  );
@@ -933,6 +1062,11 @@ function SignIn({
933
1062
  try {
934
1063
  const result = await signIn(email, password);
935
1064
  if ("error" in result) {
1065
+ if (result.statusCode === 403) {
1066
+ setStep("awaiting-verification");
1067
+ setLoading(false);
1068
+ return;
1069
+ }
936
1070
  throw new Error(result.error);
937
1071
  }
938
1072
  const { user, accessToken } = result;
@@ -947,6 +1081,21 @@ function SignIn({
947
1081
  setLoading(false);
948
1082
  }
949
1083
  }
1084
+ async function handleVerifyCode(code) {
1085
+ try {
1086
+ const result = await insforge.auth.verifyEmail({ email, otp: code });
1087
+ if (result.error) {
1088
+ throw new Error(result.error.message || "Verification failed");
1089
+ }
1090
+ if (result.data?.accessToken) {
1091
+ if (onSuccess && result.data.user) {
1092
+ onSuccess(result.data.user, result.data.accessToken);
1093
+ }
1094
+ }
1095
+ } catch (err) {
1096
+ throw new Error(err.message || "Invalid verification code");
1097
+ }
1098
+ }
950
1099
  async function handleOAuth(provider) {
951
1100
  try {
952
1101
  setOauthLoading(provider);
@@ -965,7 +1114,7 @@ function SignIn({
965
1114
  if (!emailConfig) {
966
1115
  return null;
967
1116
  }
968
- return /* @__PURE__ */ jsxRuntime.jsx(
1117
+ return step === "form" ? /* @__PURE__ */ jsxRuntime.jsx(
969
1118
  SignInForm,
970
1119
  {
971
1120
  email,
@@ -981,6 +1130,12 @@ function SignIn({
981
1130
  emailAuthConfig: emailConfig,
982
1131
  ...uiProps
983
1132
  }
1133
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1134
+ AuthEmailVerificationStep,
1135
+ {
1136
+ email,
1137
+ onVerifyCode: handleVerifyCode
1138
+ }
984
1139
  );
985
1140
  }
986
1141
  function SignUpForm({
@@ -1131,6 +1286,34 @@ function SignUpForm({
1131
1286
  }
1132
1287
  );
1133
1288
  }
1289
+ var emailSchema = zod.z.string().min(1, "Email is required").email("Invalid email address");
1290
+ function createPasswordSchema(options) {
1291
+ const {
1292
+ minLength = 6,
1293
+ requireUppercase = false,
1294
+ requireLowercase = false,
1295
+ requireNumber = false,
1296
+ requireSpecialChar = false
1297
+ } = options || {};
1298
+ let schema = zod.z.string().min(minLength, `Password must be at least ${minLength} characters`);
1299
+ if (requireUppercase) {
1300
+ schema = schema.regex(/[A-Z]/, "Password must contain at least one uppercase letter");
1301
+ }
1302
+ if (requireLowercase) {
1303
+ schema = schema.regex(/[a-z]/, "Password must contain at least one lowercase letter");
1304
+ }
1305
+ if (requireNumber) {
1306
+ schema = schema.regex(/\d/, "Password must contain at least one number");
1307
+ }
1308
+ if (requireSpecialChar) {
1309
+ schema = schema.regex(
1310
+ /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/,
1311
+ "Password must contain at least one special character"
1312
+ );
1313
+ }
1314
+ return schema;
1315
+ }
1316
+ createPasswordSchema();
1134
1317
  function SignUp({
1135
1318
  afterSignUpUrl,
1136
1319
  onSuccess,
@@ -1143,6 +1326,7 @@ function SignUp({
1143
1326
  const [password, setPassword] = react.useState("");
1144
1327
  const [error, setError] = react.useState("");
1145
1328
  const [loading, setLoading] = react.useState(false);
1329
+ const [step, setStep] = react.useState("form");
1146
1330
  const [oauthLoading, setOauthLoading] = react.useState(
1147
1331
  null
1148
1332
  );
@@ -1151,19 +1335,46 @@ function SignUp({
1151
1335
  e.preventDefault();
1152
1336
  setLoading(true);
1153
1337
  setError("");
1154
- if (emailConfig && !validatePasswordStrength(password, emailConfig)) {
1155
- setError("Password does not meet all requirements");
1338
+ if (!emailConfig) {
1339
+ setError("Configuration not loaded. Please refresh the page.");
1340
+ setLoading(false);
1341
+ return;
1342
+ }
1343
+ const emailValidation = emailSchema.safeParse(email);
1344
+ if (!emailValidation.success) {
1345
+ const firstError = emailValidation.error.issues[0];
1346
+ setError(firstError.message);
1347
+ setLoading(false);
1348
+ return;
1349
+ }
1350
+ const passwordZodSchema = createPasswordSchema({
1351
+ minLength: emailConfig.passwordMinLength,
1352
+ requireUppercase: emailConfig.requireUppercase,
1353
+ requireLowercase: emailConfig.requireLowercase,
1354
+ requireNumber: emailConfig.requireNumber,
1355
+ requireSpecialChar: emailConfig.requireSpecialChar
1356
+ });
1357
+ const passwordValidation = passwordZodSchema.safeParse(password);
1358
+ if (!passwordValidation.success) {
1359
+ const firstError = passwordValidation.error.issues[0];
1360
+ setError(firstError.message);
1156
1361
  setLoading(false);
1157
1362
  return;
1158
1363
  }
1159
1364
  try {
1160
- const result = await signUp(email, password);
1365
+ const result = await signUp(emailValidation.data, password);
1161
1366
  if ("error" in result) {
1162
1367
  throw new Error(result.error);
1163
1368
  }
1164
- const { user, accessToken } = result;
1165
- if (onSuccess) {
1166
- if (user) onSuccess(user, accessToken || "");
1369
+ if (result.requiresEmailVerification && !result.accessToken) {
1370
+ setStep("awaiting-verification");
1371
+ setLoading(false);
1372
+ return;
1373
+ }
1374
+ if (result.accessToken && result.user) {
1375
+ if (onSuccess) {
1376
+ onSuccess(result.user, result.accessToken);
1377
+ }
1167
1378
  }
1168
1379
  } catch (err) {
1169
1380
  const errorMessage = err.message || "Sign up failed";
@@ -1173,6 +1384,21 @@ function SignUp({
1173
1384
  setLoading(false);
1174
1385
  }
1175
1386
  }
1387
+ async function handleVerifyCode(code) {
1388
+ try {
1389
+ const result = await insforge.auth.verifyEmail({ email, otp: code });
1390
+ if (result.error) {
1391
+ throw new Error(result.error.message || "Verification failed");
1392
+ }
1393
+ if (result.data?.accessToken) {
1394
+ if (onSuccess && result.data.user) {
1395
+ onSuccess(result.data.user, result.data.accessToken);
1396
+ }
1397
+ }
1398
+ } catch (err) {
1399
+ throw new Error(err.message || "Invalid verification code");
1400
+ }
1401
+ }
1176
1402
  async function handleOAuth(provider) {
1177
1403
  try {
1178
1404
  setOauthLoading(provider);
@@ -1191,7 +1417,7 @@ function SignUp({
1191
1417
  if (!emailConfig) {
1192
1418
  return null;
1193
1419
  }
1194
- return /* @__PURE__ */ jsxRuntime.jsx(
1420
+ return step === "form" ? /* @__PURE__ */ jsxRuntime.jsx(
1195
1421
  SignUpForm,
1196
1422
  {
1197
1423
  email,
@@ -1207,306 +1433,75 @@ function SignUp({
1207
1433
  emailAuthConfig: emailConfig,
1208
1434
  ...uiProps
1209
1435
  }
1436
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1437
+ AuthEmailVerificationStep,
1438
+ {
1439
+ email,
1440
+ onVerifyCode: handleVerifyCode
1441
+ }
1210
1442
  );
1211
1443
  }
1212
- function UserButton({
1213
- afterSignOutUrl = "/",
1214
- mode = "detailed",
1215
- appearance = {}
1444
+ function ForgotPasswordForm({
1445
+ email,
1446
+ onEmailChange,
1447
+ onSubmit,
1448
+ error,
1449
+ loading = false,
1450
+ success = false,
1451
+ appearance = {},
1452
+ title = "Forgot Password?",
1453
+ subtitle = "Enter your email address and we'll send you a code to reset your password.",
1454
+ emailLabel = "Email",
1455
+ emailPlaceholder = "example@email.com",
1456
+ submitButtonText = "Send Reset Code",
1457
+ loadingButtonText = "Sending...",
1458
+ backToSignInText = "Remember your password?",
1459
+ backToSignInUrl = "/sign-in",
1460
+ successTitle = "Check Your Email",
1461
+ successMessage
1216
1462
  }) {
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);
1463
+ if (success) {
1464
+ return /* @__PURE__ */ jsxRuntime.jsx(
1465
+ AuthContainer,
1466
+ {
1467
+ appearance: {
1468
+ containerClassName: appearance.container,
1469
+ cardClassName: appearance.card
1470
+ },
1471
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
1472
+ /* @__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" }) }) }),
1473
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white text-center", children: successTitle }),
1474
+ /* @__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.` }),
1475
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: backToSignInUrl, className: "mt-4 text-black dark:text-white font-medium", children: "Back to Sign In" })
1476
+ ] })
1244
1477
  }
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;
1478
+ );
1257
1479
  }
1258
- if (!user) return null;
1259
- const initials = user.name ? user.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
1260
1480
  return /* @__PURE__ */ jsxRuntime.jsxs(
1261
- "div",
1481
+ AuthContainer,
1262
1482
  {
1263
- className: cn("relative inline-block", appearance.containerClassName),
1264
- ref: dropdownRef,
1483
+ appearance: {
1484
+ containerClassName: appearance.container,
1485
+ cardClassName: appearance.card
1486
+ },
1265
1487
  children: [
1266
- /* @__PURE__ */ jsxRuntime.jsxs(
1267
- "button",
1488
+ /* @__PURE__ */ jsxRuntime.jsx(
1489
+ AuthHeader,
1268
1490
  {
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",
1279
- 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
- {
1283
- src: user.avatarUrl,
1284
- alt: user.email,
1285
- onError: () => setImageError(true),
1286
- className: "rounded-full object-cover w-full h-full"
1287
- }
1288
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white font-semibold text-sm", children: initials }) }),
1289
- mode === "detailed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-0.5", children: [
1290
- user.name && /* @__PURE__ */ jsxRuntime.jsx(
1291
- "div",
1292
- {
1293
- className: cn(
1294
- "text-sm font-semibold text-gray-900 leading-5 text-left",
1295
- appearance.nameClassName
1296
- ),
1297
- children: user.name
1298
- }
1299
- ),
1300
- /* @__PURE__ */ jsxRuntime.jsx(
1301
- "div",
1302
- {
1303
- className: cn(
1304
- "text-xs text-gray-500 leading-4 text-left",
1305
- appearance.emailClassName
1306
- ),
1307
- children: user.email
1308
- }
1309
- )
1310
- ] })
1311
- ]
1491
+ title,
1492
+ subtitle,
1493
+ appearance: {
1494
+ containerClassName: appearance.header?.container,
1495
+ titleClassName: appearance.header?.title,
1496
+ subtitleClassName: appearance.header?.subtitle
1497
+ }
1312
1498
  }
1313
1499
  ),
1314
- isOpen && /* @__PURE__ */ jsxRuntime.jsx(
1315
- "div",
1500
+ /* @__PURE__ */ jsxRuntime.jsx(
1501
+ AuthErrorBanner,
1316
1502
  {
1317
- className: cn(
1318
- "absolute top-full right-0 mt-2 min-w-40",
1319
- "bg-white border border-gray-200 rounded-lg",
1320
- "shadow-lg z-50 overflow-hidden p-1",
1321
- appearance.dropdownClassName
1322
- ),
1323
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1324
- "button",
1325
- {
1326
- onClick: handleSignOut,
1327
- className: "flex items-center justify-start gap-2 w-full px-3 py-2 text-sm font-normal text-red-600 bg-transparent border-0 rounded-md cursor-pointer transition-colors hover:bg-red-50 text-left",
1328
- children: [
1329
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "w-5 h-5" }),
1330
- "Sign out"
1331
- ]
1332
- }
1333
- )
1334
- }
1335
- )
1336
- ]
1337
- }
1338
- );
1339
- }
1340
- function Protect({
1341
- children,
1342
- fallback,
1343
- redirectTo = "/sign-in",
1344
- condition,
1345
- onRedirect
1346
- }) {
1347
- const { isSignedIn, isLoaded, user } = useInsforge();
1348
- react.useEffect(() => {
1349
- if (isLoaded && !isSignedIn) {
1350
- if (onRedirect) {
1351
- onRedirect(redirectTo);
1352
- } else {
1353
- window.location.href = redirectTo;
1354
- }
1355
- } else if (isLoaded && isSignedIn && condition && user) {
1356
- if (!condition(user)) {
1357
- if (onRedirect) {
1358
- onRedirect(redirectTo);
1359
- } else {
1360
- window.location.href = redirectTo;
1361
- }
1362
- }
1363
- }
1364
- }, [isLoaded, isSignedIn, redirectTo, condition, user, onRedirect]);
1365
- if (!isLoaded) {
1366
- return fallback || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "insforge-loading", children: "Loading..." });
1367
- }
1368
- if (!isSignedIn) {
1369
- return fallback || null;
1370
- }
1371
- if (condition && user && !condition(user)) {
1372
- return fallback || null;
1373
- }
1374
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1375
- }
1376
- function SignedIn({ children }) {
1377
- const { isSignedIn, isLoaded } = useInsforge();
1378
- if (!isLoaded) return null;
1379
- if (!isSignedIn) return null;
1380
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1381
- }
1382
- function SignedOut({ children }) {
1383
- const { isSignedIn, isLoaded } = useInsforge();
1384
- if (!isLoaded) return null;
1385
- if (isSignedIn) return null;
1386
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1387
- }
1388
- function InsforgeCallback({
1389
- redirectTo = "/",
1390
- onSuccess,
1391
- onError,
1392
- loadingComponent,
1393
- onRedirect
1394
- }) {
1395
- const isProcessingRef = react.useRef(false);
1396
- const { isLoaded, isSignedIn } = useInsforge();
1397
- react.useEffect(() => {
1398
- if (!isLoaded) return;
1399
- if (isProcessingRef.current) return;
1400
- isProcessingRef.current = true;
1401
- const processCallback = async () => {
1402
- const searchParams = new URLSearchParams(window.location.search);
1403
- const error = searchParams.get("error");
1404
- if (error) {
1405
- if (onError) {
1406
- onError(error);
1407
- } else {
1408
- const errorUrl = "/?error=" + encodeURIComponent(error);
1409
- if (onRedirect) {
1410
- onRedirect(errorUrl);
1411
- } else {
1412
- window.location.href = errorUrl;
1413
- }
1414
- }
1415
- return;
1416
- }
1417
- if (!isSignedIn) {
1418
- const errorMsg = "authentication_failed";
1419
- if (onError) {
1420
- onError(errorMsg);
1421
- } else {
1422
- const errorUrl = "/?error=" + encodeURIComponent(errorMsg);
1423
- if (onRedirect) {
1424
- onRedirect(errorUrl);
1425
- } else {
1426
- window.location.href = errorUrl;
1427
- }
1428
- }
1429
- return;
1430
- }
1431
- window.history.replaceState({}, "", window.location.pathname);
1432
- if (onSuccess) {
1433
- onSuccess();
1434
- }
1435
- if (onRedirect) {
1436
- onRedirect(redirectTo);
1437
- } else {
1438
- window.location.href = redirectTo;
1439
- }
1440
- };
1441
- processCallback();
1442
- }, [isLoaded, isSignedIn, redirectTo, onSuccess, onError, onRedirect]);
1443
- const defaultLoading = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
1444
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold mb-4", children: "Completing authentication..." }),
1445
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto" })
1446
- ] }) });
1447
- return loadingComponent || defaultLoading;
1448
- }
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
1503
+ error: error || "",
1504
+ className: appearance.errorBanner
1510
1505
  }
1511
1506
  ),
1512
1507
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1677,40 +1672,620 @@ function ResetPasswordForm({
1677
1672
  }
1678
1673
  );
1679
1674
  }
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"
1675
+ function ResetPassword({
1676
+ token,
1677
+ backToSignInUrl = "/sign-in",
1678
+ onSuccess,
1679
+ onError,
1680
+ ...uiProps
1688
1681
  }) {
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
- ] }) });
1682
+ const { resetPassword } = useInsforge();
1683
+ const { emailConfig } = usePublicAuthConfig();
1684
+ const [newPassword, setNewPassword] = react.useState("");
1685
+ const [confirmPassword, setConfirmPassword] = react.useState("");
1686
+ const [error, setError] = react.useState("");
1687
+ const [loading, setLoading] = react.useState(false);
1688
+ async function handleSubmit(e) {
1689
+ e.preventDefault();
1690
+ setLoading(true);
1691
+ setError("");
1692
+ if (!emailConfig) {
1693
+ setError("Configuration not loaded. Please refresh the page.");
1694
+ setLoading(false);
1695
+ return;
1696
+ }
1697
+ if (newPassword !== confirmPassword) {
1698
+ setError("Passwords do not match");
1699
+ setLoading(false);
1700
+ return;
1701
+ }
1702
+ if (!token) {
1703
+ setError("Reset token is missing");
1704
+ setLoading(false);
1705
+ return;
1706
+ }
1707
+ const passwordZodSchema = createPasswordSchema({
1708
+ minLength: emailConfig.passwordMinLength,
1709
+ requireUppercase: emailConfig.requireUppercase,
1710
+ requireLowercase: emailConfig.requireLowercase,
1711
+ requireNumber: emailConfig.requireNumber,
1712
+ requireSpecialChar: emailConfig.requireSpecialChar
1713
+ });
1714
+ const passwordValidation = passwordZodSchema.safeParse(newPassword);
1715
+ if (!passwordValidation.success) {
1716
+ const firstError = passwordValidation.error.issues[0];
1717
+ setError(firstError.message);
1718
+ setLoading(false);
1719
+ return;
1720
+ }
1721
+ try {
1722
+ const result = await resetPassword(token, newPassword);
1723
+ if (result?.message) {
1724
+ if (onSuccess) {
1725
+ onSuccess(result.redirectTo);
1726
+ }
1727
+ } else {
1728
+ const errorMessage = "Failed to reset password";
1729
+ setError(errorMessage);
1730
+ if (onError) {
1731
+ onError(new Error(errorMessage));
1732
+ }
1733
+ }
1734
+ } catch (err) {
1735
+ const errorMessage = err.message || "Failed to reset password";
1736
+ setError(errorMessage);
1737
+ if (onError) {
1738
+ onError(new Error(errorMessage));
1739
+ }
1740
+ } finally {
1741
+ setLoading(false);
1742
+ }
1694
1743
  }
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
- ] }) }) });
1744
+ if (!emailConfig) {
1745
+ return null;
1703
1746
  }
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
- ] }) }) });
1747
+ return /* @__PURE__ */ jsxRuntime.jsx(
1748
+ ResetPasswordForm,
1749
+ {
1750
+ newPassword,
1751
+ confirmPassword,
1752
+ onNewPasswordChange: setNewPassword,
1753
+ onConfirmPasswordChange: setConfirmPassword,
1754
+ onSubmit: handleSubmit,
1755
+ error,
1756
+ loading,
1757
+ emailAuthConfig: emailConfig,
1758
+ backToSignInUrl,
1759
+ ...uiProps
1760
+ }
1761
+ );
1762
+ }
1763
+ function ForgotPassword({
1764
+ backToSignInUrl = "/sign-in",
1765
+ onSuccess,
1766
+ onError,
1767
+ ...uiProps
1768
+ }) {
1769
+ const { sendPasswordResetCode, baseUrl } = useInsforge();
1770
+ const { emailConfig } = usePublicAuthConfig();
1771
+ const [insforge] = react.useState(() => sdk.createClient({ baseUrl }));
1772
+ const [step, setStep] = react.useState("email");
1773
+ const [email, setEmail] = react.useState("");
1774
+ const [verificationCode, setVerificationCode] = react.useState("");
1775
+ const [resetToken, setResetToken] = react.useState("");
1776
+ const [error, setError] = react.useState("");
1777
+ const [loading, setLoading] = react.useState(false);
1778
+ const [success, setSuccess] = react.useState(false);
1779
+ const [resendDisabled, setResendDisabled] = react.useState(true);
1780
+ const [resendCountdown, setResendCountdown] = react.useState(60);
1781
+ const [isSendingCode, setIsSendingCode] = react.useState(false);
1782
+ const [isVerifyingCode, setIsVerifyingCode] = react.useState(false);
1783
+ react.useEffect(() => {
1784
+ if (resendCountdown > 0 && step === "code") {
1785
+ const timer = setInterval(() => {
1786
+ setResendCountdown((prev) => {
1787
+ if (prev <= 1) {
1788
+ setResendDisabled(false);
1789
+ return 0;
1790
+ }
1791
+ return prev - 1;
1792
+ });
1793
+ }, 1e3);
1794
+ return () => clearInterval(timer);
1795
+ }
1796
+ }, [resendCountdown, step]);
1797
+ async function handleEmailSubmit(e) {
1798
+ e.preventDefault();
1799
+ setLoading(true);
1800
+ setError("");
1801
+ const emailValidation = emailSchema.safeParse(email);
1802
+ if (!emailValidation.success) {
1803
+ const firstError = emailValidation.error.issues[0];
1804
+ setError(firstError.message);
1805
+ setLoading(false);
1806
+ return;
1807
+ }
1808
+ try {
1809
+ const result = await sendPasswordResetCode(emailValidation.data);
1810
+ if (result?.success) {
1811
+ if (emailConfig?.resetPasswordMethod === "link") {
1812
+ setSuccess(true);
1813
+ if (onSuccess) {
1814
+ onSuccess();
1815
+ }
1816
+ } else {
1817
+ setStep("code");
1818
+ setResendDisabled(true);
1819
+ setResendCountdown(60);
1820
+ }
1821
+ } else {
1822
+ const errorMessage = result?.message || "Failed to send reset code";
1823
+ setError(errorMessage);
1824
+ if (onError) {
1825
+ onError(new Error(errorMessage));
1826
+ }
1827
+ }
1828
+ } catch (err) {
1829
+ const errorMessage = err.message || "Failed to send reset code";
1830
+ setError(errorMessage);
1831
+ if (onError) {
1832
+ onError(new Error(errorMessage));
1833
+ }
1834
+ } finally {
1835
+ setLoading(false);
1836
+ }
1837
+ }
1838
+ async function handleVerifyCode(code) {
1839
+ setIsVerifyingCode(true);
1840
+ setError("");
1841
+ setVerificationCode(code);
1842
+ try {
1843
+ const result = await insforge.auth.verifyResetPasswordCode({ email, code });
1844
+ if (result.error) {
1845
+ throw new Error(result.error.message || "Failed to verify code");
1846
+ }
1847
+ if (result.data) {
1848
+ setResetToken(result.data.resetToken);
1849
+ setStep("password");
1850
+ }
1851
+ } catch (err) {
1852
+ setError(err.message || "Invalid verification code");
1853
+ setVerificationCode("");
1854
+ } finally {
1855
+ setIsVerifyingCode(false);
1856
+ }
1857
+ }
1858
+ const handleResendCode = react.useCallback(async () => {
1859
+ setResendDisabled(true);
1860
+ setResendCountdown(60);
1861
+ setIsSendingCode(true);
1862
+ setError("");
1863
+ try {
1864
+ await sendPasswordResetCode(email);
1865
+ } catch (err) {
1866
+ setError(err.message || "Failed to resend code");
1867
+ setResendDisabled(false);
1868
+ setResendCountdown(0);
1869
+ } finally {
1870
+ setIsSendingCode(false);
1871
+ }
1872
+ }, [email, sendPasswordResetCode]);
1873
+ function handlePasswordResetSuccess() {
1874
+ if (onSuccess) {
1875
+ onSuccess();
1876
+ }
1877
+ }
1878
+ if (!emailConfig) {
1879
+ return null;
1880
+ }
1881
+ if (step === "email") {
1882
+ return /* @__PURE__ */ jsxRuntime.jsx(
1883
+ ForgotPasswordForm,
1884
+ {
1885
+ email,
1886
+ onEmailChange: setEmail,
1887
+ onSubmit: handleEmailSubmit,
1888
+ error,
1889
+ loading,
1890
+ success,
1891
+ backToSignInUrl,
1892
+ ...uiProps
1893
+ }
1894
+ );
1895
+ }
1896
+ if (step === "code") {
1897
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1898
+ AuthContainer,
1899
+ {
1900
+ appearance: {
1901
+ containerClassName: uiProps.appearance?.container,
1902
+ cardClassName: uiProps.appearance?.card
1903
+ },
1904
+ children: [
1905
+ /* @__PURE__ */ jsxRuntime.jsx(
1906
+ AuthHeader,
1907
+ {
1908
+ title: "Enter Reset Code",
1909
+ 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.`,
1910
+ appearance: {
1911
+ containerClassName: uiProps.appearance?.header?.container,
1912
+ titleClassName: uiProps.appearance?.header?.title,
1913
+ subtitleClassName: uiProps.appearance?.header?.subtitle
1914
+ }
1915
+ }
1916
+ ),
1917
+ /* @__PURE__ */ jsxRuntime.jsx(AuthErrorBanner, { error, className: uiProps.appearance?.errorBanner }),
1918
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col gap-6 items-center", children: [
1919
+ /* @__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: [
1920
+ /* @__PURE__ */ jsxRuntime.jsx(
1921
+ AuthVerificationCodeInput,
1922
+ {
1923
+ value: verificationCode,
1924
+ onChange: setVerificationCode,
1925
+ email,
1926
+ disabled: isVerifyingCode,
1927
+ onComplete: (code) => {
1928
+ void handleVerifyCode(code);
1929
+ }
1930
+ }
1931
+ ),
1932
+ isVerifyingCode && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: "Verifying..." })
1933
+ ] }) }),
1934
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full text-sm text-center text-neutral-600 dark:text-neutral-400", children: [
1935
+ "Didn't receive the email?",
1936
+ " ",
1937
+ /* @__PURE__ */ jsxRuntime.jsx(
1938
+ "button",
1939
+ {
1940
+ onClick: () => {
1941
+ void handleResendCode();
1942
+ },
1943
+ disabled: resendDisabled || isSendingCode,
1944
+ className: "text-black dark:text-white font-medium transition-colors disabled:cursor-not-allowed cursor-pointer hover:underline disabled:no-underline disabled:opacity-50",
1945
+ children: isSendingCode ? "Sending..." : resendDisabled ? `Retry in (${resendCountdown}s)` : "Click to resend"
1946
+ }
1947
+ )
1948
+ ] })
1949
+ ] }),
1950
+ /* @__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" }) })
1951
+ ]
1952
+ }
1953
+ );
1954
+ }
1955
+ return /* @__PURE__ */ jsxRuntime.jsx(
1956
+ ResetPassword,
1957
+ {
1958
+ token: resetToken,
1959
+ backToSignInUrl,
1960
+ onSuccess: handlePasswordResetSuccess,
1961
+ onError,
1962
+ appearance: uiProps.appearance
1963
+ }
1964
+ );
1965
+ }
1966
+ function VerifyEmailStatus({
1967
+ status,
1968
+ error,
1969
+ appearance = {},
1970
+ verifyingTitle = "Verifying your email...",
1971
+ successTitle = "Email Verified!",
1972
+ successMessage = "Your email has been verified successfully. You can close this page and return to your app.",
1973
+ errorTitle = "Verification Failed"
1974
+ }) {
1975
+ if (status === "verifying") {
1976
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthContainer, { appearance, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex flex-col items-center justify-center gap-6", children: [
1977
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white", children: verifyingTitle }),
1978
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-black dark:border-white" })
1979
+ ] }) });
1980
+ }
1981
+ if (status === "error") {
1982
+ 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: [
1983
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold text-black dark:text-white", children: errorTitle }),
1984
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed", children: [
1985
+ error || "The verification link is invalid or has expired.",
1986
+ " Please try again or contact support if the problem persists. You can close this page and return to your app."
1987
+ ] })
1988
+ ] }) }) });
1989
+ }
1990
+ 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: [
1991
+ /* @__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" }) }) }),
1992
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white text-center", children: successTitle }),
1993
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 text-center", children: successMessage })
1994
+ ] }) }) });
1995
+ }
1996
+ function VerifyEmail({
1997
+ token,
1998
+ onSuccess,
1999
+ onError,
2000
+ ...uiProps
2001
+ }) {
2002
+ const { verifyEmail } = useInsforge();
2003
+ const [status, setStatus] = react.useState("verifying");
2004
+ const [error, setError] = react.useState("");
2005
+ react.useEffect(() => {
2006
+ const verifyEmailFn = async () => {
2007
+ if (!token) {
2008
+ const errorMessage = "Invalid verification link. Missing required token.";
2009
+ setError(errorMessage);
2010
+ setStatus("error");
2011
+ if (onError) {
2012
+ onError(new Error(errorMessage));
2013
+ }
2014
+ return;
2015
+ }
2016
+ try {
2017
+ const result = await verifyEmail(token);
2018
+ if (!result?.accessToken) {
2019
+ const errorMessage = result ? "Verification succeeded but no access token received" : "Email verification failed";
2020
+ setError(errorMessage);
2021
+ setStatus("error");
2022
+ if (onError) {
2023
+ onError(new Error(errorMessage));
2024
+ }
2025
+ return;
2026
+ }
2027
+ setStatus("success");
2028
+ if (onSuccess) {
2029
+ onSuccess({
2030
+ accessToken: result.accessToken,
2031
+ user: result.user
2032
+ });
2033
+ }
2034
+ } catch (err) {
2035
+ const errorMessage = err.message || "Email verification failed";
2036
+ setError(errorMessage);
2037
+ setStatus("error");
2038
+ if (onError) {
2039
+ onError(new Error(errorMessage));
2040
+ }
2041
+ }
2042
+ };
2043
+ void verifyEmailFn();
2044
+ }, [token, verifyEmail, onSuccess, onError]);
2045
+ return /* @__PURE__ */ jsxRuntime.jsx(VerifyEmailStatus, { status, error, ...uiProps });
2046
+ }
2047
+ function UserButton({
2048
+ afterSignOutUrl = "/",
2049
+ mode = "detailed",
2050
+ appearance = {}
2051
+ }) {
2052
+ const { user, signOut } = useInsforge();
2053
+ const [isOpen, setIsOpen] = react.useState(false);
2054
+ const [imageError, setImageError] = react.useState(false);
2055
+ const dropdownRef = react.useRef(null);
2056
+ react.useEffect(() => {
2057
+ setImageError(false);
2058
+ const avatarUrl = user?.avatarUrl;
2059
+ if (!avatarUrl) return;
2060
+ const checkImageUrl = async () => {
2061
+ try {
2062
+ const response = await fetch(avatarUrl, {
2063
+ method: "HEAD",
2064
+ cache: "no-cache"
2065
+ });
2066
+ if (!response.ok) {
2067
+ setImageError(true);
2068
+ }
2069
+ } catch (error) {
2070
+ setImageError(true);
2071
+ }
2072
+ };
2073
+ checkImageUrl();
2074
+ }, [user?.avatarUrl]);
2075
+ react.useEffect(() => {
2076
+ function handleClickOutside(event) {
2077
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
2078
+ setIsOpen(false);
2079
+ }
2080
+ }
2081
+ if (isOpen) {
2082
+ document.addEventListener("mousedown", handleClickOutside);
2083
+ }
2084
+ return () => {
2085
+ document.removeEventListener("mousedown", handleClickOutside);
2086
+ };
2087
+ }, [isOpen]);
2088
+ async function handleSignOut() {
2089
+ await signOut();
2090
+ setIsOpen(false);
2091
+ window.location.href = afterSignOutUrl;
2092
+ }
2093
+ if (!user) return null;
2094
+ const initials = user.name ? user.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
2095
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2096
+ "div",
2097
+ {
2098
+ className: cn("relative inline-block", appearance.containerClassName),
2099
+ ref: dropdownRef,
2100
+ children: [
2101
+ /* @__PURE__ */ jsxRuntime.jsxs(
2102
+ "button",
2103
+ {
2104
+ className: cn(
2105
+ "p-1 bg-transparent border-0 rounded-full cursor-pointer transition-all duration-200",
2106
+ "flex items-center justify-center gap-2",
2107
+ "hover:bg-black/5",
2108
+ mode === "detailed" && "rounded-lg p-2",
2109
+ appearance.buttonClassName
2110
+ ),
2111
+ onClick: () => setIsOpen(!isOpen),
2112
+ "aria-expanded": isOpen,
2113
+ "aria-haspopup": "true",
2114
+ children: [
2115
+ /* @__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(
2116
+ "img",
2117
+ {
2118
+ src: user.avatarUrl,
2119
+ alt: user.email,
2120
+ onError: () => setImageError(true),
2121
+ className: "rounded-full object-cover w-full h-full"
2122
+ }
2123
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white font-semibold text-sm", children: initials }) }),
2124
+ mode === "detailed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-0.5", children: [
2125
+ user.name && /* @__PURE__ */ jsxRuntime.jsx(
2126
+ "div",
2127
+ {
2128
+ className: cn(
2129
+ "text-sm font-semibold text-gray-900 leading-5 text-left",
2130
+ appearance.nameClassName
2131
+ ),
2132
+ children: user.name
2133
+ }
2134
+ ),
2135
+ /* @__PURE__ */ jsxRuntime.jsx(
2136
+ "div",
2137
+ {
2138
+ className: cn(
2139
+ "text-xs text-gray-500 leading-4 text-left",
2140
+ appearance.emailClassName
2141
+ ),
2142
+ children: user.email
2143
+ }
2144
+ )
2145
+ ] })
2146
+ ]
2147
+ }
2148
+ ),
2149
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx(
2150
+ "div",
2151
+ {
2152
+ className: cn(
2153
+ "absolute top-full right-0 mt-2 min-w-40",
2154
+ "bg-white border border-gray-200 rounded-lg",
2155
+ "shadow-lg z-50 overflow-hidden p-1",
2156
+ appearance.dropdownClassName
2157
+ ),
2158
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2159
+ "button",
2160
+ {
2161
+ onClick: handleSignOut,
2162
+ className: "flex items-center justify-start gap-2 w-full px-3 py-2 text-sm font-normal text-red-600 bg-transparent border-0 rounded-md cursor-pointer transition-colors hover:bg-red-50 text-left",
2163
+ children: [
2164
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "w-5 h-5" }),
2165
+ "Sign out"
2166
+ ]
2167
+ }
2168
+ )
2169
+ }
2170
+ )
2171
+ ]
2172
+ }
2173
+ );
2174
+ }
2175
+ function Protect({
2176
+ children,
2177
+ fallback,
2178
+ redirectTo = "/sign-in",
2179
+ condition,
2180
+ onRedirect
2181
+ }) {
2182
+ const { isSignedIn, isLoaded, user } = useInsforge();
2183
+ react.useEffect(() => {
2184
+ if (isLoaded && !isSignedIn) {
2185
+ if (onRedirect) {
2186
+ onRedirect(redirectTo);
2187
+ } else {
2188
+ window.location.href = redirectTo;
2189
+ }
2190
+ } else if (isLoaded && isSignedIn && condition && user) {
2191
+ if (!condition(user)) {
2192
+ if (onRedirect) {
2193
+ onRedirect(redirectTo);
2194
+ } else {
2195
+ window.location.href = redirectTo;
2196
+ }
2197
+ }
2198
+ }
2199
+ }, [isLoaded, isSignedIn, redirectTo, condition, user, onRedirect]);
2200
+ if (!isLoaded) {
2201
+ return fallback || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "insforge-loading", children: "Loading..." });
2202
+ }
2203
+ if (!isSignedIn) {
2204
+ return fallback || null;
2205
+ }
2206
+ if (condition && user && !condition(user)) {
2207
+ return fallback || null;
2208
+ }
2209
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
2210
+ }
2211
+ function SignedIn({ children }) {
2212
+ const { isSignedIn, isLoaded } = useInsforge();
2213
+ if (!isLoaded) return null;
2214
+ if (!isSignedIn) return null;
2215
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
2216
+ }
2217
+ function SignedOut({ children }) {
2218
+ const { isSignedIn, isLoaded } = useInsforge();
2219
+ if (!isLoaded) return null;
2220
+ if (isSignedIn) return null;
2221
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
2222
+ }
2223
+ function InsforgeCallback({
2224
+ redirectTo = "/",
2225
+ onSuccess,
2226
+ onError,
2227
+ loadingComponent,
2228
+ onRedirect
2229
+ }) {
2230
+ const isProcessingRef = react.useRef(false);
2231
+ const { isLoaded, isSignedIn } = useInsforge();
2232
+ react.useEffect(() => {
2233
+ if (!isLoaded) return;
2234
+ if (isProcessingRef.current) return;
2235
+ isProcessingRef.current = true;
2236
+ const processCallback = async () => {
2237
+ const searchParams = new URLSearchParams(window.location.search);
2238
+ const error = searchParams.get("error");
2239
+ if (error) {
2240
+ if (onError) {
2241
+ onError(error);
2242
+ } else {
2243
+ const errorUrl = "/?error=" + encodeURIComponent(error);
2244
+ if (onRedirect) {
2245
+ onRedirect(errorUrl);
2246
+ } else {
2247
+ window.location.href = errorUrl;
2248
+ }
2249
+ }
2250
+ return;
2251
+ }
2252
+ if (!isSignedIn) {
2253
+ const errorMsg = "authentication_failed";
2254
+ if (onError) {
2255
+ onError(errorMsg);
2256
+ } else {
2257
+ const errorUrl = "/?error=" + encodeURIComponent(errorMsg);
2258
+ if (onRedirect) {
2259
+ onRedirect(errorUrl);
2260
+ } else {
2261
+ window.location.href = errorUrl;
2262
+ }
2263
+ }
2264
+ return;
2265
+ }
2266
+ window.history.replaceState({}, "", window.location.pathname);
2267
+ if (onSuccess) {
2268
+ onSuccess();
2269
+ }
2270
+ if (onRedirect) {
2271
+ onRedirect(redirectTo);
2272
+ } else {
2273
+ window.location.href = redirectTo;
2274
+ }
2275
+ };
2276
+ processCallback();
2277
+ }, [isLoaded, isSignedIn, redirectTo, onSuccess, onError, onRedirect]);
2278
+ const defaultLoading = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2279
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-semibold mb-4", children: "Completing authentication..." }),
2280
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto" })
2281
+ ] }) });
2282
+ return loadingComponent || defaultLoading;
1709
2283
  }
1710
2284
 
1711
2285
  exports.AuthBranding = AuthBranding;
1712
2286
  exports.AuthContainer = AuthContainer;
1713
2287
  exports.AuthDivider = AuthDivider;
2288
+ exports.AuthEmailVerificationStep = AuthEmailVerificationStep;
1714
2289
  exports.AuthErrorBanner = AuthErrorBanner;
1715
2290
  exports.AuthFormField = AuthFormField;
1716
2291
  exports.AuthHeader = AuthHeader;
@@ -1721,9 +2296,11 @@ exports.AuthPasswordField = AuthPasswordField;
1721
2296
  exports.AuthPasswordStrengthIndicator = AuthPasswordStrengthIndicator;
1722
2297
  exports.AuthSubmitButton = AuthSubmitButton;
1723
2298
  exports.AuthVerificationCodeInput = AuthVerificationCodeInput;
2299
+ exports.ForgotPassword = ForgotPassword;
1724
2300
  exports.ForgotPasswordForm = ForgotPasswordForm;
1725
2301
  exports.InsforgeCallback = InsforgeCallback;
1726
2302
  exports.Protect = Protect;
2303
+ exports.ResetPassword = ResetPassword;
1727
2304
  exports.ResetPasswordForm = ResetPasswordForm;
1728
2305
  exports.SignIn = SignIn;
1729
2306
  exports.SignInForm = SignInForm;
@@ -1732,6 +2309,7 @@ exports.SignUpForm = SignUpForm;
1732
2309
  exports.SignedIn = SignedIn;
1733
2310
  exports.SignedOut = SignedOut;
1734
2311
  exports.UserButton = UserButton;
2312
+ exports.VerifyEmail = VerifyEmail;
1735
2313
  exports.VerifyEmailStatus = VerifyEmailStatus;
1736
2314
  exports.validatePasswordStrength = validatePasswordStrength;
1737
2315
  //# sourceMappingURL=components.js.map