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
73 changes: 73 additions & 0 deletions logify-frontend/src/components/ui/PasswordInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useState } from "react";
import { Eye, EyeOff } from "lucide-react";
import PropTypes from "prop-types";

function PasswordInput({
label,
name,
value,
onChange,
placeholder = "Enter your password",
error,
className = "",
id,
...props
}) {
const [isVisible, setIsVisible] = useState(false);
const inputId = id || `password-input-${name}`;

const toggleVisibility = () => {
setIsVisible(!isVisible);
};

return (
<div>
{label && (
<label
htmlFor={inputId}
className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold"
>
{label}
</label>
)}
<div className="relative mt-2">
<input
id={inputId}
type={isVisible ? "text" : "password"}
name={name}
Comment on lines +23 to +37
Comment on lines +29 to +37
value={value}
onChange={onChange}
placeholder={placeholder}
className={`w-full rounded-xl border border-border bg-white px-4 py-3 pr-10 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800 ${className}`}
{...props}
/>
Comment on lines +17 to +43
<button
type="button"
onClick={toggleVisibility}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200 transition-colors"
aria-label={isVisible ? "Hide password" : "Show password"}
>
{isVisible ? (
<EyeOff size={18} />
) : (
<Eye size={18} />
)}
</button>
</div>
{error && <p className="mt-1 text-xs text-red-600">{error}</p>}
</div>
);
}

PasswordInput.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
placeholder: PropTypes.string,
error: PropTypes.string,
className: PropTypes.string,
id: PropTypes.string,
};

export default PasswordInput;
53 changes: 17 additions & 36 deletions logify-frontend/src/pages/AdminSignupPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Link, useNavigate } from "react-router-dom";
import { api } from "../config/api.js";
import { AuthContext } from "../contexts/AuthContext";
import Loading from "../components/ui/Loading";
import PasswordInput from "../components/ui/PasswordInput";
import AuthActionButton from "../components/ui/AuthActionButton";
import AuthLayout from "./auth/AuthLayout";
import GuestOnlyRoute from "./auth/GuestOnlyRoute";
Expand Down Expand Up @@ -307,43 +308,23 @@ const AdminSignupPage = () => {
)}
</div>

<div>
<label className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold">
Password
</label>
<input
name="password"
type="password"
value={formData.password}
onChange={onChange}
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
placeholder="At least 8 characters"
/>
{fieldErrors.password && (
<p className="mt-1 text-xs text-red-600">
{fieldErrors.password}
</p>
)}
</div>
<PasswordInput
label="Password"
name="password"
value={formData.password}
onChange={onChange}
placeholder="At least 8 characters"
error={fieldErrors.password}
/>

<div>
<label className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold">
Confirm Password
</label>
<input
name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={onChange}
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
placeholder="Re-enter password"
/>
{fieldErrors.confirmPassword && (
<p className="mt-1 text-xs text-red-600">
{fieldErrors.confirmPassword}
</p>
)}
</div>
<PasswordInput
label="Confirm Password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={onChange}
placeholder="Re-enter password"
error={fieldErrors.confirmPassword}
/>

