Files
cutt/frontend/src/SetPassword.tsx
2025-12-21 17:08:24 +01:00

151 lines
4.5 KiB
TypeScript

import { jwtDecode, JwtPayload } from "jwt-decode";
import { useEffect, useState } from "react";
import { baseUrl } from "./api";
import { useNavigate } from "react-router";
interface PassToken extends JwtPayload {
username: string;
name: string;
team_id: number;
}
export const SetPassword = () => {
const [name, setName] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [passwordr, setPasswordr] = useState("");
const [passwordHint, setPasswordHint] = useState("");
const [token, setToken] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const token = params.get("token");
if (token) {
setToken(token);
try {
const payload = jwtDecode<PassToken>(token);
if (payload.sub === "set password") {
if (payload.name) setName(payload.name);
if (payload.username) setUsername(payload.username);
} else {
setError("not a valid token for setting your password");
}
} catch (InvalidTokenError) {
setError("not a valid token");
}
} else setError("no token found");
}, []);
function passwordCheck() {
if (password === passwordr) {
setPasswordHint("");
} else setPasswordHint("passwords do not match");
}
useEffect(() => passwordCheck(), [passwordr]);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (password === passwordr) {
const req = new Request(`${baseUrl}api/set_password`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ token: token, password: password }),
});
let resp: Response;
try {
resp = await fetch(req);
} catch (e) {
throw new Error(`request failed: ${e}`);
}
setLoading(false);
if (resp.ok) {
console.log(resp);
navigate("/", {
replace: true,
state: { username: username, password: password },
});
}
if (!resp.ok) {
if (resp.status === 401) {
const { detail } = await resp.json();
if (detail) setError(detail);
else setError("unauthorized");
throw new Error("Unauthorized");
}
}
} else setError("passwords are not the same");
}
return (
<section className="section is-medium">
<div className="container is-max-tablet">
<div className="block">
<p className="level-item has-text-centered">
<img
className="image"
alt="cool ultimate team tool"
src="cutt.svg"
style={{ width: 200 }}
/>
</p>
</div>
<h1 className="title">
Set password for {name && username && `${name} (${username})`}
</h1>
<form onSubmit={handleSubmit}>
<div className="field">
<div className="field">
<label className="label">password</label>
<input
className="input"
type={"password"}
id="password"
name="password"
placeholder="password"
minLength={8}
value={password}
required
onChange={(evt) => {
setError("");
setPassword(evt.target.value);
}}
/>
</div>
<div className="field">
<input
className={"input" + (passwordHint ? " is-danger" : "")}
type={"password"}
id="password-repeat"
name="password-repeat"
placeholder="repeat password"
minLength={8}
value={passwordr}
required
onChange={(evt) => {
setError("");
setPasswordr(evt.target.value);
}}
/>
<p className={"help is-danger"}>{passwordHint}</p>
</div>
<hr />
</div>
<p className={"help" + (error ? " is-danger" : " is-success")}>
{error}
</p>
<div className="field is-grouped is-grouped-centered">
<button className="button is-light is-link">change password</button>
</div>
</form>
</div>
</section>
);
};