116 lines
4.7 KiB
TypeScript
116 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import { fetchWithAuth } from "@/lib/apiClient";
|
|
import { useState } from "react";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Lock } from "lucide-react";
|
|
|
|
export default function PasswordPage() {
|
|
const [currentPassword, setCurrentPassword] = useState("");
|
|
const [newPassword, setNewPassword] = useState("");
|
|
const [confirmPassword, setConfirmPassword] = useState("");
|
|
const [passwordSaving, setPasswordSaving] = useState(false);
|
|
|
|
const handleChangePassword = async () => {
|
|
if (newPassword !== confirmPassword) {
|
|
alert("New passwords do not match.");
|
|
return;
|
|
}
|
|
setPasswordSaving(true);
|
|
try {
|
|
const res = await fetchWithAuth("/api/auth/change-password", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
current_password: currentPassword,
|
|
new_password: newPassword,
|
|
}),
|
|
});
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
alert(data.message || "Password updated successfully!");
|
|
setCurrentPassword("");
|
|
setNewPassword("");
|
|
setConfirmPassword("");
|
|
} else {
|
|
alert("Failed to change password: " + (data.detail || "Unknown error"));
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("Network error. Could not change password.");
|
|
} finally {
|
|
setPasswordSaving(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-3xl mx-auto">
|
|
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
|
|
<span className="p-2.5 rounded-xl bg-purple-50 dark:bg-[var(--cyan-ai-10)] border border-purple-300/60 dark:border-[var(--cyan-ai-20)]">
|
|
<Lock className="text-purple-600 dark:text-[var(--cyan-ai)]" size={28} />
|
|
</span>
|
|
Change Password
|
|
</h1>
|
|
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
|
|
Update your account password securely.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Card className="border-border bg-background shadow-sm">
|
|
<CardHeader>
|
|
<CardTitle>Change Password</CardTitle>
|
|
<CardDescription>Update your account password</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-bold">Current Password</label>
|
|
<input
|
|
type="password"
|
|
value={currentPassword}
|
|
onChange={(e) => setCurrentPassword(e.target.value)}
|
|
className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan-ai)]"
|
|
placeholder="Enter current password"
|
|
/>
|
|
</div>
|
|
<div className="hidden md:block"></div>
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-bold">New Password</label>
|
|
<input
|
|
type="password"
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan-ai)]"
|
|
placeholder="Enter new password"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-bold">Confirm New Password</label>
|
|
<input
|
|
type="password"
|
|
value={confirmPassword}
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
className="w-full bg-secondary/50 border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan-ai)]"
|
|
placeholder="Confirm new password"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex pt-2 border-t border-border mt-6">
|
|
<Button
|
|
className="bg-foreground text-background hover:bg-foreground/90 font-bold px-6 rounded-xl"
|
|
onClick={handleChangePassword}
|
|
disabled={passwordSaving || !currentPassword || !newPassword || !confirmPassword}
|
|
>
|
|
{passwordSaving ? "Updating..." : "Update Password"}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|