@braintwopoint0/playback-commons 0.2.3 → 0.2.5
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 +731 -0
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/index.js
CHANGED
|
@@ -2866,6 +2866,733 @@ 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/shared.ts
|
|
2973
|
+
function validateEmail(email) {
|
|
2974
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2975
|
+
return emailRegex.test(email);
|
|
2976
|
+
}
|
|
2977
|
+
function validatePassword(password) {
|
|
2978
|
+
const errors = [];
|
|
2979
|
+
if (password.length < 8) {
|
|
2980
|
+
errors.push("Password must be at least 8 characters long");
|
|
2981
|
+
}
|
|
2982
|
+
if (!/(?=.*[a-z])/.test(password)) {
|
|
2983
|
+
errors.push("Password must contain at least one lowercase letter");
|
|
2984
|
+
}
|
|
2985
|
+
if (!/(?=.*[A-Z])/.test(password)) {
|
|
2986
|
+
errors.push("Password must contain at least one uppercase letter");
|
|
2987
|
+
}
|
|
2988
|
+
if (!/(?=.*\d)/.test(password)) {
|
|
2989
|
+
errors.push("Password must contain at least one number");
|
|
2990
|
+
}
|
|
2991
|
+
return {
|
|
2992
|
+
isValid: errors.length === 0,
|
|
2993
|
+
errors
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
function getAuthErrorMessage(error) {
|
|
2997
|
+
if (!error) return "An unknown error occurred";
|
|
2998
|
+
const message = error.message || error.error_description || error.toString();
|
|
2999
|
+
const errorMappings = {
|
|
3000
|
+
"Invalid login credentials": "Invalid email or password. Please check your credentials and try again.",
|
|
3001
|
+
"Email not confirmed": "Please check your email and click the confirmation link before signing in.",
|
|
3002
|
+
"User already registered": "An account with this email address already exists. Please sign in instead.",
|
|
3003
|
+
"Password should be at least 6 characters": "Password must be at least 6 characters long.",
|
|
3004
|
+
"Signup requires a valid password": "Please provide a valid password.",
|
|
3005
|
+
"Invalid email": "Please provide a valid email address.",
|
|
3006
|
+
"Email rate limit exceeded": "Too many emails sent. Please wait before requesting another.",
|
|
3007
|
+
'duplicate key value violates unique constraint "profiles_username_key"': "This username is already taken. Please choose a different username."
|
|
3008
|
+
};
|
|
3009
|
+
return errorMappings[message] || message;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
// src/supabase/client.ts
|
|
3013
|
+
import { createBrowserClient } from "@supabase/ssr";
|
|
3014
|
+
function createClient() {
|
|
3015
|
+
const cookieDomain = process.env.NEXT_PUBLIC_COOKIE_DOMAIN;
|
|
3016
|
+
return createBrowserClient(
|
|
3017
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
3018
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
3019
|
+
{
|
|
3020
|
+
...cookieDomain && {
|
|
3021
|
+
cookieOptions: { domain: cookieDomain }
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
// src/ui/sign-in-form.tsx
|
|
3028
|
+
import { Fragment as Fragment6, jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3029
|
+
var COOLDOWN_THRESHOLD = 3;
|
|
3030
|
+
var COOLDOWN_DURATION_MS = 3e4;
|
|
3031
|
+
function SignInForm({
|
|
3032
|
+
onSuccess,
|
|
3033
|
+
forgotPasswordHref,
|
|
3034
|
+
signUpHref,
|
|
3035
|
+
initialError,
|
|
3036
|
+
emailPlaceholder = "you@example.com",
|
|
3037
|
+
title = "Welcome back",
|
|
3038
|
+
subtitle = "Sign in to access your account"
|
|
3039
|
+
}) {
|
|
3040
|
+
const [email, setEmail] = React28.useState("");
|
|
3041
|
+
const [password, setPassword] = React28.useState("");
|
|
3042
|
+
const [showPassword, setShowPassword] = React28.useState(false);
|
|
3043
|
+
const [error, setError] = React28.useState(initialError ?? "");
|
|
3044
|
+
const [loading, setLoading] = React28.useState(false);
|
|
3045
|
+
const [consecutiveErrors, setConsecutiveErrors] = React28.useState(0);
|
|
3046
|
+
const [cooldownUntil, setCooldownUntil] = React28.useState(null);
|
|
3047
|
+
const [supabase] = React28.useState(() => createClient());
|
|
3048
|
+
React28.useEffect(() => {
|
|
3049
|
+
if (initialError) setError(initialError);
|
|
3050
|
+
}, [initialError]);
|
|
3051
|
+
const handleSubmit = async (e) => {
|
|
3052
|
+
e.preventDefault();
|
|
3053
|
+
setError("");
|
|
3054
|
+
if (cooldownUntil && Date.now() < cooldownUntil) {
|
|
3055
|
+
const secondsLeft = Math.ceil((cooldownUntil - Date.now()) / 1e3);
|
|
3056
|
+
setError(`Too many attempts. Please wait ${secondsLeft} seconds.`);
|
|
3057
|
+
return;
|
|
3058
|
+
}
|
|
3059
|
+
if (!email || !password) {
|
|
3060
|
+
setError("Please fill in all fields");
|
|
3061
|
+
return;
|
|
3062
|
+
}
|
|
3063
|
+
if (!validateEmail(email)) {
|
|
3064
|
+
setError("Please enter a valid email address");
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
setLoading(true);
|
|
3068
|
+
try {
|
|
3069
|
+
const { error: signInError } = await supabase.auth.signInWithPassword({
|
|
3070
|
+
email,
|
|
3071
|
+
password
|
|
3072
|
+
});
|
|
3073
|
+
if (signInError) {
|
|
3074
|
+
setError(getAuthErrorMessage(signInError));
|
|
3075
|
+
const newCount = consecutiveErrors + 1;
|
|
3076
|
+
setConsecutiveErrors(newCount);
|
|
3077
|
+
if (newCount >= COOLDOWN_THRESHOLD) {
|
|
3078
|
+
setCooldownUntil(Date.now() + COOLDOWN_DURATION_MS);
|
|
3079
|
+
setConsecutiveErrors(0);
|
|
3080
|
+
}
|
|
3081
|
+
} else {
|
|
3082
|
+
setConsecutiveErrors(0);
|
|
3083
|
+
setCooldownUntil(null);
|
|
3084
|
+
onSuccess?.(email);
|
|
3085
|
+
}
|
|
3086
|
+
} catch {
|
|
3087
|
+
setError("An unexpected error occurred. Please try again.");
|
|
3088
|
+
} finally {
|
|
3089
|
+
setLoading(false);
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3092
|
+
return /* @__PURE__ */ jsxs23("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
|
|
3093
|
+
/* @__PURE__ */ jsxs23("div", { className: "text-center", children: [
|
|
3094
|
+
/* @__PURE__ */ jsx39("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
|
|
3095
|
+
/* @__PURE__ */ jsx39("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3096
|
+
] }),
|
|
3097
|
+
/* @__PURE__ */ jsxs23("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3098
|
+
error && /* @__PURE__ */ jsx39(
|
|
3099
|
+
"div",
|
|
3100
|
+
{
|
|
3101
|
+
id: "signin-error",
|
|
3102
|
+
role: "alert",
|
|
3103
|
+
"aria-live": "polite",
|
|
3104
|
+
className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
|
|
3105
|
+
children: /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
|
|
3106
|
+
/* @__PURE__ */ jsx39(AlertCircle, { className: "h-4 w-4 flex-shrink-0" }),
|
|
3107
|
+
/* @__PURE__ */ jsx39("span", { children: error })
|
|
3108
|
+
] })
|
|
3109
|
+
}
|
|
3110
|
+
),
|
|
3111
|
+
/* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
|
|
3112
|
+
/* @__PURE__ */ jsx39(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
|
|
3113
|
+
/* @__PURE__ */ jsxs23("div", { className: "relative", children: [
|
|
3114
|
+
/* @__PURE__ */ jsx39(
|
|
3115
|
+
Input,
|
|
3116
|
+
{
|
|
3117
|
+
id: "email",
|
|
3118
|
+
type: "email",
|
|
3119
|
+
placeholder: emailPlaceholder,
|
|
3120
|
+
value: email,
|
|
3121
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3122
|
+
className: "h-11 pl-10",
|
|
3123
|
+
disabled: loading,
|
|
3124
|
+
autoComplete: "email",
|
|
3125
|
+
"aria-invalid": !!error,
|
|
3126
|
+
"aria-describedby": error ? "signin-error" : void 0
|
|
3127
|
+
}
|
|
3128
|
+
),
|
|
3129
|
+
/* @__PURE__ */ jsx39(Mail, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
|
|
3130
|
+
] })
|
|
3131
|
+
] }),
|
|
3132
|
+
/* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
|
|
3133
|
+
/* @__PURE__ */ jsx39(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "Password" }),
|
|
3134
|
+
/* @__PURE__ */ jsxs23("div", { className: "relative", children: [
|
|
3135
|
+
/* @__PURE__ */ jsx39(
|
|
3136
|
+
Input,
|
|
3137
|
+
{
|
|
3138
|
+
id: "password",
|
|
3139
|
+
type: showPassword ? "text" : "password",
|
|
3140
|
+
placeholder: "Enter your password",
|
|
3141
|
+
value: password,
|
|
3142
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3143
|
+
className: "h-11 pl-10 pr-10",
|
|
3144
|
+
disabled: loading,
|
|
3145
|
+
autoComplete: "current-password",
|
|
3146
|
+
"aria-invalid": !!error,
|
|
3147
|
+
"aria-describedby": error ? "signin-error" : void 0
|
|
3148
|
+
}
|
|
3149
|
+
),
|
|
3150
|
+
/* @__PURE__ */ jsx39(Lock, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
|
|
3151
|
+
/* @__PURE__ */ jsx39(
|
|
3152
|
+
"button",
|
|
3153
|
+
{
|
|
3154
|
+
type: "button",
|
|
3155
|
+
onClick: () => setShowPassword((prev) => !prev),
|
|
3156
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[var(--timberwolf)]",
|
|
3157
|
+
disabled: loading,
|
|
3158
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
3159
|
+
"aria-pressed": showPassword,
|
|
3160
|
+
children: showPassword ? /* @__PURE__ */ jsx39(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx39(Eye, { className: "h-4 w-4" })
|
|
3161
|
+
}
|
|
3162
|
+
)
|
|
3163
|
+
] })
|
|
3164
|
+
] }),
|
|
3165
|
+
forgotPasswordHref ? /* @__PURE__ */ jsx39("div", { className: "flex justify-end -mt-1", children: /* @__PURE__ */ jsx39(
|
|
3166
|
+
Link4,
|
|
3167
|
+
{
|
|
3168
|
+
href: forgotPasswordHref,
|
|
3169
|
+
className: "text-xs text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
|
|
3170
|
+
children: "Forgot your password?"
|
|
3171
|
+
}
|
|
3172
|
+
) }) : null,
|
|
3173
|
+
/* @__PURE__ */ jsx39(
|
|
3174
|
+
Button,
|
|
3175
|
+
{
|
|
3176
|
+
type: "submit",
|
|
3177
|
+
className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
|
|
3178
|
+
disabled: loading,
|
|
3179
|
+
children: loading ? /* @__PURE__ */ jsxs23(Fragment6, { children: [
|
|
3180
|
+
/* @__PURE__ */ jsx39(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
3181
|
+
"Signing in..."
|
|
3182
|
+
] }) : "Sign in"
|
|
3183
|
+
}
|
|
3184
|
+
)
|
|
3185
|
+
] }),
|
|
3186
|
+
signUpHref ? /* @__PURE__ */ jsx39("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs23("p", { className: "text-muted-foreground", children: [
|
|
3187
|
+
"Don't have an account?",
|
|
3188
|
+
" ",
|
|
3189
|
+
/* @__PURE__ */ jsx39(
|
|
3190
|
+
Link4,
|
|
3191
|
+
{
|
|
3192
|
+
href: signUpHref,
|
|
3193
|
+
className: "text-[var(--timberwolf)] hover:underline",
|
|
3194
|
+
children: "Sign up"
|
|
3195
|
+
}
|
|
3196
|
+
)
|
|
3197
|
+
] }) }) : null
|
|
3198
|
+
] });
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
// src/ui/forgot-password-form.tsx
|
|
3202
|
+
import * as React29 from "react";
|
|
3203
|
+
import Link5 from "next/link";
|
|
3204
|
+
import {
|
|
3205
|
+
AlertCircle as AlertCircle2,
|
|
3206
|
+
ArrowLeft,
|
|
3207
|
+
CheckCircle,
|
|
3208
|
+
Loader2 as Loader22,
|
|
3209
|
+
Mail as Mail2
|
|
3210
|
+
} from "lucide-react";
|
|
3211
|
+
import { Fragment as Fragment7, jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3212
|
+
var RESEND_COOLDOWN_MS = 3e4;
|
|
3213
|
+
var COOLDOWN_STORAGE_KEY = "playback:pwreset-cooldown-until";
|
|
3214
|
+
function ForgotPasswordForm({
|
|
3215
|
+
redirectTo,
|
|
3216
|
+
loginHref = "/auth/login",
|
|
3217
|
+
title = "Forgot password?",
|
|
3218
|
+
subtitle = "Enter your email and we'll send you a reset link."
|
|
3219
|
+
}) {
|
|
3220
|
+
const [email, setEmail] = React29.useState("");
|
|
3221
|
+
const [error, setError] = React29.useState("");
|
|
3222
|
+
const [loading, setLoading] = React29.useState(false);
|
|
3223
|
+
const [emailSent, setEmailSent] = React29.useState(false);
|
|
3224
|
+
const [cooldownUntil, setCooldownUntilState] = React29.useState(
|
|
3225
|
+
null
|
|
3226
|
+
);
|
|
3227
|
+
const [now, setNow] = React29.useState(() => Date.now());
|
|
3228
|
+
const [supabase] = React29.useState(() => createClient());
|
|
3229
|
+
const setCooldownUntil = (value) => {
|
|
3230
|
+
setCooldownUntilState(value);
|
|
3231
|
+
if (typeof window === "undefined") return;
|
|
3232
|
+
if (value === null) {
|
|
3233
|
+
window.sessionStorage.removeItem(COOLDOWN_STORAGE_KEY);
|
|
3234
|
+
} else {
|
|
3235
|
+
window.sessionStorage.setItem(COOLDOWN_STORAGE_KEY, String(value));
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
React29.useEffect(() => {
|
|
3239
|
+
if (typeof window === "undefined") return;
|
|
3240
|
+
const stored = window.sessionStorage.getItem(COOLDOWN_STORAGE_KEY);
|
|
3241
|
+
if (!stored) return;
|
|
3242
|
+
const parsed = Number(stored);
|
|
3243
|
+
if (!Number.isFinite(parsed) || parsed <= Date.now()) {
|
|
3244
|
+
window.sessionStorage.removeItem(COOLDOWN_STORAGE_KEY);
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
setCooldownUntilState(parsed);
|
|
3248
|
+
}, []);
|
|
3249
|
+
React29.useEffect(() => {
|
|
3250
|
+
if (!cooldownUntil) return;
|
|
3251
|
+
const id = setInterval(() => setNow(Date.now()), 1e3);
|
|
3252
|
+
return () => clearInterval(id);
|
|
3253
|
+
}, [cooldownUntil]);
|
|
3254
|
+
const cooldownSecondsLeft = cooldownUntil && cooldownUntil > now ? Math.ceil((cooldownUntil - now) / 1e3) : 0;
|
|
3255
|
+
const handleSubmit = async (e) => {
|
|
3256
|
+
e.preventDefault();
|
|
3257
|
+
setError("");
|
|
3258
|
+
if (cooldownSecondsLeft > 0) {
|
|
3259
|
+
setError(`Please wait ${cooldownSecondsLeft} seconds before resending.`);
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3262
|
+
if (!email) {
|
|
3263
|
+
setError("Please enter your email address");
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
if (!validateEmail(email)) {
|
|
3267
|
+
setError("Please enter a valid email address");
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
setLoading(true);
|
|
3271
|
+
try {
|
|
3272
|
+
const { error: resetError } = await supabase.auth.resetPasswordForEmail(
|
|
3273
|
+
email,
|
|
3274
|
+
{ redirectTo }
|
|
3275
|
+
);
|
|
3276
|
+
if (resetError) {
|
|
3277
|
+
setError(getAuthErrorMessage(resetError));
|
|
3278
|
+
} else {
|
|
3279
|
+
setEmailSent(true);
|
|
3280
|
+
setCooldownUntil(Date.now() + RESEND_COOLDOWN_MS);
|
|
3281
|
+
}
|
|
3282
|
+
} catch {
|
|
3283
|
+
setError("An unexpected error occurred. Please try again.");
|
|
3284
|
+
} finally {
|
|
3285
|
+
setLoading(false);
|
|
3286
|
+
}
|
|
3287
|
+
};
|
|
3288
|
+
if (emailSent) {
|
|
3289
|
+
return /* @__PURE__ */ jsxs24("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6 text-center", children: [
|
|
3290
|
+
/* @__PURE__ */ jsx40("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx40("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx40(CheckCircle, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
|
|
3291
|
+
/* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
|
|
3292
|
+
/* @__PURE__ */ jsx40("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Check your email" }),
|
|
3293
|
+
/* @__PURE__ */ jsxs24("p", { className: "text-sm text-muted-foreground", children: [
|
|
3294
|
+
"We sent a reset link to",
|
|
3295
|
+
" ",
|
|
3296
|
+
/* @__PURE__ */ jsx40("span", { className: "text-[var(--timberwolf)] font-medium", children: email }),
|
|
3297
|
+
". If it's not in your inbox, check spam."
|
|
3298
|
+
] })
|
|
3299
|
+
] }),
|
|
3300
|
+
/* @__PURE__ */ jsxs24("div", { className: "space-y-3 pt-2", children: [
|
|
3301
|
+
/* @__PURE__ */ jsx40(
|
|
3302
|
+
Button,
|
|
3303
|
+
{
|
|
3304
|
+
onClick: () => {
|
|
3305
|
+
setEmailSent(false);
|
|
3306
|
+
setEmail("");
|
|
3307
|
+
setError("");
|
|
3308
|
+
},
|
|
3309
|
+
variant: "outline",
|
|
3310
|
+
className: "w-full h-11",
|
|
3311
|
+
disabled: cooldownSecondsLeft > 0,
|
|
3312
|
+
children: cooldownSecondsLeft > 0 ? `Send another email in ${cooldownSecondsLeft}s` : "Send another email"
|
|
3313
|
+
}
|
|
3314
|
+
),
|
|
3315
|
+
/* @__PURE__ */ jsx40(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: [
|
|
3316
|
+
/* @__PURE__ */ jsx40(ArrowLeft, { className: "h-4 w-4 mr-2" }),
|
|
3317
|
+
"Back to sign in"
|
|
3318
|
+
] }) })
|
|
3319
|
+
] })
|
|
3320
|
+
] });
|
|
3321
|
+
}
|
|
3322
|
+
return /* @__PURE__ */ jsxs24("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
|
|
3323
|
+
/* @__PURE__ */ jsxs24("div", { className: "text-center", children: [
|
|
3324
|
+
/* @__PURE__ */ jsx40("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
|
|
3325
|
+
/* @__PURE__ */ jsx40("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3326
|
+
] }),
|
|
3327
|
+
/* @__PURE__ */ jsxs24("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3328
|
+
error && /* @__PURE__ */ jsx40(
|
|
3329
|
+
"div",
|
|
3330
|
+
{
|
|
3331
|
+
id: "forgot-error",
|
|
3332
|
+
role: "alert",
|
|
3333
|
+
"aria-live": "polite",
|
|
3334
|
+
className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
|
|
3335
|
+
children: /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
|
|
3336
|
+
/* @__PURE__ */ jsx40(AlertCircle2, { className: "h-4 w-4 flex-shrink-0" }),
|
|
3337
|
+
/* @__PURE__ */ jsx40("span", { children: error })
|
|
3338
|
+
] })
|
|
3339
|
+
}
|
|
3340
|
+
),
|
|
3341
|
+
/* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
|
|
3342
|
+
/* @__PURE__ */ jsx40(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
|
|
3343
|
+
/* @__PURE__ */ jsxs24("div", { className: "relative", children: [
|
|
3344
|
+
/* @__PURE__ */ jsx40(
|
|
3345
|
+
Input,
|
|
3346
|
+
{
|
|
3347
|
+
id: "email",
|
|
3348
|
+
type: "email",
|
|
3349
|
+
placeholder: "you@example.com",
|
|
3350
|
+
value: email,
|
|
3351
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3352
|
+
className: "h-11 pl-10",
|
|
3353
|
+
disabled: loading,
|
|
3354
|
+
autoComplete: "email",
|
|
3355
|
+
autoFocus: true,
|
|
3356
|
+
"aria-invalid": !!error,
|
|
3357
|
+
"aria-describedby": error ? "forgot-error" : void 0
|
|
3358
|
+
}
|
|
3359
|
+
),
|
|
3360
|
+
/* @__PURE__ */ jsx40(Mail2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
|
|
3361
|
+
] })
|
|
3362
|
+
] }),
|
|
3363
|
+
/* @__PURE__ */ jsx40(
|
|
3364
|
+
Button,
|
|
3365
|
+
{
|
|
3366
|
+
type: "submit",
|
|
3367
|
+
className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
|
|
3368
|
+
disabled: loading || cooldownSecondsLeft > 0,
|
|
3369
|
+
children: loading ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
|
|
3370
|
+
/* @__PURE__ */ jsx40(Loader22, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
3371
|
+
"Sending email..."
|
|
3372
|
+
] }) : cooldownSecondsLeft > 0 ? `Try again in ${cooldownSecondsLeft}s` : "Send reset link"
|
|
3373
|
+
}
|
|
3374
|
+
)
|
|
3375
|
+
] }),
|
|
3376
|
+
/* @__PURE__ */ jsx40("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs24(
|
|
3377
|
+
Link5,
|
|
3378
|
+
{
|
|
3379
|
+
href: loginHref,
|
|
3380
|
+
className: "inline-flex items-center gap-1.5 text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
|
|
3381
|
+
children: [
|
|
3382
|
+
/* @__PURE__ */ jsx40(ArrowLeft, { className: "h-3.5 w-3.5" }),
|
|
3383
|
+
"Back to sign in"
|
|
3384
|
+
]
|
|
3385
|
+
}
|
|
3386
|
+
) })
|
|
3387
|
+
] });
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
// src/ui/reset-password-form.tsx
|
|
3391
|
+
import * as React30 from "react";
|
|
3392
|
+
import Link6 from "next/link";
|
|
3393
|
+
import { useRouter } from "next/navigation";
|
|
3394
|
+
import {
|
|
3395
|
+
AlertCircle as AlertCircle3,
|
|
3396
|
+
CheckCircle as CheckCircle2,
|
|
3397
|
+
Eye as Eye2,
|
|
3398
|
+
EyeOff as EyeOff2,
|
|
3399
|
+
Loader2 as Loader23,
|
|
3400
|
+
Lock as Lock2
|
|
3401
|
+
} from "lucide-react";
|
|
3402
|
+
import { Fragment as Fragment8, jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3403
|
+
function ResetPasswordForm({
|
|
3404
|
+
loginHref = "/auth/login",
|
|
3405
|
+
initialError,
|
|
3406
|
+
redirectDelayMs = 3e3,
|
|
3407
|
+
title = "Reset password",
|
|
3408
|
+
subtitle = "Enter a new password for your account."
|
|
3409
|
+
}) {
|
|
3410
|
+
const [password, setPassword] = React30.useState("");
|
|
3411
|
+
const [confirmPassword, setConfirmPassword] = React30.useState("");
|
|
3412
|
+
const [showPassword, setShowPassword] = React30.useState(false);
|
|
3413
|
+
const [showConfirmPassword, setShowConfirmPassword] = React30.useState(false);
|
|
3414
|
+
const [error, setError] = React30.useState(initialError ?? "");
|
|
3415
|
+
const [loading, setLoading] = React30.useState(false);
|
|
3416
|
+
const [passwordReset, setPasswordReset] = React30.useState(false);
|
|
3417
|
+
const router = useRouter();
|
|
3418
|
+
const [supabase] = React30.useState(() => createClient());
|
|
3419
|
+
React30.useEffect(() => {
|
|
3420
|
+
if (initialError) setError(initialError);
|
|
3421
|
+
}, [initialError]);
|
|
3422
|
+
React30.useEffect(() => {
|
|
3423
|
+
if (!passwordReset) return;
|
|
3424
|
+
const id = setTimeout(() => {
|
|
3425
|
+
router.push(loginHref);
|
|
3426
|
+
}, redirectDelayMs);
|
|
3427
|
+
return () => clearTimeout(id);
|
|
3428
|
+
}, [passwordReset, router, loginHref, redirectDelayMs]);
|
|
3429
|
+
const handleSubmit = async (e) => {
|
|
3430
|
+
e.preventDefault();
|
|
3431
|
+
setError("");
|
|
3432
|
+
if (!password || !confirmPassword) {
|
|
3433
|
+
setError("Please fill in both fields");
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3436
|
+
const { isValid, errors } = validatePassword(password);
|
|
3437
|
+
if (!isValid) {
|
|
3438
|
+
setError(errors[0]);
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
if (password !== confirmPassword) {
|
|
3442
|
+
setError("Passwords do not match");
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
setLoading(true);
|
|
3446
|
+
try {
|
|
3447
|
+
const { error: updateError } = await supabase.auth.updateUser({
|
|
3448
|
+
password
|
|
3449
|
+
});
|
|
3450
|
+
if (updateError) {
|
|
3451
|
+
setError(getAuthErrorMessage(updateError));
|
|
3452
|
+
} else {
|
|
3453
|
+
try {
|
|
3454
|
+
await supabase.auth.signOut({ scope: "global" });
|
|
3455
|
+
} catch (signOutError) {
|
|
3456
|
+
console.warn(
|
|
3457
|
+
"[ResetPasswordForm] global sign-out failed after successful update",
|
|
3458
|
+
signOutError
|
|
3459
|
+
);
|
|
3460
|
+
}
|
|
3461
|
+
setPasswordReset(true);
|
|
3462
|
+
}
|
|
3463
|
+
} catch {
|
|
3464
|
+
setError("An unexpected error occurred. Please try again.");
|
|
3465
|
+
} finally {
|
|
3466
|
+
setLoading(false);
|
|
3467
|
+
}
|
|
3468
|
+
};
|
|
3469
|
+
if (passwordReset) {
|
|
3470
|
+
return /* @__PURE__ */ jsxs25("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6 text-center", children: [
|
|
3471
|
+
/* @__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(CheckCircle2, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
|
|
3472
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
3473
|
+
/* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Password updated" }),
|
|
3474
|
+
/* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: "Redirecting you to sign in\u2026" })
|
|
3475
|
+
] }),
|
|
3476
|
+
/* @__PURE__ */ jsx41(Link6, { href: loginHref, className: "block pt-2", children: /* @__PURE__ */ jsx41(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: "Continue to sign in" }) })
|
|
3477
|
+
] });
|
|
3478
|
+
}
|
|
3479
|
+
return /* @__PURE__ */ jsxs25("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
|
|
3480
|
+
/* @__PURE__ */ jsxs25("div", { className: "text-center", children: [
|
|
3481
|
+
/* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
|
|
3482
|
+
/* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3483
|
+
] }),
|
|
3484
|
+
/* @__PURE__ */ jsxs25("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3485
|
+
error && /* @__PURE__ */ jsx41(
|
|
3486
|
+
"div",
|
|
3487
|
+
{
|
|
3488
|
+
id: "reset-error",
|
|
3489
|
+
role: "alert",
|
|
3490
|
+
"aria-live": "polite",
|
|
3491
|
+
className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
|
|
3492
|
+
children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
|
|
3493
|
+
/* @__PURE__ */ jsx41(AlertCircle3, { className: "h-4 w-4 flex-shrink-0" }),
|
|
3494
|
+
/* @__PURE__ */ jsx41("span", { children: error })
|
|
3495
|
+
] })
|
|
3496
|
+
}
|
|
3497
|
+
),
|
|
3498
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
3499
|
+
/* @__PURE__ */ jsx41(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "New password" }),
|
|
3500
|
+
/* @__PURE__ */ jsxs25("div", { className: "relative", children: [
|
|
3501
|
+
/* @__PURE__ */ jsx41(
|
|
3502
|
+
Input,
|
|
3503
|
+
{
|
|
3504
|
+
id: "password",
|
|
3505
|
+
type: showPassword ? "text" : "password",
|
|
3506
|
+
placeholder: "Enter a new password",
|
|
3507
|
+
value: password,
|
|
3508
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3509
|
+
className: "h-11 pl-10 pr-10",
|
|
3510
|
+
disabled: loading,
|
|
3511
|
+
autoComplete: "new-password",
|
|
3512
|
+
autoFocus: true,
|
|
3513
|
+
"aria-invalid": !!error,
|
|
3514
|
+
"aria-describedby": error ? "reset-error" : void 0
|
|
3515
|
+
}
|
|
3516
|
+
),
|
|
3517
|
+
/* @__PURE__ */ jsx41(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
|
|
3518
|
+
/* @__PURE__ */ jsx41(
|
|
3519
|
+
"button",
|
|
3520
|
+
{
|
|
3521
|
+
type: "button",
|
|
3522
|
+
onClick: () => setShowPassword((prev) => !prev),
|
|
3523
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[var(--timberwolf)]",
|
|
3524
|
+
disabled: loading,
|
|
3525
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
3526
|
+
"aria-pressed": showPassword,
|
|
3527
|
+
children: showPassword ? /* @__PURE__ */ jsx41(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx41(Eye2, { className: "h-4 w-4" })
|
|
3528
|
+
}
|
|
3529
|
+
)
|
|
3530
|
+
] })
|
|
3531
|
+
] }),
|
|
3532
|
+
/* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
3533
|
+
/* @__PURE__ */ jsx41(
|
|
3534
|
+
Label,
|
|
3535
|
+
{
|
|
3536
|
+
htmlFor: "confirmPassword",
|
|
3537
|
+
className: "text-[var(--timberwolf)]",
|
|
3538
|
+
children: "Confirm new password"
|
|
3539
|
+
}
|
|
3540
|
+
),
|
|
3541
|
+
/* @__PURE__ */ jsxs25("div", { className: "relative", children: [
|
|
3542
|
+
/* @__PURE__ */ jsx41(
|
|
3543
|
+
Input,
|
|
3544
|
+
{
|
|
3545
|
+
id: "confirmPassword",
|
|
3546
|
+
type: showConfirmPassword ? "text" : "password",
|
|
3547
|
+
placeholder: "Re-enter your new password",
|
|
3548
|
+
value: confirmPassword,
|
|
3549
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
3550
|
+
className: "h-11 pl-10 pr-10",
|
|
3551
|
+
disabled: loading,
|
|
3552
|
+
autoComplete: "new-password",
|
|
3553
|
+
"aria-invalid": !!error,
|
|
3554
|
+
"aria-describedby": error ? "reset-error" : void 0
|
|
3555
|
+
}
|
|
3556
|
+
),
|
|
3557
|
+
/* @__PURE__ */ jsx41(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
|
|
3558
|
+
/* @__PURE__ */ jsx41(
|
|
3559
|
+
"button",
|
|
3560
|
+
{
|
|
3561
|
+
type: "button",
|
|
3562
|
+
onClick: () => setShowConfirmPassword((prev) => !prev),
|
|
3563
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[var(--timberwolf)]",
|
|
3564
|
+
disabled: loading,
|
|
3565
|
+
"aria-label": showConfirmPassword ? "Hide password" : "Show password",
|
|
3566
|
+
"aria-pressed": showConfirmPassword,
|
|
3567
|
+
children: showConfirmPassword ? /* @__PURE__ */ jsx41(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx41(Eye2, { className: "h-4 w-4" })
|
|
3568
|
+
}
|
|
3569
|
+
)
|
|
3570
|
+
] })
|
|
3571
|
+
] }),
|
|
3572
|
+
/* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", children: "At least 8 characters with an uppercase letter, a lowercase letter, and a number." }),
|
|
3573
|
+
/* @__PURE__ */ jsx41(
|
|
3574
|
+
Button,
|
|
3575
|
+
{
|
|
3576
|
+
type: "submit",
|
|
3577
|
+
className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
|
|
3578
|
+
disabled: loading,
|
|
3579
|
+
children: loading ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
|
|
3580
|
+
/* @__PURE__ */ jsx41(Loader23, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
3581
|
+
"Updating password..."
|
|
3582
|
+
] }) : "Update password"
|
|
3583
|
+
}
|
|
3584
|
+
)
|
|
3585
|
+
] }),
|
|
3586
|
+
/* @__PURE__ */ jsx41("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsx41(
|
|
3587
|
+
Link6,
|
|
3588
|
+
{
|
|
3589
|
+
href: loginHref,
|
|
3590
|
+
className: "text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
|
|
3591
|
+
children: "Back to sign in"
|
|
3592
|
+
}
|
|
3593
|
+
) })
|
|
3594
|
+
] });
|
|
3595
|
+
}
|
|
2869
3596
|
export {
|
|
2870
3597
|
AnimatedTooltip,
|
|
2871
3598
|
Avatar,
|
|
@@ -2909,6 +3636,7 @@ export {
|
|
|
2909
3636
|
FlipWords,
|
|
2910
3637
|
Footer,
|
|
2911
3638
|
FooterCreditsBar,
|
|
3639
|
+
ForgotPasswordForm,
|
|
2912
3640
|
HeroHighlight,
|
|
2913
3641
|
Highlight,
|
|
2914
3642
|
HoverCard,
|
|
@@ -2924,6 +3652,7 @@ export {
|
|
|
2924
3652
|
PopoverAnchor,
|
|
2925
3653
|
PopoverContent,
|
|
2926
3654
|
PopoverTrigger,
|
|
3655
|
+
ResetPasswordForm,
|
|
2927
3656
|
SearchBar,
|
|
2928
3657
|
SectionCard,
|
|
2929
3658
|
Select,
|
|
@@ -2947,6 +3676,8 @@ export {
|
|
|
2947
3676
|
SheetPortal,
|
|
2948
3677
|
SheetTitle,
|
|
2949
3678
|
SheetTrigger,
|
|
3679
|
+
SignInForm,
|
|
3680
|
+
SiteFooter,
|
|
2950
3681
|
Skeleton,
|
|
2951
3682
|
StatsGrid,
|
|
2952
3683
|
Switch,
|