/
masterOK
/
FEF
Обзор
Документация
Войти
/
masterOK
/
FEF
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ReviewDialog.tsx
167 строк
5 KB
masterOK
upload files
10 ноя 2025, 19:52
10 ноя 2025, 19:52
2cb4a49
Код
Авторство
О чём код?
import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; import { z } from "zod"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Star } from "lucide-react"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; import { insertReviewSchema } from "@shared/schema"; const reviewFormSchema = insertReviewSchema.extend({ comment: z.string().min(10, "Комментарий должен содержать минимум 10 символов").optional().or(z.literal("")), }); type ReviewFormData = z.infer<typeof reviewFormSchema>; interface ReviewDialogProps { open: boolean; onOpenChange: (open: boolean) => void; orderId: string; masterId: string; } export function ReviewDialog({ open, onOpenChange, orderId, masterId }: ReviewDialogProps) { const [hoveredStar, setHoveredStar] = useState(0); const { toast } = useToast(); const { register, handleSubmit, setValue, watch, formState: { errors }, reset, } = useForm<ReviewFormData>({ resolver: zodResolver(reviewFormSchema), defaultValues: { orderId, masterId, rating: 0, comment: "", }, }); const rating = watch("rating"); const createReviewMutation = useMutation({ mutationFn: async (data: ReviewFormData) => { return await apiRequest("POST", "/api/reviews", data); }, onSuccess: () => { toast({ title: "Отзыв отправлен", description: "Спасибо за ваш отзыв!", }); queryClient.invalidateQueries({ queryKey: [`/api/orders/${orderId}`] }); queryClient.invalidateQueries({ queryKey: [`/api/reviews/${masterId}`] }); queryClient.invalidateQueries({ queryKey: [`/api/masters/${masterId}`] }); reset(); onOpenChange(false); }, onError: (error: any) => { toast({ title: "Ошибка", description: error.message || "Не удалось отправить отзыв", variant: "destructive", }); }, }); const onSubmit = (data: ReviewFormData) => { if (data.rating === 0) { toast({ title: "Ошибка", description: "Пожалуйста, выберите рейтинг", variant: "destructive", }); return; } createReviewMutation.mutate(data); }; return ( <Dialog open={open} onOpenChange={onOpenChange}> <DialogContent className="sm:max-w-[500px]" data-testid="dialog-review"> <DialogHeader> <DialogTitle>Оставить отзыв</DialogTitle> <DialogDescription> Оцените качество выполненной работы и оставьте комментарий </DialogDescription> </DialogHeader> <form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> <div> <Label>Рейтинг</Label> <div className="flex items-center gap-2 mt-2"> {[1, 2, 3, 4, 5].map((star) => ( <button key={star} type="button" onClick={() => setValue("rating", star)} onMouseEnter={() => setHoveredStar(star)} onMouseLeave={() => setHoveredStar(0)} className="focus:outline-none transition-transform hover:scale-110" data-testid={`star-${star}`} > <Star className={`h-8 w-8 ${ star <= (hoveredStar || rating) ? "fill-yellow-400 text-yellow-400" : "text-gray-300" }`} /> </button> ))} <span className="ml-2 text-sm font-medium" data-testid="text-rating"> {rating > 0 ? `${rating} из 5` : "Не выбрано"} </span> </div> </div> <div> <Label htmlFor="comment">Комментарий</Label> <Textarea id="comment" {...register("comment")} placeholder="Расскажите о вашем опыте работы с мастером (минимум 10 символов)" className="mt-2 min-h-[120px]" data-testid="textarea-comment" /> {errors.comment && ( <p className="text-sm text-destructive mt-1">{errors.comment.message}</p> )} </div> <DialogFooter> <Button type="button" variant="outline" onClick={() => onOpenChange(false)} data-testid="button-cancel" > Отмена </Button> <Button type="submit" disabled={createReviewMutation.isPending || rating === 0} data-testid="button-submit-review" > {createReviewMutation.isPending ? "Отправка..." : "Отправить отзыв"} </Button> </DialogFooter> </form> </DialogContent> </Dialog> ); }