@braintwopoint0/playback-commons 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/index.d.ts +62 -1
- package/dist/ui/index.js +752 -0
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/index.js
CHANGED
|
@@ -2866,6 +2866,754 @@ function NewsletterForm2({
|
|
|
2866
2866
|
}
|
|
2867
2867
|
);
|
|
2868
2868
|
}
|
|
2869
|
+
|
|
2870
|
+
// src/ui/site-footer.tsx
|
|
2871
|
+
import Link3 from "next/link";
|
|
2872
|
+
import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2873
|
+
var INK_MUTED = "rgba(214,213,201,0.64)";
|
|
2874
|
+
var INK_SUBTLE = "rgba(214,213,201,0.44)";
|
|
2875
|
+
var LINE = "rgba(214,213,201,0.08)";
|
|
2876
|
+
function FooterLinkItem2({ link }) {
|
|
2877
|
+
const classes = "text-[14px] text-[rgba(214,213,201,0.64)] hover:text-[var(--timberwolf)] transition-colors rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--timberwolf)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--night)]";
|
|
2878
|
+
if (link.external) {
|
|
2879
|
+
return /* @__PURE__ */ jsx38(
|
|
2880
|
+
"a",
|
|
2881
|
+
{
|
|
2882
|
+
href: link.href,
|
|
2883
|
+
target: "_blank",
|
|
2884
|
+
rel: "noopener noreferrer",
|
|
2885
|
+
className: classes,
|
|
2886
|
+
children: link.label
|
|
2887
|
+
}
|
|
2888
|
+
);
|
|
2889
|
+
}
|
|
2890
|
+
return /* @__PURE__ */ jsx38(Link3, { href: link.href, className: classes, children: link.label });
|
|
2891
|
+
}
|
|
2892
|
+
function SiteFooter({
|
|
2893
|
+
columns,
|
|
2894
|
+
siteName = "PLAYBACK",
|
|
2895
|
+
newsletterEndpoint = "/api/newsletter/subscribe",
|
|
2896
|
+
newsletterSource = "footer",
|
|
2897
|
+
newsletterLabel = "Newsletter",
|
|
2898
|
+
newsletterTitle = "Updates from the Network.",
|
|
2899
|
+
newsletterSubtitle = "Only when relevant. No spam."
|
|
2900
|
+
}) {
|
|
2901
|
+
return /* @__PURE__ */ jsxs22(
|
|
2902
|
+
"footer",
|
|
2903
|
+
{
|
|
2904
|
+
id: "footer",
|
|
2905
|
+
className: "mt-24 border-t bg-[var(--night)]",
|
|
2906
|
+
style: { borderTopColor: LINE },
|
|
2907
|
+
"aria-labelledby": "site-footer-heading",
|
|
2908
|
+
children: [
|
|
2909
|
+
/* @__PURE__ */ jsxs22("h2", { id: "site-footer-heading", className: "sr-only", children: [
|
|
2910
|
+
siteName,
|
|
2911
|
+
" site footer"
|
|
2912
|
+
] }),
|
|
2913
|
+
/* @__PURE__ */ jsxs22("div", { className: "mx-auto max-w-[1400px] px-6 sm:px-10", children: [
|
|
2914
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-6 pt-16 pb-10 md:flex-row md:items-start md:justify-between md:gap-10 md:pt-20", children: [
|
|
2915
|
+
/* @__PURE__ */ jsxs22("div", { className: "max-w-[36ch]", children: [
|
|
2916
|
+
/* @__PURE__ */ jsx38(
|
|
2917
|
+
"p",
|
|
2918
|
+
{
|
|
2919
|
+
className: "text-[11px] uppercase tracking-[0.22em] font-semibold",
|
|
2920
|
+
style: { color: INK_SUBTLE },
|
|
2921
|
+
children: newsletterLabel
|
|
2922
|
+
}
|
|
2923
|
+
),
|
|
2924
|
+
/* @__PURE__ */ jsx38("p", { className: "mt-3 text-[17px] md:text-[19px] leading-[1.35] tracking-[-0.01em] text-[var(--timberwolf)]", children: newsletterTitle }),
|
|
2925
|
+
/* @__PURE__ */ jsx38(
|
|
2926
|
+
"p",
|
|
2927
|
+
{
|
|
2928
|
+
className: "mt-2 text-[13px] leading-[1.5]",
|
|
2929
|
+
style: { color: INK_MUTED },
|
|
2930
|
+
children: newsletterSubtitle
|
|
2931
|
+
}
|
|
2932
|
+
)
|
|
2933
|
+
] }),
|
|
2934
|
+
/* @__PURE__ */ jsx38("div", { className: "w-full md:max-w-md md:pt-1", children: /* @__PURE__ */ jsx38(
|
|
2935
|
+
NewsletterForm2,
|
|
2936
|
+
{
|
|
2937
|
+
endpoint: newsletterEndpoint,
|
|
2938
|
+
source: newsletterSource
|
|
2939
|
+
}
|
|
2940
|
+
) })
|
|
2941
|
+
] }),
|
|
2942
|
+
/* @__PURE__ */ jsx38("div", { className: "border-t py-14", style: { borderTopColor: LINE }, children: /* @__PURE__ */ jsx38("div", { className: "grid grid-cols-2 gap-x-6 gap-y-10 md:grid-cols-4", children: columns.map((col) => /* @__PURE__ */ jsxs22("nav", { "aria-label": col.title, children: [
|
|
2943
|
+
/* @__PURE__ */ jsx38(
|
|
2944
|
+
"p",
|
|
2945
|
+
{
|
|
2946
|
+
className: "text-[12px] uppercase tracking-[0.14em] mb-4",
|
|
2947
|
+
style: { color: INK_SUBTLE },
|
|
2948
|
+
children: col.title
|
|
2949
|
+
}
|
|
2950
|
+
),
|
|
2951
|
+
/* @__PURE__ */ jsx38("ul", { className: "flex flex-col gap-3", children: col.links.map((link) => /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsx38(FooterLinkItem2, { link }) }, `${col.title}-${link.label}`)) })
|
|
2952
|
+
] }, col.title)) }) }),
|
|
2953
|
+
/* @__PURE__ */ jsx38(FooterCreditsBar, {})
|
|
2954
|
+
] })
|
|
2955
|
+
]
|
|
2956
|
+
}
|
|
2957
|
+
);
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
// src/ui/sign-in-form.tsx
|
|
2961
|
+
import * as React28 from "react";
|
|
2962
|
+
import Link4 from "next/link";
|
|
2963
|
+
import {
|
|
2964
|
+
AlertCircle,
|
|
2965
|
+
Eye,
|
|
2966
|
+
EyeOff,
|
|
2967
|
+
Loader2,
|
|
2968
|
+
Lock,
|
|
2969
|
+
Mail
|
|
2970
|
+
} from "lucide-react";
|
|
2971
|
+
|
|
2972
|
+
// src/auth/context.tsx
|
|
2973
|
+
import {
|
|
2974
|
+
createContext as createContext2,
|
|
2975
|
+
useContext as useContext2,
|
|
2976
|
+
useEffect as useEffect6,
|
|
2977
|
+
useState as useState14,
|
|
2978
|
+
useMemo as useMemo2,
|
|
2979
|
+
useCallback as useCallback2,
|
|
2980
|
+
useRef as useRef6
|
|
2981
|
+
} from "react";
|
|
2982
|
+
import { useRouter } from "next/navigation";
|
|
2983
|
+
|
|
2984
|
+
// src/supabase/client.ts
|
|
2985
|
+
import { createBrowserClient } from "@supabase/ssr";
|
|
2986
|
+
function createClient() {
|
|
2987
|
+
const cookieDomain = process.env.NEXT_PUBLIC_COOKIE_DOMAIN;
|
|
2988
|
+
return createBrowserClient(
|
|
2989
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
2990
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
2991
|
+
{
|
|
2992
|
+
...cookieDomain && {
|
|
2993
|
+
cookieOptions: { domain: cookieDomain }
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
// src/auth/context.tsx
|
|
3000
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
3001
|
+
var AuthContext = createContext2(void 0);
|
|
3002
|
+
var CACHE_TIMEOUT = 5 * 60 * 1e3;
|
|
3003
|
+
function useAuth() {
|
|
3004
|
+
const context = useContext2(AuthContext);
|
|
3005
|
+
if (context === void 0) {
|
|
3006
|
+
throw new Error("useAuth must be used within an AuthProvider");
|
|
3007
|
+
}
|
|
3008
|
+
return context;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
// src/auth/shared.ts
|
|
3012
|
+
function validateEmail(email) {
|
|
3013
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3014
|
+
return emailRegex.test(email);
|
|
3015
|
+
}
|
|
3016
|
+
function validatePassword(password) {
|
|
3017
|
+
const errors = [];
|
|
3018
|
+
if (password.length < 8) {
|
|
3019
|
+
errors.push("Password must be at least 8 characters long");
|
|
3020
|
+
}
|
|
3021
|
+
if (!/(?=.*[a-z])/.test(password)) {
|
|
3022
|
+
errors.push("Password must contain at least one lowercase letter");
|
|
3023
|
+
}
|
|
3024
|
+
if (!/(?=.*[A-Z])/.test(password)) {
|
|
3025
|
+
errors.push("Password must contain at least one uppercase letter");
|
|
3026
|
+
}
|
|
3027
|
+
if (!/(?=.*\d)/.test(password)) {
|
|
3028
|
+
errors.push("Password must contain at least one number");
|
|
3029
|
+
}
|
|
3030
|
+
return {
|
|
3031
|
+
isValid: errors.length === 0,
|
|
3032
|
+
errors
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
function getAuthErrorMessage(error) {
|
|
3036
|
+
if (!error) return "An unknown error occurred";
|
|
3037
|
+
const message = error.message || error.error_description || error.toString();
|
|
3038
|
+
const errorMappings = {
|
|
3039
|
+
"Invalid login credentials": "Invalid email or password. Please check your credentials and try again.",
|
|
3040
|
+
"Email not confirmed": "Please check your email and click the confirmation link before signing in.",
|
|
3041
|
+
"User already registered": "An account with this email address already exists. Please sign in instead.",
|
|
3042
|
+
"Password should be at least 6 characters": "Password must be at least 6 characters long.",
|
|
3043
|
+
"Signup requires a valid password": "Please provide a valid password.",
|
|
3044
|
+
"Invalid email": "Please provide a valid email address.",
|
|
3045
|
+
"Email rate limit exceeded": "Too many emails sent. Please wait before requesting another.",
|
|
3046
|
+
'duplicate key value violates unique constraint "profiles_username_key"': "This username is already taken. Please choose a different username."
|
|
3047
|
+
};
|
|
3048
|
+
return errorMappings[message] || message;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
// src/ui/sign-in-form.tsx
|
|
3052
|
+
import { Fragment as Fragment6, jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3053
|
+
var COOLDOWN_THRESHOLD = 3;
|
|
3054
|
+
var COOLDOWN_DURATION_MS = 3e4;
|
|
3055
|
+
function SignInForm({
|
|
3056
|
+
onSuccess,
|
|
3057
|
+
forgotPasswordHref,
|
|
3058
|
+
signUpHref,
|
|
3059
|
+
initialError,
|
|
3060
|
+
emailPlaceholder = "you@example.com",
|
|
3061
|
+
title = "Welcome back",
|
|
3062
|
+
subtitle = "Sign in to access your account"
|
|
3063
|
+
}) {
|
|
3064
|
+
const [email, setEmail] = React28.useState("");
|
|
3065
|
+
const [password, setPassword] = React28.useState("");
|
|
3066
|
+
const [showPassword, setShowPassword] = React28.useState(false);
|
|
3067
|
+
const [error, setError] = React28.useState(initialError ?? "");
|
|
3068
|
+
const [loading, setLoading] = React28.useState(false);
|
|
3069
|
+
const [consecutiveErrors, setConsecutiveErrors] = React28.useState(0);
|
|
3070
|
+
const [cooldownUntil, setCooldownUntil] = React28.useState(null);
|
|
3071
|
+
const { signIn } = useAuth();
|
|
3072
|
+
React28.useEffect(() => {
|
|
3073
|
+
if (initialError) setError(initialError);
|
|
3074
|
+
}, [initialError]);
|
|
3075
|
+
const handleSubmit = async (e) => {
|
|
3076
|
+
e.preventDefault();
|
|
3077
|
+
setError("");
|
|
3078
|
+
if (cooldownUntil && Date.now() < cooldownUntil) {
|
|
3079
|
+
const secondsLeft = Math.ceil((cooldownUntil - Date.now()) / 1e3);
|
|
3080
|
+
setError(`Too many attempts. Please wait ${secondsLeft} seconds.`);
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
if (!email || !password) {
|
|
3084
|
+
setError("Please fill in all fields");
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
3087
|
+
if (!validateEmail(email)) {
|
|
3088
|
+
setError("Please enter a valid email address");
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
setLoading(true);
|
|
3092
|
+
try {
|
|
3093
|
+
const { error: signInError } = await signIn(email, password);
|
|
3094
|
+
if (signInError) {
|
|
3095
|
+
setError(getAuthErrorMessage(signInError));
|
|
3096
|
+
const newCount = consecutiveErrors + 1;
|
|
3097
|
+
setConsecutiveErrors(newCount);
|
|
3098
|
+
if (newCount >= COOLDOWN_THRESHOLD) {
|
|
3099
|
+
setCooldownUntil(Date.now() + COOLDOWN_DURATION_MS);
|
|
3100
|
+
setConsecutiveErrors(0);
|
|
3101
|
+
}
|
|
3102
|
+
} else {
|
|
3103
|
+
setConsecutiveErrors(0);
|
|
3104
|
+
setCooldownUntil(null);
|
|
3105
|
+
onSuccess?.(email);
|
|
3106
|
+
}
|
|
3107
|
+
} catch {
|
|
3108
|
+
setError("An unexpected error occurred. Please try again.");
|
|
3109
|
+
} finally {
|
|
3110
|
+
setLoading(false);
|
|
3111
|
+
}
|
|
3112
|
+
};
|
|
3113
|
+
return /* @__PURE__ */ jsxs23("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
|
|
3114
|
+
/* @__PURE__ */ jsxs23("div", { className: "text-center", children: [
|
|
3115
|
+
/* @__PURE__ */ jsx40("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
|
|
3116
|
+
/* @__PURE__ */ jsx40("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3117
|
+
] }),
|
|
3118
|
+
/* @__PURE__ */ jsxs23("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3119
|
+
error && /* @__PURE__ */ jsx40(
|
|
3120
|
+
"div",
|
|
3121
|
+
{
|
|
3122
|
+
id: "signin-error",
|
|
3123
|
+
role: "alert",
|
|
3124
|
+
"aria-live": "polite",
|
|
3125
|
+
className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
|
|
3126
|
+
children: /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
|
|
3127
|
+
/* @__PURE__ */ jsx40(AlertCircle, { className: "h-4 w-4 flex-shrink-0" }),
|
|
3128
|
+
/* @__PURE__ */ jsx40("span", { children: error })
|
|
3129
|
+
] })
|
|
3130
|
+
}
|
|
3131
|
+
),
|
|
3132
|
+
/* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
|
|
3133
|
+
/* @__PURE__ */ jsx40(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
|
|
3134
|
+
/* @__PURE__ */ jsxs23("div", { className: "relative", children: [
|
|
3135
|
+
/* @__PURE__ */ jsx40(
|
|
3136
|
+
Input,
|
|
3137
|
+
{
|
|
3138
|
+
id: "email",
|
|
3139
|
+
type: "email",
|
|
3140
|
+
placeholder: emailPlaceholder,
|
|
3141
|
+
value: email,
|
|
3142
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3143
|
+
className: "h-11 pl-10",
|
|
3144
|
+
disabled: loading,
|
|
3145
|
+
autoComplete: "email",
|
|
3146
|
+
"aria-invalid": !!error,
|
|
3147
|
+
"aria-describedby": error ? "signin-error" : void 0
|
|
3148
|
+
}
|
|
3149
|
+
),
|
|
3150
|
+
/* @__PURE__ */ jsx40(Mail, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
|
|
3151
|
+
] })
|
|
3152
|
+
] }),
|
|
3153
|
+
/* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
|
|
3154
|
+
/* @__PURE__ */ jsx40(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "Password" }),
|
|
3155
|
+
/* @__PURE__ */ jsxs23("div", { className: "relative", children: [
|
|
3156
|
+
/* @__PURE__ */ jsx40(
|
|
3157
|
+
Input,
|
|
3158
|
+
{
|
|
3159
|
+
id: "password",
|
|
3160
|
+
type: showPassword ? "text" : "password",
|
|
3161
|
+
placeholder: "Enter your password",
|
|
3162
|
+
value: password,
|
|
3163
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3164
|
+
className: "h-11 pl-10 pr-10",
|
|
3165
|
+
disabled: loading,
|
|
3166
|
+
autoComplete: "current-password",
|
|
3167
|
+
"aria-invalid": !!error,
|
|
3168
|
+
"aria-describedby": error ? "signin-error" : void 0
|
|
3169
|
+
}
|
|
3170
|
+
),
|
|
3171
|
+
/* @__PURE__ */ jsx40(Lock, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
|
|
3172
|
+
/* @__PURE__ */ jsx40(
|
|
3173
|
+
"button",
|
|
3174
|
+
{
|
|
3175
|
+
type: "button",
|
|
3176
|
+
onClick: () => setShowPassword((prev) => !prev),
|
|
3177
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[var(--timberwolf)]",
|
|
3178
|
+
disabled: loading,
|
|
3179
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
3180
|
+
"aria-pressed": showPassword,
|
|
3181
|
+
children: showPassword ? /* @__PURE__ */ jsx40(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx40(Eye, { className: "h-4 w-4" })
|
|
3182
|
+
}
|
|
3183
|
+
)
|
|
3184
|
+
] })
|
|
3185
|
+
] }),
|
|
3186
|
+
forgotPasswordHref ? /* @__PURE__ */ jsx40("div", { className: "flex justify-end -mt-1", children: /* @__PURE__ */ jsx40(
|
|
3187
|
+
Link4,
|
|
3188
|
+
{
|
|
3189
|
+
href: forgotPasswordHref,
|
|
3190
|
+
className: "text-xs text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
|
|
3191
|
+
children: "Forgot your password?"
|
|
3192
|
+
}
|
|
3193
|
+
) }) : null,
|
|
3194
|
+
/* @__PURE__ */ jsx40(
|
|
3195
|
+
Button,
|
|
3196
|
+
{
|
|
3197
|
+
type: "submit",
|
|
3198
|
+
className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
|
|
3199
|
+
disabled: loading,
|
|
3200
|
+
children: loading ? /* @__PURE__ */ jsxs23(Fragment6, { children: [
|
|
3201
|
+
/* @__PURE__ */ jsx40(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
3202
|
+
"Signing in..."
|
|
3203
|
+
] }) : "Sign in"
|
|
3204
|
+
}
|
|
3205
|
+
)
|
|
3206
|
+
] }),
|
|
3207
|
+
signUpHref ? /* @__PURE__ */ jsx40("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs23("p", { className: "text-muted-foreground", children: [
|
|
3208
|
+
"Don't have an account?",
|
|
3209
|
+
" ",
|
|
3210
|
+
/* @__PURE__ */ jsx40(
|
|
3211
|
+
Link4,
|
|
3212
|
+
{
|
|
3213
|
+
href: signUpHref,
|
|
3214
|
+
className: "text-[var(--timberwolf)] hover:underline",
|
|
3215
|
+
children: "Sign up"
|
|
3216
|
+
}
|
|
3217
|
+
)
|
|
3218
|
+
] }) }) : null
|
|
3219
|
+
] });
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// src/ui/forgot-password-form.tsx
|
|
3223
|
+
import * as React29 from "react";
|
|
3224
|
+
import Link5 from "next/link";
|
|
3225
|
+
import {
|
|
3226
|
+
AlertCircle as AlertCircle2,
|
|
3227
|
+
ArrowLeft,
|
|
3228
|
+
CheckCircle,
|
|
3229
|
+
Loader2 as Loader22,
|
|
3230
|
+
Mail as Mail2
|
|
3231
|
+
} from "lucide-react";
|
|
3232
|
+
import { Fragment as Fragment7, jsx as jsx41, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3233
|
+
var RESEND_COOLDOWN_MS = 3e4;
|
|
3234
|
+
var COOLDOWN_STORAGE_KEY = "playback:pwreset-cooldown-until";
|
|
3235
|
+
function ForgotPasswordForm({
|
|
3236
|
+
redirectTo,
|
|
3237
|
+
loginHref = "/auth/login",
|
|
3238
|
+
title = "Forgot password?",
|
|
3239
|
+
subtitle = "Enter your email and we'll send you a reset link."
|
|
3240
|
+
}) {
|
|
3241
|
+
const [email, setEmail] = React29.useState("");
|
|
3242
|
+
const [error, setError] = React29.useState("");
|
|
3243
|
+
const [loading, setLoading] = React29.useState(false);
|
|
3244
|
+
const [emailSent, setEmailSent] = React29.useState(false);
|
|
3245
|
+
const [cooldownUntil, setCooldownUntilState] = React29.useState(
|
|
3246
|
+
null
|
|
3247
|
+
);
|
|
3248
|
+
const [now, setNow] = React29.useState(() => Date.now());
|
|
3249
|
+
const [supabase] = React29.useState(() => createClient());
|
|
3250
|
+
const setCooldownUntil = (value) => {
|
|
3251
|
+
setCooldownUntilState(value);
|
|
3252
|
+
if (typeof window === "undefined") return;
|
|
3253
|
+
if (value === null) {
|
|
3254
|
+
window.sessionStorage.removeItem(COOLDOWN_STORAGE_KEY);
|
|
3255
|
+
} else {
|
|
3256
|
+
window.sessionStorage.setItem(COOLDOWN_STORAGE_KEY, String(value));
|
|
3257
|
+
}
|
|
3258
|
+
};
|
|
3259
|
+
React29.useEffect(() => {
|
|
3260
|
+
if (typeof window === "undefined") return;
|
|
3261
|
+
const stored = window.sessionStorage.getItem(COOLDOWN_STORAGE_KEY);
|
|
3262
|
+
if (!stored) return;
|
|
3263
|
+
const parsed = Number(stored);
|
|
3264
|
+
if (!Number.isFinite(parsed) || parsed <= Date.now()) {
|
|
3265
|
+
window.sessionStorage.removeItem(COOLDOWN_STORAGE_KEY);
|
|
3266
|
+
return;
|
|
3267
|
+
}
|
|
3268
|
+
setCooldownUntilState(parsed);
|
|
3269
|
+
}, []);
|
|
3270
|
+
React29.useEffect(() => {
|
|
3271
|
+
if (!cooldownUntil) return;
|
|
3272
|
+
const id = setInterval(() => setNow(Date.now()), 1e3);
|
|
3273
|
+
return () => clearInterval(id);
|
|
3274
|
+
}, [cooldownUntil]);
|
|
3275
|
+
const cooldownSecondsLeft = cooldownUntil && cooldownUntil > now ? Math.ceil((cooldownUntil - now) / 1e3) : 0;
|
|
3276
|
+
const handleSubmit = async (e) => {
|
|
3277
|
+
e.preventDefault();
|
|
3278
|
+
setError("");
|
|
3279
|
+
if (cooldownSecondsLeft > 0) {
|
|
3280
|
+
setError(`Please wait ${cooldownSecondsLeft} seconds before resending.`);
|
|
3281
|
+
return;
|
|
3282
|
+
}
|
|
3283
|
+
if (!email) {
|
|
3284
|
+
setError("Please enter your email address");
|
|
3285
|
+
return;
|
|
3286
|
+
}
|
|
3287
|
+
if (!validateEmail(email)) {
|
|
3288
|
+
setError("Please enter a valid email address");
|
|
3289
|
+
return;
|
|
3290
|
+
}
|
|
3291
|
+
setLoading(true);
|
|
3292
|
+
try {
|
|
3293
|
+
const { error: resetError } = await supabase.auth.resetPasswordForEmail(
|
|
3294
|
+
email,
|
|
3295
|
+
{ redirectTo }
|
|
3296
|
+
);
|
|
3297
|
+
if (resetError) {
|
|
3298
|
+
setError(getAuthErrorMessage(resetError));
|
|
3299
|
+
} else {
|
|
3300
|
+
setEmailSent(true);
|
|
3301
|
+
setCooldownUntil(Date.now() + RESEND_COOLDOWN_MS);
|
|
3302
|
+
}
|
|
3303
|
+
} catch {
|
|
3304
|
+
setError("An unexpected error occurred. Please try again.");
|
|
3305
|
+
} finally {
|
|
3306
|
+
setLoading(false);
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
if (emailSent) {
|
|
3310
|
+
return /* @__PURE__ */ jsxs24("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6 text-center", children: [
|
|
3311
|
+
/* @__PURE__ */ jsx41("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx41("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx41(CheckCircle, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
|
|
3312
|
+
/* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
|
|
3313
|
+
/* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Check your email" }),
|
|
3314
|
+
/* @__PURE__ */ jsxs24("p", { className: "text-sm text-muted-foreground", children: [
|
|
3315
|
+
"We sent a reset link to",
|
|
3316
|
+
" ",
|
|
3317
|
+
/* @__PURE__ */ jsx41("span", { className: "text-[var(--timberwolf)] font-medium", children: email }),
|
|
3318
|
+
". If it's not in your inbox, check spam."
|
|
3319
|
+
] })
|
|
3320
|
+
] }),
|
|
3321
|
+
/* @__PURE__ */ jsxs24("div", { className: "space-y-3 pt-2", children: [
|
|
3322
|
+
/* @__PURE__ */ jsx41(
|
|
3323
|
+
Button,
|
|
3324
|
+
{
|
|
3325
|
+
onClick: () => {
|
|
3326
|
+
setEmailSent(false);
|
|
3327
|
+
setEmail("");
|
|
3328
|
+
setError("");
|
|
3329
|
+
},
|
|
3330
|
+
variant: "outline",
|
|
3331
|
+
className: "w-full h-11",
|
|
3332
|
+
disabled: cooldownSecondsLeft > 0,
|
|
3333
|
+
children: cooldownSecondsLeft > 0 ? `Send another email in ${cooldownSecondsLeft}s` : "Send another email"
|
|
3334
|
+
}
|
|
3335
|
+
),
|
|
3336
|
+
/* @__PURE__ */ jsx41(Link5, { href: loginHref, className: "block", children: /* @__PURE__ */ jsxs24(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: [
|
|
3337
|
+
/* @__PURE__ */ jsx41(ArrowLeft, { className: "h-4 w-4 mr-2" }),
|
|
3338
|
+
"Back to sign in"
|
|
3339
|
+
] }) })
|
|
3340
|
+
] })
|
|
3341
|
+
] });
|
|
3342
|
+
}
|
|
3343
|
+
return /* @__PURE__ */ jsxs24("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
|
|
3344
|
+
/* @__PURE__ */ jsxs24("div", { className: "text-center", children: [
|
|
3345
|
+
/* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
|
|
3346
|
+
/* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3347
|
+
] }),
|
|
3348
|
+
/* @__PURE__ */ jsxs24("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3349
|
+
error && /* @__PURE__ */ jsx41(
|
|
3350
|
+
"div",
|
|
3351
|
+
{
|
|
3352
|
+
id: "forgot-error",
|
|
3353
|
+
role: "alert",
|
|
3354
|
+
"aria-live": "polite",
|
|
3355
|
+
className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
|
|
3356
|
+
children: /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
|
|
3357
|
+
/* @__PURE__ */ jsx41(AlertCircle2, { className: "h-4 w-4 flex-shrink-0" }),
|
|
3358
|
+
/* @__PURE__ */ jsx41("span", { children: error })
|
|
3359
|
+
] })
|
|
3360
|
+
}
|
|
3361
|
+
),
|
|
3362
|
+
/* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
|
|
3363
|
+
/* @__PURE__ */ jsx41(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
|
|
3364
|
+
/* @__PURE__ */ jsxs24("div", { className: "relative", children: [
|
|
3365
|
+
/* @__PURE__ */ jsx41(
|
|
3366
|
+
Input,
|
|
3367
|
+
{
|
|
3368
|
+
id: "email",
|
|
3369
|
+
type: "email",
|
|
3370
|
+
placeholder: "you@example.com",
|
|
3371
|
+
value: email,
|
|
3372
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3373
|
+
className: "h-11 pl-10",
|
|
3374
|
+
disabled: loading,
|
|
3375
|
+
autoComplete: "email",
|
|
3376
|
+
autoFocus: true,
|
|
3377
|
+
"aria-invalid": !!error,
|
|
3378
|
+
"aria-describedby": error ? "forgot-error" : void 0
|
|
3379
|
+
}
|
|
3380
|
+
),
|
|
3381
|
+
/* @__PURE__ */ jsx41(Mail2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
|
|
3382
|
+
] })
|
|
3383
|
+
] }),
|
|
3384
|
+
/* @__PURE__ */ jsx41(
|
|
3385
|
+
Button,
|
|
3386
|
+
{
|
|
3387
|
+
type: "submit",
|
|
3388
|
+
className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
|
|
3389
|
+
disabled: loading || cooldownSecondsLeft > 0,
|
|
3390
|
+
children: loading ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
|
|
3391
|
+
/* @__PURE__ */ jsx41(Loader22, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
3392
|
+
"Sending email..."
|
|
3393
|
+
] }) : cooldownSecondsLeft > 0 ? `Try again in ${cooldownSecondsLeft}s` : "Send reset link"
|
|
3394
|
+
}
|
|
3395
|
+
)
|
|
3396
|
+
] }),
|
|
3397
|
+
/* @__PURE__ */ jsx41("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs24(
|
|
3398
|
+
Link5,
|
|
3399
|
+
{
|
|
3400
|
+
href: loginHref,
|
|
3401
|
+
className: "inline-flex items-center gap-1.5 text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
|
|
3402
|
+
children: [
|
|
3403
|
+
/* @__PURE__ */ jsx41(ArrowLeft, { className: "h-3.5 w-3.5" }),
|
|
3404
|
+
"Back to sign in"
|
|
3405
|
+
]
|
|
3406
|
+
}
|
|
3407
|
+
) })
|
|
3408
|
+
] });
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
// src/ui/reset-password-form.tsx
|
|
3412
|
+
import * as React30 from "react";
|
|
3413
|
+
import Link6 from "next/link";
|
|
3414
|
+
import { useRouter as useRouter2 } from "next/navigation";
|
|
3415
|
+
import {
|
|
3416
|
+
AlertCircle as AlertCircle3,
|
|
3417
|
+
CheckCircle as CheckCircle2,
|
|
3418
|
+
Eye as Eye2,
|
|
3419
|
+
EyeOff as EyeOff2,
|
|
3420
|
+
Loader2 as Loader23,
|
|
3421
|
+
Lock as Lock2
|
|
3422
|
+
} from "lucide-react";
|
|
3423
|
+
import { Fragment as Fragment8, jsx as jsx42, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3424
|
+
function ResetPasswordForm({
|
|
3425
|
+
loginHref = "/auth/login",
|
|
3426
|
+
initialError,
|
|
3427
|
+
redirectDelayMs = 3e3,
|
|
3428
|
+
title = "Reset password",
|
|
3429
|
+
subtitle = "Enter a new password for your account."
|
|
3430
|
+
}) {
|
|
3431
|
+
const [password, setPassword] = React30.useState("");
|
|
3432
|
+
const [confirmPassword, setConfirmPassword] = React30.useState("");
|
|
3433
|
+
const [showPassword, setShowPassword] = React30.useState(false);
|
|
3434
|
+
const [showConfirmPassword, setShowConfirmPassword] = React30.useState(false);
|
|
3435
|
+
const [error, setError] = React30.useState(initialError ?? "");
|
|
3436
|
+
const [loading, setLoading] = React30.useState(false);
|
|
3437
|
+
const [passwordReset, setPasswordReset] = React30.useState(false);
|
|
3438
|
+
const router = useRouter2();
|
|
3439
|
+
const [supabase] = React30.useState(() => createClient());
|
|
3440
|
+
React30.useEffect(() => {
|
|
3441
|
+
if (initialError) setError(initialError);
|
|
3442
|
+
}, [initialError]);
|
|
3443
|
+
React30.useEffect(() => {
|
|
3444
|
+
if (!passwordReset) return;
|
|
3445
|
+
const id = setTimeout(() => {
|
|
3446
|
+
router.push(loginHref);
|
|
3447
|
+
}, redirectDelayMs);
|
|
3448
|
+
return () => clearTimeout(id);
|
|
3449
|
+
}, [passwordReset, router, loginHref, redirectDelayMs]);
|
|
3450
|
+
const handleSubmit = async (e) => {
|
|
3451
|
+
e.preventDefault();
|
|
3452
|
+
setError("");
|
|
3453
|
+
if (!password || !confirmPassword) {
|
|
3454
|
+
setError("Please fill in both fields");
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
const { isValid, errors } = validatePassword(password);
|
|
3458
|
+
if (!isValid) {
|
|
3459
|
+
setError(errors[0]);
|
|
3460
|
+
return;
|
|
3461
|
+
}
|
|
3462
|
+
if (password !== confirmPassword) {
|
|
3463
|
+
setError("Passwords do not match");
|
|
3464
|
+
return;
|
|
3465
|
+
}
|
|
3466
|
+
setLoading(true);
|
|
3467
|
+
try {
|
|
3468
|
+
const { error: updateError } = await supabase.auth.updateUser({
|
|
3469
|
+
password
|
|
3470
|
+
});
|
|
3471
|
+
if (updateError) {
|
|
3472
|
+
setError(getAuthErrorMessage(updateError));
|
|
3473
|
+
} else {
|
|
3474
|
+
try {
|
|
3475
|
+
await supabase.auth.signOut({ scope: "global" });
|
|
3476
|
+
} catch (signOutError) {
|
|
3477
|
+
console.warn(
|
|
3478
|
+
"[ResetPasswordForm] global sign-out failed after successful update",
|
|
3479
|
+
signOutError
|
|
3480
|
+
);
|
|
3481
|
+
}
|
|
3482
|
+
setPasswordReset(true);
|
|
3483
|
+
}
|
|
3484
|
+
} catch {
|
|
3485
|
+
setError("An unexpected error occurred. Please try again.");
|
|
3486
|
+
} finally {
|
|
3487
|
+
setLoading(false);
|
|
3488
|
+
}
|
|
3489
|
+
};
|
|
3490
|
+
if (passwordReset) {
|
|
3491
|
+
return /* @__PURE__ */ jsxs25("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6 text-center", children: [
|
|
3492
|
+
/* @__PURE__ */ jsx42("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx42("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx42(CheckCircle2, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
|
|
3493
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
3494
|
+
/* @__PURE__ */ jsx42("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Password updated" }),
|
|
3495
|
+
/* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: "Redirecting you to sign in\u2026" })
|
|
3496
|
+
] }),
|
|
3497
|
+
/* @__PURE__ */ jsx42(Link6, { href: loginHref, className: "block pt-2", children: /* @__PURE__ */ jsx42(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: "Continue to sign in" }) })
|
|
3498
|
+
] });
|
|
3499
|
+
}
|
|
3500
|
+
return /* @__PURE__ */ jsxs25("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
|
|
3501
|
+
/* @__PURE__ */ jsxs25("div", { className: "text-center", children: [
|
|
3502
|
+
/* @__PURE__ */ jsx42("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
|
|
3503
|
+
/* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3504
|
+
] }),
|
|
3505
|
+
/* @__PURE__ */ jsxs25("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3506
|
+
error && /* @__PURE__ */ jsx42(
|
|
3507
|
+
"div",
|
|
3508
|
+
{
|
|
3509
|
+
id: "reset-error",
|
|
3510
|
+
role: "alert",
|
|
3511
|
+
"aria-live": "polite",
|
|
3512
|
+
className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
|
|
3513
|
+
children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
|
|
3514
|
+
/* @__PURE__ */ jsx42(AlertCircle3, { className: "h-4 w-4 flex-shrink-0" }),
|
|
3515
|
+
/* @__PURE__ */ jsx42("span", { children: error })
|
|
3516
|
+
] })
|
|
3517
|
+
}
|
|
3518
|
+
),
|
|
3519
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
3520
|
+
/* @__PURE__ */ jsx42(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "New password" }),
|
|
3521
|
+
/* @__PURE__ */ jsxs25("div", { className: "relative", children: [
|
|
3522
|
+
/* @__PURE__ */ jsx42(
|
|
3523
|
+
Input,
|
|
3524
|
+
{
|
|
3525
|
+
id: "password",
|
|
3526
|
+
type: showPassword ? "text" : "password",
|
|
3527
|
+
placeholder: "Enter a new password",
|
|
3528
|
+
value: password,
|
|
3529
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3530
|
+
className: "h-11 pl-10 pr-10",
|
|
3531
|
+
disabled: loading,
|
|
3532
|
+
autoComplete: "new-password",
|
|
3533
|
+
autoFocus: true,
|
|
3534
|
+
"aria-invalid": !!error,
|
|
3535
|
+
"aria-describedby": error ? "reset-error" : void 0
|
|
3536
|
+
}
|
|
3537
|
+
),
|
|
3538
|
+
/* @__PURE__ */ jsx42(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
|
|
3539
|
+
/* @__PURE__ */ jsx42(
|
|
3540
|
+
"button",
|
|
3541
|
+
{
|
|
3542
|
+
type: "button",
|
|
3543
|
+
onClick: () => setShowPassword((prev) => !prev),
|
|
3544
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[var(--timberwolf)]",
|
|
3545
|
+
disabled: loading,
|
|
3546
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
3547
|
+
"aria-pressed": showPassword,
|
|
3548
|
+
children: showPassword ? /* @__PURE__ */ jsx42(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx42(Eye2, { className: "h-4 w-4" })
|
|
3549
|
+
}
|
|
3550
|
+
)
|
|
3551
|
+
] })
|
|
3552
|
+
] }),
|
|
3553
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
3554
|
+
/* @__PURE__ */ jsx42(
|
|
3555
|
+
Label,
|
|
3556
|
+
{
|
|
3557
|
+
htmlFor: "confirmPassword",
|
|
3558
|
+
className: "text-[var(--timberwolf)]",
|
|
3559
|
+
children: "Confirm new password"
|
|
3560
|
+
}
|
|
3561
|
+
),
|
|
3562
|
+
/* @__PURE__ */ jsxs25("div", { className: "relative", children: [
|
|
3563
|
+
/* @__PURE__ */ jsx42(
|
|
3564
|
+
Input,
|
|
3565
|
+
{
|
|
3566
|
+
id: "confirmPassword",
|
|
3567
|
+
type: showConfirmPassword ? "text" : "password",
|
|
3568
|
+
placeholder: "Re-enter your new password",
|
|
3569
|
+
value: confirmPassword,
|
|
3570
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
3571
|
+
className: "h-11 pl-10 pr-10",
|
|
3572
|
+
disabled: loading,
|
|
3573
|
+
autoComplete: "new-password",
|
|
3574
|
+
"aria-invalid": !!error,
|
|
3575
|
+
"aria-describedby": error ? "reset-error" : void 0
|
|
3576
|
+
}
|
|
3577
|
+
),
|
|
3578
|
+
/* @__PURE__ */ jsx42(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
|
|
3579
|
+
/* @__PURE__ */ jsx42(
|
|
3580
|
+
"button",
|
|
3581
|
+
{
|
|
3582
|
+
type: "button",
|
|
3583
|
+
onClick: () => setShowConfirmPassword((prev) => !prev),
|
|
3584
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[var(--timberwolf)]",
|
|
3585
|
+
disabled: loading,
|
|
3586
|
+
"aria-label": showConfirmPassword ? "Hide password" : "Show password",
|
|
3587
|
+
"aria-pressed": showConfirmPassword,
|
|
3588
|
+
children: showConfirmPassword ? /* @__PURE__ */ jsx42(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx42(Eye2, { className: "h-4 w-4" })
|
|
3589
|
+
}
|
|
3590
|
+
)
|
|
3591
|
+
] })
|
|
3592
|
+
] }),
|
|
3593
|
+
/* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", children: "At least 8 characters with an uppercase letter, a lowercase letter, and a number." }),
|
|
3594
|
+
/* @__PURE__ */ jsx42(
|
|
3595
|
+
Button,
|
|
3596
|
+
{
|
|
3597
|
+
type: "submit",
|
|
3598
|
+
className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
|
|
3599
|
+
disabled: loading,
|
|
3600
|
+
children: loading ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
|
|
3601
|
+
/* @__PURE__ */ jsx42(Loader23, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
3602
|
+
"Updating password..."
|
|
3603
|
+
] }) : "Update password"
|
|
3604
|
+
}
|
|
3605
|
+
)
|
|
3606
|
+
] }),
|
|
3607
|
+
/* @__PURE__ */ jsx42("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsx42(
|
|
3608
|
+
Link6,
|
|
3609
|
+
{
|
|
3610
|
+
href: loginHref,
|
|
3611
|
+
className: "text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
|
|
3612
|
+
children: "Back to sign in"
|
|
3613
|
+
}
|
|
3614
|
+
) })
|
|
3615
|
+
] });
|
|
3616
|
+
}
|
|
2869
3617
|
export {
|
|
2870
3618
|
AnimatedTooltip,
|
|
2871
3619
|
Avatar,
|
|
@@ -2909,6 +3657,7 @@ export {
|
|
|
2909
3657
|
FlipWords,
|
|
2910
3658
|
Footer,
|
|
2911
3659
|
FooterCreditsBar,
|
|
3660
|
+
ForgotPasswordForm,
|
|
2912
3661
|
HeroHighlight,
|
|
2913
3662
|
Highlight,
|
|
2914
3663
|
HoverCard,
|
|
@@ -2924,6 +3673,7 @@ export {
|
|
|
2924
3673
|
PopoverAnchor,
|
|
2925
3674
|
PopoverContent,
|
|
2926
3675
|
PopoverTrigger,
|
|
3676
|
+
ResetPasswordForm,
|
|
2927
3677
|
SearchBar,
|
|
2928
3678
|
SectionCard,
|
|
2929
3679
|
Select,
|
|
@@ -2947,6 +3697,8 @@ export {
|
|
|
2947
3697
|
SheetPortal,
|
|
2948
3698
|
SheetTitle,
|
|
2949
3699
|
SheetTrigger,
|
|
3700
|
+
SignInForm,
|
|
3701
|
+
SiteFooter,
|
|
2950
3702
|
Skeleton,
|
|
2951
3703
|
StatsGrid,
|
|
2952
3704
|
Switch,
|