Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/auth/forgot-password/ForgotPasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ export default function ForgotPasswordForm() {
await authApi.forgotPassword({
email: data.email,
});

// Show success message regardless of whether email exists
setIsSubmitted(true);
} catch (err) {
const apiError = err as ApiError;

// Still show success message for security (don't reveal if email exists)
if (apiError.status === 404) {
setIsSubmitted(true);
Expand Down Expand Up @@ -114,7 +114,7 @@ export default function ForgotPasswordForm() {
Return to Sign In
</Button>
</Link>

<p className="text-center text-sm text-gray-500">
Didn&apos;t receive the email? Check your spam folder or{' '}
<button
Expand Down Expand Up @@ -212,4 +212,4 @@ export default function ForgotPasswordForm() {
</p>
</Card>
);
}
}
17 changes: 13 additions & 4 deletions app/auth/reset-password/ResetPasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ export default function ResetPasswordForm() {
token,
password: data.password,
});

setIsSuccess(true);
} catch (err) {
const apiError = err as ApiError;

if (apiError.status === 400 && apiError.message?.includes('expired')) {
setTokenError('This reset link has expired. Please request a new password reset.');
} else if (apiError.status === 400 && apiError.message?.includes('invalid')) {
Expand Down Expand Up @@ -197,7 +197,7 @@ export default function ResetPasswordForm() {
Request New Reset Link
</Button>
</Link>

<Link href="/auth/login" className="block">
<Button variant="secondary" fullWidth>
Return to Sign In
Expand Down Expand Up @@ -270,6 +270,7 @@ export default function ResetPasswordForm() {
Password must contain:
</p>
<div className="space-y-1">
{/* Length check */}
<div className="flex items-center gap-2 text-xs">
<div
className={`w-4 h-4 rounded-full flex items-center justify-center ${
Expand Down Expand Up @@ -300,6 +301,8 @@ export default function ResetPasswordForm() {
At least {passwordRequirements.minLength} characters
</span>
</div>

{/* Uppercase check */}
<div className="flex items-center gap-2 text-xs">
<div
className={`w-4 h-4 rounded-full flex items-center justify-center ${
Expand Down Expand Up @@ -330,6 +333,8 @@ export default function ResetPasswordForm() {
One uppercase letter
</span>
</div>

{/* Lowercase check */}
<div className="flex items-center gap-2 text-xs">
<div
className={`w-4 h-4 rounded-full flex items-center justify-center ${
Expand Down Expand Up @@ -360,6 +365,8 @@ export default function ResetPasswordForm() {
One lowercase letter
</span>
</div>

{/* Number check */}
<div className="flex items-center gap-2 text-xs">
<div
className={`w-4 h-4 rounded-full flex items-center justify-center ${
Expand Down Expand Up @@ -390,6 +397,8 @@ export default function ResetPasswordForm() {
One number
</span>
</div>

{/* Special character check */}
<div className="flex items-center gap-2 text-xs">
<div
className={`w-4 h-4 rounded-full flex items-center justify-center ${
Expand Down Expand Up @@ -477,4 +486,4 @@ export default function ResetPasswordForm() {
</p>
</Card>
);
}
}
5 changes: 2 additions & 3 deletions app/auth/reset-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import ResetPasswordForm from './ResetPasswordForm';

export const metadata: Metadata = {
title: 'Reset Password | StellarAid',
description:
'Create a new password for your StellarAid account.',
description: 'Create a new password for your StellarAid account.',
};

export default function ResetPasswordPage() {
return <ResetPasswordForm />;
}
}
176 changes: 119 additions & 57 deletions components/passwordInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState, forwardRef, InputHTMLAttributes } from "react";
// import { FiEye, FiEyeOff } from "react-icons/fi";
import PasswordStrengthBar from "./passwordStrengthBar";

interface PasswordInputProps extends InputHTMLAttributes<HTMLInputElement> {
Expand All @@ -11,67 +10,130 @@ interface PasswordInputProps extends InputHTMLAttributes<HTMLInputElement> {
autoComplete?: string;
}

const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(function PasswordInput({ label, id, value, onChange, showStrength = false, autoComplete, ...props }, ref) {
const [focused, setFocused] = useState(false);
const [visible, setVisible] = useState(false);
return (
<div style={{ display: "flex", flexDirection: "column", gap: "6px", width: "100%" }}>
<label
htmlFor={id}
style={{ fontSize: "13px", fontWeight: "700", color: "#374151", fontFamily: "'Outfit', sans-serif" }}
const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
function PasswordInput(
{
label,
id,
value,
onChange,
showStrength = false,
autoComplete,
...props
},
ref,
) {
const [focused, setFocused] = useState(false);
const [visible, setVisible] = useState(false);

return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "6px",
width: "100%",
}}
>
{label}
</label>
<div style={{ position: "relative" }}>
<input
ref={ref}
id={id}
type={visible ? "text" : "password"}
value={value}
onChange={onChange}
autoComplete={autoComplete}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
<label
htmlFor={id}
style={{
width: "100%",
padding: "10px 13px",
fontSize: "14px",
fontSize: "13px",
fontWeight: "700",
color: "#374151",
fontFamily: "'Outfit', sans-serif",
color: "#1e293b",
background: focused ? "#fff" : "#f8fafc",
border: focused ? "1.5px solid #1e3a8a" : "1.5px solid #e2e8f0",
borderRadius: "8px",
outline: "none",
boxSizing: "border-box",
transition: "border-color 0.18s, background 0.18s, box-shadow 0.18s",
boxShadow: focused ? "0 0 0 3px rgba(30,58,138,0.10)" : "none",
letterSpacing: "0.12em",
}}
onMouseEnter={(e) => { if (!focused) e.currentTarget.style.borderColor = "#94a3b8"; }}
onMouseLeave={(e) => { if (!focused) e.currentTarget.style.borderColor = "#e2e8f0"; }}
{...props}
/>
<button
type="button"
onClick={() => setVisible((v) => !v)}
style={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
background: "transparent",
border: "none",
padding: 0,
cursor: "pointer",
color: "#6b7280",
}}
>
{/* {visible ? <FiEyeOff size={20} /> : <FiEye size={20} />} */}
</button>
{label}
</label>
<div style={{ position: "relative" }}>
<input
ref={ref}
id={id}
type={visible ? "text" : "password"}
value={value}
onChange={onChange}
autoComplete={autoComplete}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
style={{
width: "100%",
padding: "10px 13px",
fontSize: "14px",
fontFamily: "'Outfit', sans-serif",
color: "#1e293b",
background: focused ? "#fff" : "#f8fafc",
border: focused ? "1.5px solid #1e3a8a" : "1.5px solid #e2e8f0",
borderRadius: "8px",
outline: "none",
boxSizing: "border-box",
transition:
"border-color 0.18s, background 0.18s, box-shadow 0.18s",
boxShadow: focused ? "0 0 0 3px rgba(30,58,138,0.10)" : "none",
letterSpacing: "0.12em",
}}
onMouseEnter={(e) => {
if (!focused) e.currentTarget.style.borderColor = "#94a3b8";
}}
onMouseLeave={(e) => {
if (!focused) e.currentTarget.style.borderColor = "#e2e8f0";
}}
{...props}
/>
<button
type="button"
onClick={() => setVisible((v) => !v)}
aria-label={visible ? "Hide password" : "Show password"}
style={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
background: "transparent",
border: "none",
padding: 0,
cursor: "pointer",
color: "#6b7280",
}}
>
{visible ? (
// Eye-off icon (hide password)
<svg
width={20}
height={20}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
>
<path d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94" />
<path d="M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19" />
<line x1="1" y1="1" x2="23" y2="23" />
</svg>
) : (
// Eye icon (show password)
<svg
width={20}
height={20}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
>
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
<circle cx="12" cy="12" r="3" />
</svg>
)}
</button>
</div>
{showStrength && <PasswordStrengthBar password={value} />}
</div>
{showStrength && <PasswordStrengthBar password={value} />}
</div>
);
});
);
},
);

export default PasswordInput;
26 changes: 7 additions & 19 deletions components/ui/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import React, {
createContext,
useContext,
HTMLAttributes,
ForwardRefRenderFunction,
forwardRef,
} from "react";

Expand Down Expand Up @@ -177,10 +176,6 @@ export const ToastProvider: React.FC<ToastProviderProps> = ({
setToasts((prev) => prev.filter((toast) => toast.id !== id));
}, []);

// const removeToast = useCallback((id: string) => {
// setToasts((prev) => prev.filter((toast) => toast.id !== id));
// }, []);

const addToast = useCallback(
(toast: Omit<ToastProps, "id" | "onClose">): string => {
const id = generateId();
Expand Down Expand Up @@ -283,12 +278,12 @@ const ToastItem = forwardRef<HTMLDivElement, ToastProps>(function ToastItem(
const [isVisible, setIsVisible] = useState(false);
const [isLeaving, setIsLeaving] = useState(false);

// const handleClose = useCallback(() => {
// setIsLeaving(true);
// setTimeout(() => {
// onClose(id);
// }, 300);
// }, [id, onClose]);
const handleClose = useCallback(() => {
setIsLeaving(true);
setTimeout(() => {
onClose(id);
}, 300);
}, [id, onClose]);

// Handle animation on mount
useEffect(() => {
Expand All @@ -297,13 +292,6 @@ const ToastItem = forwardRef<HTMLDivElement, ToastProps>(function ToastItem(
});
}, []);

const handleClose = useCallback(() => {
setIsLeaving(true);
setTimeout(() => {
onClose(id);
}, 300);
}, [id, onClose]);

// Handle auto-dismiss
useEffect(() => {
const dismissTime = durationMap[duration];
Expand Down Expand Up @@ -430,4 +418,4 @@ export const Toast = forwardRef<HTMLDivElement, ToastProps>(function Toast(

Toast.displayName = "Toast";

export default Toast;
export default Toast;
Loading