Initial Release

This commit is contained in:
Tola Leng
2025-05-09 21:28:07 +07:00
commit 0c6cd18e6f
244 changed files with 50633 additions and 0 deletions
@@ -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>
);
}