270 lines
9.1 KiB
TypeScript
270 lines
9.1 KiB
TypeScript
"use client";
|
||
|
||
import { zodResolver } from "@hookform/resolvers/zod";
|
||
import { useForm } from "react-hook-form";
|
||
import * as z from "zod";
|
||
|
||
import { getUserDetail, updateUser } from "@/pages/users/lib/api";
|
||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||
|
||
import formatPhone from "@/shared/lib/formatPhone";
|
||
import onlyNumber from "@/shared/lib/onlyNumber";
|
||
import { Button } from "@/shared/ui/button";
|
||
import {
|
||
Form,
|
||
FormControl,
|
||
FormField,
|
||
FormItem,
|
||
FormLabel,
|
||
FormMessage,
|
||
} from "@/shared/ui/form";
|
||
import { Input } from "@/shared/ui/input";
|
||
import { ArrowLeft, Loader2, Mail, Phone, Save, User } from "lucide-react";
|
||
import { useEffect } from "react";
|
||
import { useTranslation } from "react-i18next";
|
||
import { useNavigate, useParams } from "react-router-dom";
|
||
|
||
const formSchema = z
|
||
.object({
|
||
first_name: z
|
||
.string()
|
||
.min(3, "Ism kamida 3 ta belgidan iborat bo‘lishi kerak")
|
||
.nonempty("Ism majburiy"),
|
||
last_name: z
|
||
.string()
|
||
.min(3, "Familiya kamida 3 ta belgidan iborat bo‘lishi kerak")
|
||
.nonempty("Familiya majburiy"),
|
||
email: z.string().email("Email formati noto‘g‘ri").or(z.literal("")),
|
||
phone: z.string().min(9, "Telefon raqam to‘liq emas").or(z.literal("+998")),
|
||
})
|
||
.refine((data) => data.email || data.phone, {
|
||
message: "Email yoki telefon raqami kiritilishi shart",
|
||
path: ["contact"],
|
||
});
|
||
|
||
export default function EditUser() {
|
||
const navigate = useNavigate();
|
||
const { id } = useParams();
|
||
const { t } = useTranslation();
|
||
|
||
const { data } = useQuery({
|
||
queryKey: ["user_detail", id],
|
||
queryFn: () => getUserDetail({ id: Number(id) }),
|
||
});
|
||
|
||
const form = useForm<z.infer<typeof formSchema>>({
|
||
resolver: zodResolver(formSchema),
|
||
defaultValues: {
|
||
first_name: "",
|
||
last_name: "",
|
||
email: "",
|
||
phone: "+998",
|
||
},
|
||
});
|
||
|
||
useEffect(() => {
|
||
if (data) {
|
||
const u = data.data.data;
|
||
form.reset({
|
||
first_name: u.first_name,
|
||
last_name: u.last_name,
|
||
email: u.email || "",
|
||
phone: formatPhone(u.phone),
|
||
});
|
||
}
|
||
}, [data, form]);
|
||
|
||
const { mutate, isPending } = useMutation({
|
||
mutationFn: ({
|
||
body,
|
||
id,
|
||
}: {
|
||
body: {
|
||
first_name: string;
|
||
last_name: string;
|
||
email: string | null;
|
||
phone: string | null;
|
||
};
|
||
id: number;
|
||
}) => updateUser({ body, id }),
|
||
onSuccess: () => navigate("/user"),
|
||
});
|
||
|
||
const onSubmit = (values: z.infer<typeof formSchema>) => {
|
||
if (data) {
|
||
mutate({
|
||
body: {
|
||
first_name: values.first_name,
|
||
last_name: values.last_name,
|
||
email: values.email.length === 0 ? null : values.email,
|
||
phone: values.phone.length === 0 ? null : onlyNumber(values.phone),
|
||
},
|
||
id: data.data.data.id,
|
||
});
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen w-full bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 p-4 md:p-8">
|
||
<div className="w-full mx-auto">
|
||
{/* Header */}
|
||
<div className="mb-6">
|
||
<Button
|
||
variant="ghost"
|
||
onClick={() => navigate(-1)}
|
||
className="mb-4 -ml-2 text-slate-400 cursor-pointer hover:text-slate-100 hover:bg-slate-800/50"
|
||
>
|
||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||
{t("Orqaga")}
|
||
</Button>
|
||
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-emerald-600 to-teal-700 flex items-center justify-center shadow-lg shadow-emerald-500/20">
|
||
<User className="w-6 h-6 text-white" />
|
||
</div>
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-slate-100">
|
||
{t("Tahrirlash")}
|
||
</h1>
|
||
<p className="text-slate-400 text-sm">
|
||
{t("Ma'lumotlarni yangilang")}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Form */}
|
||
<div className="bg-slate-900/50 backdrop-blur-sm rounded-2xl shadow-xl border border-slate-800/50 p-6 md:p-8">
|
||
<Form {...form}>
|
||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-5">
|
||
{/* First Name */}
|
||
<FormField
|
||
control={form.control}
|
||
name="first_name"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel className="text-slate-300">
|
||
{t("Ismi")}
|
||
</FormLabel>
|
||
<FormControl>
|
||
<div className="relative">
|
||
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500" />
|
||
<Input
|
||
{...field}
|
||
placeholder="John"
|
||
className="pl-10 h-11 bg-slate-800/50 text-slate-200 border-slate-700/50 focus:border-emerald-500"
|
||
/>
|
||
</div>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
{/* Last Name */}
|
||
<FormField
|
||
control={form.control}
|
||
name="last_name"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel className="text-slate-300">
|
||
{t("Familiyasi")}
|
||
</FormLabel>
|
||
<FormControl>
|
||
<div className="relative">
|
||
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500" />
|
||
<Input
|
||
{...field}
|
||
placeholder="Doe"
|
||
className="pl-10 h-11 bg-slate-800/50 text-slate-200 border-slate-700/50 focus:border-emerald-500"
|
||
/>
|
||
</div>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
{/* Email */}
|
||
<FormField
|
||
control={form.control}
|
||
name="email"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel className="text-slate-300">
|
||
{t("Email")}
|
||
</FormLabel>
|
||
<FormControl>
|
||
<div className="relative">
|
||
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500" />
|
||
<Input
|
||
{...field}
|
||
placeholder="email@example.com"
|
||
className="pl-10 h-11 bg-slate-800/50 text-slate-200 border-slate-700/50 focus:border-emerald-500"
|
||
/>
|
||
</div>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
{/* Phone */}
|
||
<FormField
|
||
control={form.control}
|
||
name="phone"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel className="text-slate-300">
|
||
{t("Telefon raqami")}
|
||
</FormLabel>
|
||
<FormControl>
|
||
<div className="relative">
|
||
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500" />
|
||
<Input
|
||
{...field}
|
||
value={formatPhone(field.value)}
|
||
onChange={(e) => field.onChange(e.target.value)}
|
||
placeholder="+998 90 123 45 67"
|
||
className="pl-10 h-11 bg-slate-800/50 text-slate-200 border-slate-700/50 focus:border-emerald-500"
|
||
/>
|
||
</div>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
|
||
{/* Submit buttons */}
|
||
<div className="flex gap-3 pt-4">
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
onClick={() => navigate(-1)}
|
||
className="flex-1 h-11 rounded-lg border-slate-700/50 bg-slate-800/50 hover:bg-slate-700/50 text-slate-300 hover:text-slate-100 font-medium"
|
||
>
|
||
{t("Bekor qilish")}
|
||
</Button>
|
||
<Button
|
||
type="submit"
|
||
disabled={isPending}
|
||
className="flex-1 h-11 rounded-lg bg-gradient-to-r from-emerald-600 to-teal-700 hover:from-emerald-700 hover:to-teal-800 text-white font-medium"
|
||
>
|
||
{isPending ? (
|
||
<Loader2 className="animate-spin" />
|
||
) : (
|
||
<>
|
||
<Save className="w-5 h-5 mr-2" />
|
||
{t("Yangilash")}
|
||
</>
|
||
)}
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
</Form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|