feat(users): add user administration and password management

This commit is contained in:
Schubert Ferenc 2026-07-11 14:04:30 +02:00
parent 47f54d3461
commit b584e60273
16 changed files with 720 additions and 22 deletions

View file

@ -0,0 +1,63 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { KeyRound } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useAuth } from "@/components/auth";
import { apiFetch } from "@/lib/api";
const schema = z.object({
current_password: z.string().min(1, "Aktuelles Passwort erforderlich"),
new_password: z.string().min(12, "Mindestens 12 Zeichen"),
new_password_confirmation: z.string().min(12, "Mindestens 12 Zeichen")
}).refine((values) => values.new_password === values.new_password_confirmation, {
path: ["new_password_confirmation"],
message: "Die neuen Passwoerter stimmen nicht ueberein"
});
type SecurityForm = z.infer<typeof schema>;
export default function SecurityPage() {
const { token, refreshUser } = useAuth();
const [message, setMessage] = useState("");
const form = useForm<SecurityForm>({
resolver: zodResolver(schema),
defaultValues: { current_password: "", new_password: "", new_password_confirmation: "" }
});
async function onSubmit(values: SecurityForm) {
await apiFetch("/auth/change-password", token ?? "", { method: "POST", body: JSON.stringify(values) });
setMessage("Passwort erfolgreich geaendert");
form.reset();
refreshUser();
}
return (
<div className="mx-auto max-w-xl">
<header className="mb-6">
<h1 className="text-3xl font-semibold text-text">Sicherheit</h1>
<p className="mt-2 text-text-light">Hier aenderst du dein Passwort.</p>
</header>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4 rounded-lg border border-border bg-surface p-6 shadow-soft">
<label className="block">
<span className="text-sm font-medium">Aktuelles Passwort</span>
<input type="password" {...form.register("current_password")} className="mt-2 h-12 w-full rounded-lg border border-border px-4" />
</label>
<label className="block">
<span className="text-sm font-medium">Neues Passwort</span>
<input type="password" {...form.register("new_password")} className="mt-2 h-12 w-full rounded-lg border border-border px-4" />
</label>
<label className="block">
<span className="text-sm font-medium">Neues Passwort wiederholen</span>
<input type="password" {...form.register("new_password_confirmation")} className="mt-2 h-12 w-full rounded-lg border border-border px-4" />
</label>
{message && <div className="rounded-lg border border-success/30 bg-white p-3 text-sm text-success shadow-soft">{message}</div>}
<div className="flex justify-end">
<button type="submit" className="btn btn-primary h-12"><KeyRound className="h-4 w-4" /> Passwort ändern</button>
</div>
</form>
</div>
);
}