feat: add password change functionality and dependencies

This commit is contained in:
decolua committed 2026-01-09 17:29:11 +07:00
1 parent bf6e09bb6f
commit 23cfb19459
8 files changed
+320 -3

No files matched your search

+90 -1
View File
@@ -1,7 +1,7 @@
"use client";
import { useState, useEffect } from "react";
import { Card, Button, Badge, Toggle } from "@/shared/components";
import { Card, Button, Badge, Toggle, Input } from "@/shared/components";
import { useTheme } from "@/shared/hooks/useTheme";
import { APP_CONFIG } from "@/shared/constants/config";
@@ -9,6 +9,9 @@ export default function ProfilePage() {
const { theme, setTheme, isDark } = useTheme();
const [settings, setSettings] = useState({ fallbackStrategy: "fill-first" });
const [loading, setLoading] = useState(true);
const [passwords, setPasswords] = useState({ current: "", new: "", confirm: "" });
const [passStatus, setPassStatus] = useState({ type: "", message: "" });
const [passLoading, setPassLoading] = useState(false);
useEffect(() => {
fetch("/api/settings")
@@ -23,6 +26,41 @@ export default function ProfilePage() {
});
}, []);
const handlePasswordChange = async (e) => {
e.preventDefault();
if (passwords.new !== passwords.confirm) {
setPassStatus({ type: "error", message: "Passwords do not match" });
return;
}
setPassLoading(true);
setPassStatus({ type: "", message: "" });
try {
const res = await fetch("/api/settings", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
currentPassword: passwords.current,
newPassword: passwords.new,
}),
});
const data = await res.json();
if (res.ok) {
setPassStatus({ type: "success", message: "Password updated successfully" });
setPasswords({ current: "", new: "", confirm: "" });
} else {
setPassStatus({ type: "error", message: data.error || "Failed to update password" });
}
} catch (err) {
setPassStatus({ type: "error", message: "An error occurred" });
} finally {
setPassLoading(false);
}
};
const updateFallbackStrategy = async (strategy) => {
try {
const res = await fetch("/api/settings", {
@@ -59,6 +97,57 @@ export default function ProfilePage() {
</div>
</Card>
{/* Routing Preferences */}
<Card>
<h3 className="text-lg font-semibold mb-4">Security</h3>
<form onSubmit={handlePasswordChange} className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<label className="text-sm font-medium">Current Password</label>
<Input
type="password"
placeholder="Enter current password"
value={passwords.current}
onChange={(e) => setPasswords({ ...passwords, current: e.target.value })}
required
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex flex-col gap-2">
<label className="text-sm font-medium">New Password</label>
<Input
type="password"
placeholder="Enter new password"
value={passwords.new}
onChange={(e) => setPasswords({ ...passwords, new: e.target.value })}
required
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-sm font-medium">Confirm New Password</label>
<Input
type="password"
placeholder="Confirm new password"
value={passwords.confirm}
onChange={(e) => setPasswords({ ...passwords, confirm: e.target.value })}
required
/>
</div>
</div>
{passStatus.message && (
<p className={`text-sm ${passStatus.type === "error" ? "text-red-500" : "text-green-500"}`}>
{passStatus.message}
</p>
)}
<div className="pt-2">
<Button type="submit" variant="primary" isLoading={passLoading}>
Update Password
</Button>
</div>
</form>
</Card>
{/* Routing Preferences */}
<Card>
<h3 className="text-lg font-semibold mb-4">Routing Strategy</h3>