{error && (
<div className="rounded-xl border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/70 dark:bg-red-950/40 dark:text-red-300">
Expand Down
25 changes: 8 additions & 17 deletions logify-frontend/src/pages/LoginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link, useLocation } from "react-router-dom";

import { AuthContext } from "../contexts/AuthContext";
import Loading from "../components/ui/Loading";
import PasswordInput from "../components/ui/PasswordInput";
import AuthLayout from "./auth/AuthLayout";
import GuestOnlyRoute from "./auth/GuestOnlyRoute";

Expand Down Expand Up @@ -76,23 +77,13 @@ const LoginPage = () => {
/>
</div>

<div>
<label
htmlFor="password"
className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold"
>
Password
</label>
<input
id="password"
name="password"
type="password"
value={formData.password}
onChange={onChange}
placeholder="Enter your password"
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none ring-0 transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
/>
</div>
<PasswordInput
label="Password"
name="password"
value={formData.password}
onChange={onChange}
placeholder="Enter your password"
/>

{error && (
<div className="rounded-xl border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/70 dark:bg-red-950/40 dark:text-red-300">
Expand Down
55 changes: 18 additions & 37 deletions logify-frontend/src/pages/StudentSignupPage.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../contexts/AuthContext";
import Loading from "../components/ui/Loading";
import PasswordInput from "../components/ui/PasswordInput";
import AuthLayout from "./auth/AuthLayout";
import GuestOnlyRoute from "./auth/GuestOnlyRoute";
import { api } from "../config/api.js";
Expand Down Expand Up @@ -442,43 +443,23 @@ const StudentSignupPage = () => {
)}
</div>

<div>
<label className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold">
Password
</label>
<input
type="password"
name="password"
value={formData.password}
onChange={onChange}
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
placeholder="At least 8 characters"
/>
{fieldErrors.password && (
<p className="mt-1 text-xs text-red-600">
{fieldErrors.password}
</p>
)}
</div>

<div>
<label className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold">
Confirm Password
</label>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={onChange}
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
placeholder="Re-enter password"
/>
{fieldErrors.confirmPassword && (
<p className="mt-1 text-xs text-red-600">
{fieldErrors.confirmPassword}
</p>
)}
</div>
<PasswordInput
label="Password"
name="password"
value={formData.password}
onChange={onChange}
placeholder="At least 8 characters"
error={fieldErrors.password}
/>

<PasswordInput
label="Confirm Password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={onChange}
placeholder="Re-enter password"
error={fieldErrors.confirmPassword}
/>

{error && (
<div className="my-4 rounded-xl border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/70 dark:bg-red-950/40 dark:text-red-300">
Expand Down
53 changes: 17 additions & 36 deletions logify-frontend/src/pages/SupervisorSignupPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Link, useNavigate, useLocation } from "react-router-dom";
import { api } from "../config/api.js";
import { AuthContext } from "../contexts/AuthContext";
import Loading from "../components/ui/Loading";
import PasswordInput from "../components/ui/PasswordInput";
import AuthActionButton from "../components/ui/AuthActionButton";
import AuthLayout from "./auth/AuthLayout";
import GuestOnlyRoute from "./auth/GuestOnlyRoute";
Expand Down Expand Up @@ -400,43 +401,23 @@ const SupervisorSignupPage = () => {
</div>
)}

<div>
<label className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold">
Password
</label>
<input
name="password"
type="password"
value={formData.password}
onChange={onChange}
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
placeholder="At least 8 characters"
/>
{fieldErrors.password && (
<p className="mt-1 text-xs text-red-600">
{fieldErrors.password}
</p>
)}
</div>
<PasswordInput
label="Password"
name="password"
value={formData.password}
onChange={onChange}
placeholder="At least 8 characters"
error={fieldErrors.password}
/>

<div>
<label className="text-xs font-black uppercase tracking-widest text-maroon-dark dark:text-gold">
Confirm Password
</label>
<input
name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={onChange}
className="mt-2 w-full rounded-xl border border-border bg-white px-4 py-3 text-sm outline-none transition focus:border-gold dark:border-slate-700 dark:bg-slate-800"
placeholder="Re-enter password"
/>
{fieldErrors.confirmPassword && (
<p className="mt-1 text-xs text-red-600">
{fieldErrors.confirmPassword}
</p>
)}
</div>
<PasswordInput
label="Confirm Password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={onChange}
placeholder="Re-enter password"
error={fieldErrors.confirmPassword}
/>

{error && (
<div className="rounded-xl border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/70 dark:bg-red-950/40 dark:text-red-300">
Expand Down
2 changes: 1 addition & 1 deletion logify-frontend/src/tests/authRoutes.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ test("login page forwards the original destination to the login action", async (
fireEvent.change(screen.getByLabelText(/email \/ webmail/i), {
target: { value: "student@example.com" },
});
fireEvent.change(screen.getByLabelText(/password/i), {
fireEvent.change(screen.getByLabelText(/^password$/i), {
target: { value: "password123" },
});
fireEvent.click(screen.getByRole("button", { name: /login/i }));
Expand Down
Loading