Initial Release
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { z } from "zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { authService } from "@/services/authService";
|
||||
|
||||
// Password change form schema
|
||||
const passwordFormSchema = z.object({
|
||||
currentPassword: z.string().min(1, "Current password is required"),
|
||||
newPassword: z.string().min(8, "Password must be at least 8 characters"),
|
||||
confirmPassword: z.string().min(8, "Confirm password is required"),
|
||||
}).refine((data) => data.newPassword === data.confirmPassword, {
|
||||
message: "Passwords don't match",
|
||||
path: ["confirmPassword"],
|
||||
});
|
||||
|
||||
type PasswordFormValues = z.infer<typeof passwordFormSchema>;
|
||||
|
||||
interface ChangePasswordFormProps {
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export function ChangePasswordForm({ userId }: ChangePasswordFormProps) {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
|
||||
const [showNewPassword, setShowNewPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const form = useForm<PasswordFormValues>({
|
||||
resolver: zodResolver(passwordFormSchema),
|
||||
defaultValues: {
|
||||
currentPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(data: PasswordFormValues) {
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
// PocketBase requires the old password along with the new one
|
||||
await pb.collection('users').update(userId, {
|
||||
oldPassword: data.currentPassword,
|
||||
password: data.newPassword,
|
||||
passwordConfirm: data.confirmPassword,
|
||||
});
|
||||
|
||||
// Refresh auth data to ensure token remains valid
|
||||
await authService.refreshUserData();
|
||||
|
||||
toast({
|
||||
title: "Password updated",
|
||||
description: "Your password has been changed successfully.",
|
||||
});
|
||||
|
||||
// Reset the form
|
||||
form.reset();
|
||||
} catch (error) {
|
||||
console.error("Password change error:", error);
|
||||
|
||||
let errorMessage = "Failed to update password. Please try again.";
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "Password change failed",
|
||||
description: errorMessage,
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="currentPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Current Password</FormLabel>
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showCurrentPassword ? "text" : "password"}
|
||||
placeholder="Your current password"
|
||||
{...field}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2"
|
||||
onClick={() => setShowCurrentPassword(!showCurrentPassword)}
|
||||
>
|
||||
{showCurrentPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
<span className="sr-only">
|
||||
{showCurrentPassword ? "Hide password" : "Show password"}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="newPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>New Password</FormLabel>
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showNewPassword ? "text" : "password"}
|
||||
placeholder="New password"
|
||||
{...field}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2"
|
||||
onClick={() => setShowNewPassword(!showNewPassword)}
|
||||
>
|
||||
{showNewPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
<span className="sr-only">
|
||||
{showNewPassword ? "Hide password" : "Show password"}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="confirmPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Confirm Password</FormLabel>
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showConfirmPassword ? "text" : "password"}
|
||||
placeholder="Confirm new password"
|
||||
{...field}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2"
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
>
|
||||
{showConfirmPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
<span className="sr-only">
|
||||
{showConfirmPassword ? "Hide password" : "Show password"}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? "Updating..." : "Change Password"}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user