/
masterOK
/
FEF
Обзор
Документация
Войти
/
masterOK
/
FEF
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
CreateServicePage.tsx
273 строки
9 KB
masterOK
upload files
10 ноя 2025, 19:52
10 ноя 2025, 19:52
2cb4a49
Код
Авторство
О чём код?
import { useQuery, useMutation } from "@tanstack/react-query"; import { useLocation } from "wouter"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription, } from "@/components/ui/form"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useToast } from "@/hooks/use-toast"; import { Loader2, ArrowLeft } from "lucide-react"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { insertServiceSchema } from "@shared/schema"; interface Category { id: string; name: string; } const formSchema = insertServiceSchema .omit({ masterId: true }) .extend({ price: z.coerce.number().positive({ message: "Цена должна быть больше 0" }), durationMinutes: z.coerce.number().positive({ message: "Длительность должна быть больше 0" }).optional(), }); type FormValues = z.infer<typeof formSchema>; export default function CreateServicePage() { const [, navigate] = useLocation(); const { toast } = useToast(); const form = useForm<FormValues>({ resolver: zodResolver(formSchema), defaultValues: { title: "", description: "", price: 0, categoryId: "", deliveryType: "offline", durationMinutes: 60, }, }); const { data: categories = [], isLoading: categoriesLoading } = useQuery<Category[]>({ queryKey: ["/api/categories"], }); const createServiceMutation = useMutation({ mutationFn: async (serviceData: FormValues) => { return await apiRequest("POST", "/api/services", serviceData); }, onSuccess: () => { toast({ title: "Успешно!", description: "Услуга создана", }); queryClient.invalidateQueries({ queryKey: ["/api/services"] }); navigate("/profile"); }, onError: (error: any) => { toast({ title: "Ошибка", description: error.message || "Не удалось создать услугу", variant: "destructive", }); }, }); const onSubmit = (data: FormValues) => { createServiceMutation.mutate(data); }; return ( <div className="min-h-screen bg-muted/30"> <div className="container mx-auto px-4 py-12 max-w-2xl"> <Button variant="ghost" onClick={() => navigate("/profile")} className="mb-6 gap-2" data-testid="button-back" > <ArrowLeft className="h-4 w-4" /> Назад в профиль </Button> <Card> <CardHeader> <CardTitle>Создать новую услугу</CardTitle> </CardHeader> <CardContent> <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> <FormField control={form.control} name="title" render={({ field }) => ( <FormItem> <FormLabel>Название услуги *</FormLabel> <FormControl> <Input placeholder="Например: Ремонт стиральной машины" {...field} data-testid="input-title" /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="description" render={({ field }) => ( <FormItem> <FormLabel>Описание</FormLabel> <FormControl> <Textarea placeholder="Опишите что включает услуга, ваш опыт и преимущества" className="min-h-32" {...field} value={field.value || ""} data-testid="input-description" /> </FormControl> <FormMessage /> </FormItem> )} /> <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <FormField control={form.control} name="price" render={({ field }) => ( <FormItem> <FormLabel>Цена (₽) *</FormLabel> <FormControl> <Input type="number" min="0" step="0.01" placeholder="1000" {...field} data-testid="input-price" /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="durationMinutes" render={({ field }) => ( <FormItem> <FormLabel>Длительность (мин)</FormLabel> <FormControl> <Input type="number" min="0" placeholder="60" {...field} value={field.value || ""} data-testid="input-duration" /> </FormControl> <FormDescription>Примерное время выполнения</FormDescription> <FormMessage /> </FormItem> )} /> </div> <FormField control={form.control} name="categoryId" render={({ field }) => ( <FormItem> <FormLabel>Категория *</FormLabel> <Select onValueChange={field.onChange} value={field.value} disabled={categoriesLoading} > <FormControl> <SelectTrigger data-testid="select-category"> <SelectValue placeholder="Выберите категорию" /> </SelectTrigger> </FormControl> <SelectContent> {categories.map((category) => ( <SelectItem key={category.id} value={category.id}> {category.name} </SelectItem> ))} </SelectContent> </Select> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="deliveryType" render={({ field }) => ( <FormItem> <FormLabel>Тип оказания услуги *</FormLabel> <Select onValueChange={field.onChange} value={field.value}> <FormControl> <SelectTrigger data-testid="select-delivery-type"> <SelectValue /> </SelectTrigger> </FormControl> <SelectContent> <SelectItem value="offline">Выезд к клиенту</SelectItem> <SelectItem value="online">Онлайн</SelectItem> </SelectContent> </Select> <FormMessage /> </FormItem> )} /> <div className="flex gap-4"> <Button type="submit" disabled={createServiceMutation.isPending} className="flex-1" data-testid="button-submit" > {createServiceMutation.isPending && ( <Loader2 className="mr-2 h-4 w-4 animate-spin" /> )} Создать услугу </Button> <Button type="button" variant="outline" onClick={() => navigate("/profile")} disabled={createServiceMutation.isPending} data-testid="button-cancel" > Отмена </Button> </div> </form> </Form> </CardContent> </Card> </div> </div> ); }