feat: button to make password visible

This commit is contained in:
2025-03-11 19:14:34 +01:00
parent e4c95c37ee
commit 5b8f476997
5 changed files with 195 additions and 143 deletions

72
src/Icons.tsx Normal file
View File

@@ -0,0 +1,72 @@
export const Eye = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 36 36"
>
<path
fill="#E1E8ED"
d="M35.059 18c0 3.304-7.642 11-17.067 11S.925 22.249.925 18c0-3.314 34.134-3.314 34.134 0"
/>
<path
fill="#292F33"
d="M35.059 18H.925c0-3.313 7.642-11 17.067-11s17.067 7.686 17.067 11"
/>
<path
fill="#F5F8FA"
d="M33.817 18c0 2.904-7.087 9.667-15.826 9.667S2.166 21.732 2.166 18c0-2.912 7.085-9.666 15.825-9.666C26.73 8.333 33.817 15.088 33.817 18"
/>
<circle
cx="18"
cy="18"
r="8.458"
fill="#8B5E3C"
style={{ fill: "#919191", fillOpacity: 1 }}
/>
<circle cx="18" cy="18" r="4.708" fill="#292F33" />
<circle cx="14.983" cy="15" r="2" fill="#F5F8FA" />
</svg>
);
export const EyeSlash = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 36 36"
>
<path
fill="#e1e8ed"
d="M35.059 18c0 3.304-7.642 11-17.067 11S.925 22.249.925 18c0-3.314 34.134-3.314 34.134 0"
/>
<path
fill="#292f33"
d="M35.059 18H.925c0-3.313 7.642-11 17.067-11s17.067 7.686 17.067 11"
/>
<path
fill="#f5f8fa"
d="M33.817 18c0 2.904-7.087 9.667-15.826 9.667S2.166 21.732 2.166 18c0-2.912 7.085-9.666 15.825-9.666C26.73 8.333 33.817 15.088 33.817 18"
/>
<circle
cx="18"
cy="18"
r="8.458"
fill="#8B5E3C"
style={{ fill: "#919191", fillOpacity: 1 }}
/>
<circle cx="18" cy="18" r="4.708" fill="#292f33" />
<circle cx="14.983" cy="15" r="2" fill="#f5f8fa" />
<path
d="m-2.97 30.25 41.94-24.5"
style={{
fill: "#404040",
fillOpacity: 1,
stroke: "#404040",
strokeWidth: 3,
strokeDasharray: "none",
strokeOpacity: 1,
}}
/>
</svg>
);

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { currentUser, login, User } from "./api";
import Header from "./Header";
import { useLocation, useNavigate } from "react-router";
import { Eye, EyeSlash } from "./Icons";
export interface LoginProps {
onLogin: (user: User) => void;
@@ -12,6 +13,7 @@ export const Login = ({ onLogin }: LoginProps) => {
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [visible, setVisible] = useState(false);
const navigate = useNavigate();
const location = useLocation();
@@ -52,34 +54,65 @@ export const Login = ({ onLogin }: LoginProps) => {
<>
<Header />
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
id="username"
name="username"
placeholder="username"
required
value={username}
onChange={(evt) => {
setError("");
setUsername(evt.target.value);
<div
style={{
position: "relative",
display: "flex",
alignItems: "end",
}}
>
<div
style={{
width: "100%",
marginRight: "8px",
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
/>
</div>
<div>
<input
type="password"
id="password"
name="password"
placeholder="password"
minLength={8}
value={password}
required
onChange={(evt) => {
setError("");
setPassword(evt.target.value);
>
<div>
<input
type="text"
id="username"
name="username"
placeholder="username"
required
value={username}
onChange={(evt) => {
setError("");
setUsername(evt.target.value);
}}
/>
</div>
<div>
<input
type={visible ? "text" : "password"}
id="password"
name="password"
placeholder="password"
minLength={8}
value={password}
required
onChange={(evt) => {
setError("");
setPassword(evt.target.value);
}}
/>
</div>
</div>
<div
style={{
position: "absolute",
right: "-1em",
margin: "auto 4px",
background: "unset",
fontSize: "x-large",
cursor: "pointer",
}}
/>
onClick={() => setVisible(!visible)}
>
{visible ? <Eye /> : <EyeSlash />}
</div>
</div>
<div>{error && <span style={{ color: "red" }}>{error}</span>}</div>
<button type="submit" value="login" style={{ fontSize: "small" }}>

View File

@@ -2,6 +2,9 @@ import { jwtDecode, JwtPayload } from "jwt-decode";
import { useEffect, useState } from "react";
import { baseUrl } from "./api";
import { useNavigate } from "react-router";
import eye from "./eye.svg";
import { Eye, EyeSlash } from "./Icons";
import { relative } from "path";
interface SetPassToken extends JwtPayload {
name: string;
@@ -15,6 +18,7 @@ export const SetPassword = () => {
const [token, setToken] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [visible, setVisible] = useState(false);
const navigate = useNavigate();
useEffect(() => {
@@ -85,35 +89,66 @@ export const SetPassword = () => {
</span>
)}
<form onSubmit={handleSubmit}>
<div>
<input
type="password"
id="password"
name="password"
placeholder="password"
minLength={8}
value={password}
required
onChange={(evt) => {
setError("");
setPassword(evt.target.value);
<div
style={{
position: "relative",
display: "flex",
alignItems: "center",
}}
>
<div
style={{
width: "100%",
marginRight: "8px",
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
/>
</div>
<div>
<input
type="password"
id="password-repeat"
name="password-repeat"
placeholder="repeat password"
minLength={8}
value={passwordr}
required
onChange={(evt) => {
setError("");
setPasswordr(evt.target.value);
>
<div>
<input
type={visible ? "text" : "password"}
id="password"
name="password"
placeholder="password"
minLength={8}
value={password}
required
onChange={(evt) => {
setError("");
setPassword(evt.target.value);
}}
/>
</div>
<div>
<input
type={visible ? "text" : "password"}
id="password-repeat"
name="password-repeat"
placeholder="repeat password"
minLength={8}
value={passwordr}
required
onChange={(evt) => {
setError("");
setPasswordr(evt.target.value);
}}
/>
</div>
</div>
<div
style={{
position: "absolute",
right: 0,
margin: "auto 4px",
background: "unset",
fontSize: "xx-large",
cursor: "pointer",
}}
/>
onClick={() => setVisible(!visible)}
>
{visible ? <Eye /> : <EyeSlash />}
</div>
</div>
<div>{error && <span style={{ color: "red" }}>{error}</span>}</div>
<button type="submit" value="login" style={{ fontSize: "small" }}>