Add change password feature to settings page
- Add PUT /api/me/password endpoint in backend - Add changePassword API function in frontend - Add change password form UI in SettingsPanel - Add i18n translations for change password (zh/en) - Fix TypeScript type error in i18n context Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,14 @@ const SettingsPanel = () => {
|
||||
});
|
||||
const [saved, setSaved] = useState(false);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [passwordForm, setPasswordForm] = useState({
|
||||
currentPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
const [passwordError, setPasswordError] = useState("");
|
||||
const [passwordSuccess, setPasswordSuccess] = useState(false);
|
||||
const [changingPassword, setChangingPassword] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
api
|
||||
@@ -92,6 +100,43 @@ const SettingsPanel = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePassword = async () => {
|
||||
setPasswordError("");
|
||||
setPasswordSuccess(false);
|
||||
|
||||
if (!passwordForm.currentPassword) {
|
||||
setPasswordError(t("currentPasswordRequired"));
|
||||
return;
|
||||
}
|
||||
if (!passwordForm.newPassword) {
|
||||
setPasswordError(t("newPasswordRequired"));
|
||||
return;
|
||||
}
|
||||
if (passwordForm.newPassword.length < 6) {
|
||||
setPasswordError(t("passwordTooShort"));
|
||||
return;
|
||||
}
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
setPasswordError(t("passwordMismatch"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setChangingPassword(true);
|
||||
await api.changePassword({
|
||||
currentPassword: passwordForm.currentPassword,
|
||||
newPassword: passwordForm.newPassword,
|
||||
});
|
||||
setPasswordSuccess(true);
|
||||
setPasswordForm({ currentPassword: "", newPassword: "", confirmPassword: "" });
|
||||
setTimeout(() => setPasswordSuccess(false), 3000);
|
||||
} catch (error) {
|
||||
setPasswordError(error instanceof Error ? error.message : "Failed to change password");
|
||||
} finally {
|
||||
setChangingPassword(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="bg-white">
|
||||
<CardHeader>
|
||||
@@ -195,6 +240,53 @@ const SettingsPanel = () => {
|
||||
</Button>
|
||||
{saved && <div className="text-sm text-primary">{t("saved")}</div>}
|
||||
</div>
|
||||
|
||||
{/* Change Password Section */}
|
||||
<div className="mt-8 pt-6 border-t border-slate-200">
|
||||
<h3 className="text-lg font-medium mb-4">{t("changePassword")}</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="currentPassword">{t("currentPassword")}</Label>
|
||||
<Input
|
||||
id="currentPassword"
|
||||
type="password"
|
||||
value={passwordForm.currentPassword}
|
||||
onChange={(e) => setPasswordForm({ ...passwordForm, currentPassword: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="newPassword">{t("newPassword")}</Label>
|
||||
<Input
|
||||
id="newPassword"
|
||||
type="password"
|
||||
value={passwordForm.newPassword}
|
||||
onChange={(e) => setPasswordForm({ ...passwordForm, newPassword: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirmPassword">{t("confirmNewPassword")}</Label>
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
value={passwordForm.confirmPassword}
|
||||
onChange={(e) => setPasswordForm({ ...passwordForm, confirmPassword: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
{passwordError && (
|
||||
<div className="text-sm text-red-500">{passwordError}</div>
|
||||
)}
|
||||
{passwordSuccess && (
|
||||
<div className="text-sm text-green-600">{t("passwordChanged")}</div>
|
||||
)}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleChangePassword}
|
||||
disabled={changingPassword}
|
||||
>
|
||||
{changingPassword ? t("loading") : t("changePassword")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user