Files
simple-admin/src/pages/tours/ui/StepOne.tsx
2025-11-12 13:53:30 +05:00

2220 lines
81 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import {
createTours,
getAllAmenities,
hotelBadge,
hotelTransport,
updateTours,
} from "@/pages/tours/lib/api";
import { TourformSchema } from "@/pages/tours/lib/form";
import { useTicketStore } from "@/pages/tours/lib/store";
import type { GetOneTours } from "@/pages/tours/lib/type";
import TicketsImagesModel from "@/pages/tours/ui/TicketsImagesModel";
import formatPrice from "@/shared/lib/formatPrice";
import { Badge } from "@/shared/ui/badge";
import { Button } from "@/shared/ui/button";
import { Calendar } from "@/shared/ui/calendar";
import {
Command,
CommandGroup,
CommandItem,
CommandList,
} from "@/shared/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from "@/shared/ui/form";
import { Input } from "@/shared/ui/input";
import { Label } from "@/shared/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
import { RadioGroup, RadioGroupItem } from "@/shared/ui/radio-group";
import { Textarea } from "@/shared/ui/textarea";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery } from "@tanstack/react-query";
import { ChevronDownIcon, Loader2, SquareCheckBig, XIcon } from "lucide-react";
import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import z from "zod";
const StepOne = ({
setStep,
data,
id,
isEditMode,
}: {
setStep: Dispatch<SetStateAction<number>>;
data: GetOneTours | undefined;
id: string | undefined;
isEditMode: boolean;
}) => {
const [displayPrice, setDisplayPrice] = useState("");
// const [tarifdisplayPrice, setTarifDisplayPrice] = useState<string[]>([]);
const [transportPrices, setTransportPrices] = useState<string[]>([]);
const { t } = useTranslation();
const [openDate, setOpenDate] = useState(false);
const [openDateTravel, setOpenDateTravel] = useState(false);
const form = useForm<z.infer<typeof TourformSchema>>({
resolver: zodResolver(TourformSchema),
defaultValues: {
title: "",
title_ru: "",
hotel_services: [],
price: 0,
departure: "",
// tarif: [],
transport: [],
departure_ru: "",
destination: "",
destination_ru: "",
location_name: "",
location_name_ru: "",
departureDateTime: {
date: undefined,
},
amenities: [],
hotel_meals: [],
travelDateTime: {
date: undefined,
},
passenger_count: "1",
min_person: "1",
max_person: "1",
extra_service: [],
paid_extra_service: [],
languages: "",
duration: "1",
badges: [],
images: [],
hotel_info: "",
hotel_info_ru: "",
hotel_meals_info: "",
hotel_meals_info_ru: "",
ticket_itinerary: [],
},
});
const { addAmenity, setId, removeAmenity } = useTicketStore();
useEffect(() => {
if (!isEditMode || !data?.data) return;
const tour = data.data;
// 🔹 Jo'nash vaqti
let departureDateTime = undefined;
if (tour.departure_time) {
const d = new Date(tour.departure_time);
departureDateTime = {
date: d,
};
}
// 🔹 Qaytish vaqti
let travelDateTime = undefined;
if (tour.travel_time) {
const d = new Date(tour.travel_time);
travelDateTime = {
date: d,
};
}
// 🔹 Transport
const transports =
tour.transports?.map((t, i) => ({
transport: i + 1,
price: t.price ?? 0,
})) ?? [];
setTransportPrices(transports.map((t) => formatPrice(t.price ?? 0)));
// 🔹 Tarif
// const tariffs =
// tour.tariff?.map((t) => ({
// tariff: t.tariff ?? 0,
// price: t.price ?? 0,
// })) ?? [];
// setTarifDisplayPrice(tariffs.map((t) => formatPrice(t.price ?? 0)));
form.reset({
title: tour.title_uz ?? "",
title_ru: tour.title_ru ?? "",
price: tour.price ?? 0,
passenger_count: String(tour.passenger_count) ?? "1",
min_person: String(tour.min_person) ?? "1",
max_person: String(tour.max_person) ?? "1",
departure: tour.departure_uz ?? "",
departure_ru: tour.departure_ru ?? "",
destination: tour.destination_uz ?? "",
destination_ru: tour.destination_ru ?? "",
location_name: tour.location_name_uz ?? "",
location_name_ru: tour.location_name_ru ?? "",
hotel_info: tour.hotel_info_uz ?? "",
hotel_info_ru: tour.hotel_info_ru ?? "",
hotel_meals_info: tour.hotel_meals_uz ?? "",
hotel_meals_info_ru: tour.hotel_meals_ru ?? "",
languages: tour.languages ?? "",
duration: String(tour.duration_days) ?? "1",
visa_required: tour.visa_required ? "yes" : "no",
badges: tour.badge ?? [],
departureDateTime,
travelDateTime,
amenities: tour.ticket_amenities.map((e) => e.id),
hotel_services:
tour.ticket_included_services?.map((service) => ({
id: service.id,
image: service.image ?? null,
title: service.title_uz ?? service.title ?? "",
title_ru: service.title_ru ?? "",
description: service.desc_uz ?? service.desc ?? "",
desc_ru: service.desc_ru ?? "",
})) ?? [],
hotel_meals:
tour.ticket_hotel_meals?.map((meal) => ({
id: meal.id,
image: meal.image ?? null,
title: meal.name_uz ?? meal.name ?? "",
title_ru: meal.name_ru ?? "",
description: meal.desc_uz ?? meal.desc ?? "",
desc_ru: meal.desc_ru ?? "",
})) ?? [],
transport: transports,
// tarif: tariffs,
ticket_itinerary:
tour.ticket_itinerary?.map((item) => ({
id: item.id,
title: item.title_uz ?? item.title ?? "",
title_ru: item.title_ru ?? "",
duration: item.duration ?? 1,
ticket_itinerary_image:
item.ticket_itinerary_image?.map((img) => ({
image: img.image,
})) ?? [],
ticket_itinerary_destinations:
item.ticket_itinerary_destinations?.map((dest) => ({
name: dest.name_uz ?? dest.name ?? "",
name_ru: dest.name_ru ?? "",
})) ?? [],
})) ?? [],
banner: tour.image_banner ?? null,
images: tour.ticket_images.map((img) => ({
id: img.id,
image: img.image,
})),
extra_service:
tour.extra_service?.map((s) => ({
name: s.name_uz ?? s.name ?? "",
name_ru: s.name_ru ?? "",
})) ?? [],
paid_extra_service:
tour.paid_extra_service?.map((s) => ({
name: s.name_uz ?? s.name ?? "",
name_ru: s.name_ru ?? "",
price: s.price ?? 0,
})) ?? [],
});
// Display price
setDisplayPrice(formatPrice(tour.price ?? 0));
// TicketStore uchun id
setId(tour.id);
}, [isEditMode, data, form, setId]);
const { watch, setValue } = form;
const selectedDate = watch("departureDateTime.date");
const selectedDateTravel = watch("travelDateTime.date");
const { mutate: create, isPending } = useMutation({
mutationFn: (body: FormData) => {
return createTours({ body });
},
onSuccess: (res) => {
setId(res.data.data.id);
setStep(2);
},
onError: () => {
toast.error(t("Xatolik yuz berdi"), {
richColors: true,
position: "top-center",
});
},
});
const { mutate: update, isPending: updatePending } = useMutation({
mutationFn: ({ body, id }: { id: number; body: FormData }) => {
return updateTours({ body, id });
},
onSuccess: (res) => {
setId(res.data.data.id);
setStep(2);
},
onError: () => {
toast.error(t("Xatolik yuz berdi"), {
richColors: true,
position: "top-center",
});
},
});
function onSubmit(value: z.infer<typeof TourformSchema>) {
const formData = new FormData();
// Asosiy ma'lumotlar
formData.append("title", value.title);
formData.append("location_name", value.location_name);
formData.append("location_name_ru", value.location_name_ru);
formData.append(
"visa_required",
value.visa_required === "yes" ? "true" : "false",
);
formData.append("title_ru", value.title_ru);
formData.append("price", String(value.price));
formData.append("min_person", String(value.min_person));
formData.append("max_person", String(value.max_person));
formData.append("departure", String(value.departure));
formData.append("departure_ru", String(value.departure_ru));
formData.append("destination", String(value.destination));
formData.append("destination_ru", String(value.destination_ru));
formData.append(
"departure_time",
value.departureDateTime.date?.toISOString(),
);
formData.append(
"travel_time",
String(value.travelDateTime.date.toISOString()),
);
formData.append("passenger_count", String(value.passenger_count));
if (value.languages) {
formData.append("languages", value.languages);
}
if (value.hotel_info) {
formData.append("hotel_info", value.hotel_info);
}
if (value.hotel_info_ru) {
formData.append("hotel_info_ru", value.hotel_info_ru);
}
if (value.hotel_meals_info) {
formData.append("hotel_meals", value.hotel_meals_info);
}
if (value.hotel_meals_info_ru) {
formData.append("hotel_meals_ru", value.hotel_meals_info_ru);
}
formData.append("duration_days", String(value.duration));
formData.append("rating", String("0.0"));
if (value.banner instanceof File) {
formData.append("image_banner", value.banner);
}
console.log(value.banner, "value.banner");
// Tarif va transport
// value.tarif?.forEach((e, i) => {
// formData.append(`tariff[${i}]tariff`, String(e.tariff));
// formData.append(`tariff[${i}]price`, String(e.price));
// });
value.transport?.forEach((e, i) => {
formData.append(`transports[${i}]transport`, String(e.transport));
formData.append(`transports[${i}]price`, String(e.price));
});
value.badges?.forEach((e) => {
formData.append(`badge`, String(e));
});
value.amenities?.forEach((e) => {
formData.append(`ticket_amenities`, String(e));
});
value.images.forEach((e) => {
if (e.id) {
formData.append(`ticket_images_ids`, String(e.id));
} else if (e.id === undefined) {
formData.append(`ticket_images`, e.image!);
}
});
// 🔹 Hotel Services - edit rejimida ID yuborish, yangi bo'lsa to'liq ma'lumot yuborish
value.hotel_services &&
value.hotel_services.forEach((e, i) => {
if (e.id && typeof e.image === "string") {
formData.append(`ticket_included_services_ids`, String(e.id));
} else {
// Yangi xizmat yoki o'zgartirilgan xizmat
if (e.image instanceof File) {
formData.append(`ticket_included_services[${i}]image`, e.image);
}
formData.append(`ticket_included_services[${i}]title`, e.title);
formData.append(`ticket_included_services[${i}]title_ru`, e.title_ru);
formData.append(`ticket_included_services[${i}]desc_ru`, e.desc_ru);
formData.append(`ticket_included_services[${i}]desc`, e.description);
}
});
// 🔹 Ticket Itinerary - edit rejimida ID yuborish
value.ticket_itinerary?.forEach((itinerary, i) => {
if (itinerary.id) {
// Mavjud itinerary (o'zgartirilmagan)
const hasNewImages = itinerary.ticket_itinerary_image?.some(
(img) =>
img instanceof File ||
(typeof img === "object" &&
"image" in img &&
img.image instanceof File),
);
if (!hasNewImages) {
formData.append(`ticket_itinerary_ids`, String(itinerary.id));
return; // Faqat ID yuborish, boshqa ma'lumot kerak emas
}
}
// Yangi itinerary yoki o'zgartirilgan itinerary
formData.append(`ticket_itinerary[${i}]title`, itinerary.title);
formData.append(`ticket_itinerary[${i}]title_ru`, itinerary.title_ru);
formData.append(
`ticket_itinerary[${i}]duration`,
String(itinerary.duration),
);
// Rasmlar
if (Array.isArray(itinerary.ticket_itinerary_image)) {
itinerary.ticket_itinerary_image.forEach((img, j) => {
let file: File | null = null;
if (img instanceof File) {
file = img;
} else if (
typeof img === "object" &&
"image" in img &&
img.image instanceof File
) {
file = img.image;
}
if (file) {
formData.append(
`ticket_itinerary[${i}]ticket_itinerary_image[${j}]image`,
file,
);
}
});
}
// Destinations
if (Array.isArray(itinerary.ticket_itinerary_destinations)) {
itinerary.ticket_itinerary_destinations.forEach((dest, k) => {
formData.append(
`ticket_itinerary[${i}]ticket_itinerary_destinations[${k}]name`,
dest.name,
);
formData.append(
`ticket_itinerary[${i}]ticket_itinerary_destinations[${k}]name_ru`,
dest.name_ru,
);
});
}
});
if (value.hotel_meals) {
value.hotel_meals.forEach((e, i) => {
if (e.id && typeof e.image === "string") {
// Mavjud meal (o'zgartirilmagan)
formData.append(`ticket_hotel_meals_ids`, String(e.id));
} else {
// Yangi meal yoki o'zgartirilgan meal
if (e.image instanceof File) {
formData.append(`ticket_hotel_meals[${i}]image`, e.image);
}
formData.append(`ticket_hotel_meals[${i}]name`, e.title);
formData.append(`ticket_hotel_meals[${i}]name_ru`, e.title_ru);
formData.append(`ticket_hotel_meals[${i}]desc`, e.description);
formData.append(`ticket_hotel_meals[${i}]desc_ru`, e.desc_ru);
}
});
}
// Extra services
value.extra_service &&
value.extra_service.forEach((e, i) => {
formData.append(`extra_service[${i}]name`, e.name);
formData.append(`extra_service[${i}]name_ru`, e.name_ru);
});
value.paid_extra_service &&
value.paid_extra_service.forEach((e, i) => {
formData.append(`paid_extra_service[${i}]name`, e.name);
formData.append(`paid_extra_service[${i}]name_ru`, e.name_ru);
formData.append(`paid_extra_service[${i}]price`, String(e.price));
});
if (isEditMode && id) {
update({
body: formData,
id: Number(id),
});
} else {
create(formData);
}
}
const { data: badge } = useQuery({
queryKey: ["all_badge"],
queryFn: () => hotelBadge({ page: 1, page_size: 10 }),
});
const { data: amenitiesData } = useQuery({
queryKey: ["all_amenities"],
queryFn: () => getAllAmenities({ page: 1, page_size: 10 }),
});
// const { data: tariff } = useQuery({
// queryKey: ["all_tarif"],
// queryFn: () => hotelTarif({ page: 1, page_size: 10 }),
// });
const { data: transport } = useQuery({
queryKey: ["all_transport"],
queryFn: () => hotelTransport({ page: 1, page_size: 10 }),
});
console.log(form.formState.errors);
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<div className="grid grid-cols-2 gap-4 max-lg:grid-cols-1 items-start">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Sarlavha")}</Label>
<FormControl>
<Input
placeholder="Toshkent - Dubay"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="title_ru"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Sarlavha")} (ru)</Label>
<FormControl>
<Input
placeholder="Ташкент - Дубай"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="price"
render={() => (
<FormItem>
<Label className="text-md">
{t("Narx")} {t("(1 kishi uchun)")}
</Label>
<FormControl>
<Input
type="text"
inputMode="numeric"
placeholder="1 500 000"
value={displayPrice}
onChange={(e) => {
const raw = e.target.value.replace(/\D/g, "");
const num = Number(raw);
if (!isNaN(num)) {
form.setValue("price", num);
setDisplayPrice(raw ? formatPrice(num) : "");
}
}}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="min_person"
render={({ field }) => (
<FormItem>
<Label className="text-md">
{t("hamrohlar soni (eng kamida)")}
</Label>
<FormControl>
<Input
type="number"
inputMode="numeric"
placeholder="1"
{...field}
onChange={(e) =>
form.setValue("min_person", e.target.value)
}
className="h-12 !text-md
[&::-webkit-inner-spin-button]:appearance-none
[&::-webkit-outer-spin-button]:appearance-none
[appearance:textfield]"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="max_person"
render={({ field }) => (
<FormItem>
<Label className="text-md">
{t("hamrohlar soni (eng ko'pida)")}
</Label>
<FormControl>
<Input
type="number"
inputMode="numeric"
placeholder="1"
{...field}
onChange={(e) =>
form.setValue("max_person", e.target.value)
}
className="h-12 !text-md
[&::-webkit-inner-spin-button]:appearance-none
[&::-webkit-outer-spin-button]:appearance-none
[appearance:textfield]"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4 max-lg:grid-cols-1 items-start">
<FormField
control={form.control}
name="departure"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Ketish joyi")}</Label>
<FormControl>
<Input
placeholder="Toshkent"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="departure_ru"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Ketish joyi")} (ru)</Label>
<FormControl>
<Input
placeholder="Ташкент"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="destination"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Borish joyi")}</Label>
<FormControl>
<Input
placeholder="Dubai"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="destination_ru"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Borish joyi")} (ru)</Label>
<FormControl>
<Input
placeholder="Дубай"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="location_name"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Manzil nomi")}</Label>
<FormControl>
<Input
placeholder="Dubai"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="location_name_ru"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Manzil nomi")} (ru)</Label>
<FormControl>
<Input
placeholder="Дубай"
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="passenger_count"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Yo'lovchilar soni")}</Label>
<FormControl>
<Input
type="number"
inputMode="numeric"
placeholder="1"
{...field}
onChange={(e) =>
form.setValue("passenger_count", e.target.value)
}
className="h-12 !text-md
[&::-webkit-inner-spin-button]:appearance-none
[&::-webkit-outer-spin-button]:appearance-none
[appearance:textfield]"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid grid-cols-2 max-lg:grid-cols-1 gap-4">
<FormField
control={form.control}
name="departureDateTime.date"
render={() => (
<FormItem>
<div>
<div className="flex flex-col gap-3">
<Label htmlFor="date-picker" className="px-1 text-md">
{t("Ketish sanasi")}
</Label>
<Popover open={openDate} onOpenChange={setOpenDate}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date-picker"
className="w-full justify-between font-normal h-12"
>
{selectedDate
? selectedDate.toLocaleDateString()
: t("Sana tanlang")}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto overflow-hidden p-0"
align="start"
>
<Calendar
mode="single"
selected={selectedDate}
captionLayout="dropdown"
onSelect={(date) => {
setValue("departureDateTime.date", date!);
setOpenDate(false);
}}
toYear={new Date().getFullYear() + 100}
/>
</PopoverContent>
</Popover>
</div>
</div>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="departureDateTime.time"
render={() => (
<FormItem>
<div className="flex flex-col gap-3">
<Label htmlFor="time-picker" className="px-1 text-md">
{t("Ketish vaqti")}
</Label>
<Input
type="time"
id="time-picker"
step="1"
value={watch("departureDateTime.time")}
onChange={(e) =>
setValue("departureDateTime.time", e.target.value)
}
className="bg-background !h-12 appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div>
</FormItem>
)}
/> */}
<FormField
control={form.control}
name="travelDateTime.date"
render={() => (
<FormItem>
<div className="grid grid-cols-1 gap-4 max-lg:grid-cols-1">
<div className="flex flex-col gap-3">
<Label htmlFor="date-picker" className="px-1 text-md">
{t("Qaytish sanasi")}
</Label>
<Popover
open={openDateTravel}
onOpenChange={setOpenDateTravel}
>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date-picker"
className="w-full justify-between font-normal h-12"
>
{selectedDateTravel
? selectedDateTravel.toLocaleDateString()
: t("Sana tanlang")}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto overflow-hidden p-0"
align="start"
>
<Calendar
mode="single"
selected={selectedDateTravel}
captionLayout="dropdown"
onSelect={(date) => {
setValue("travelDateTime.date", date!);
setOpenDateTravel(false);
}}
toYear={new Date().getFullYear() + 100}
/>
</PopoverContent>
</Popover>
</div>
{/* <div className="flex flex-col gap-3">
<Label htmlFor="time-picker" className="px-1 text-md">
{t("Qaytish vaqti")}
</Label>
<Input
type="time"
id="time-picker"
step="1"
value={watch("travelDateTime.time")}
onChange={(e) =>
setValue("travelDateTime.time", e.target.value)
}
className="bg-background !h-12 appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
</div> */}
</div>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid grid-cols-2 max-lg:grid-cols-1 gap-6">
<FormField
control={form.control}
name="duration"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Tur davomiyligi")}</Label>
<FormControl>
<Input
type="number"
inputMode="numeric"
placeholder="1"
{...field}
onChange={(e) => form.setValue("duration", e.target.value)}
className="h-12 !text-md
[&::-webkit-inner-spin-button]:appearance-none
[&::-webkit-outer-spin-button]:appearance-none
[appearance:textfield]"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="languages"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Tillar")}</Label>
<FormControl>
<Input
placeholder={t("Har bir tilni vergul (,) bilan ajrating")}
{...field}
className="h-12 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid grid-cols-2 max-lg:grid-cols-1 gap-6 items-end">
<FormField
control={form.control}
name="badges"
render={() => (
<FormItem>
<Label className="text-md">{t("Belgilar (Badge)")}</Label>
<div className="flex flex-wrap gap-2 mb-3">
{(form.watch("badges") ?? []).length > 0 &&
(form.watch("badges") ?? []).map((badgeId: number) => {
const badgeItem = badge?.data?.data?.results?.find(
(b: any) => b.id === badgeId,
);
return (
<Badge
key={badgeId}
variant="secondary"
className="px-3 py-1 text-sm flex items-center gap-2"
>
{badgeItem?.name || `Badge #${badgeId}`}
<button
type="button"
onClick={() => {
const current = form.getValues("badges") ?? [];
form.setValue(
"badges",
current.filter((b: number) => b !== badgeId),
);
}}
className="ml-1 text-muted-foreground hover:text-destructive"
>
<XIcon className="size-4" />
</button>
</Badge>
);
})}
</div>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full h-12 justify-between font-normal"
>
{t("Belgilarni tanlang")}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[250px]" align="start">
<Command>
<CommandList>
<CommandGroup heading={t("Mavjud belgilar")}>
{badge?.data?.data?.results?.map((item: any) => {
const currentBadges =
form.getValues("badges") ?? [];
const selected = currentBadges.includes(item.id);
return (
<CommandItem
key={item.id}
onSelect={() => {
if (selected) {
form.setValue(
"badges",
currentBadges.filter(
(b: number) => b !== item.id,
),
);
} else {
form.setValue("badges", [
...currentBadges,
item.id,
]);
}
}}
className="flex items-center gap-2"
>
<SquareCheckBig
className={`size-4 ${
selected
? "text-green-500"
: "text-muted-foreground"
}`}
/>
{item.name}
</CommandItem>
);
})}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="amenities"
render={() => (
<FormItem>
<Label className="text-md">{t("Qulayliklar")}</Label>
<div className="flex flex-wrap gap-2 mb-3">
{(form.watch("amenities") ?? []).length > 0 &&
(form.watch("amenities") ?? []).map((badgeId: number) => {
const badgeItem =
amenitiesData?.data?.data?.results?.find(
(b: any) => b.id === badgeId,
);
return (
<Badge
key={badgeId}
variant="secondary"
className="px-3 py-1 text-sm flex items-center gap-2"
>
{badgeItem?.name || `Qulaylilar #${badgeId}`}
<button
type="button"
onClick={() => {
const current = form.getValues("amenities") ?? [];
form.setValue(
"amenities",
current.filter((b: number) => b !== badgeId),
);
}}
className="ml-1 text-muted-foreground hover:text-destructive"
>
<XIcon className="size-4" />
</button>
</Badge>
);
})}
</div>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full h-12 justify-between font-normal"
>
{t("Qulaylik tanlang")}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[250px]" align="start">
<Command>
<CommandList>
<CommandGroup heading={t("Mavjud qulayliklar")}>
{amenitiesData?.data?.data?.results?.map(
(item: any) => {
const currentBadges =
form.getValues("amenities") ?? [];
const selected = currentBadges.includes(item.id);
return (
<CommandItem
key={item.id}
onSelect={() => {
const selected = currentBadges.includes(
item.id,
);
if (selected) {
removeAmenity(item.id);
form.setValue(
"amenities",
currentBadges.filter(
(b: number) => b !== item.id,
),
);
} else {
addAmenity(item.id);
form.setValue("amenities", [
...currentBadges,
item.id,
]);
}
}}
className="flex items-center gap-2"
>
<SquareCheckBig
className={`size-4 ${
selected
? "text-green-500"
: "text-muted-foreground"
}`}
/>
{item.name}
</CommandItem>
);
},
)}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
{/* <FormField
control={form.control}
name="tarif"
render={() => (
<FormItem>
<Label className="text-md">{t("Tariflar")}</Label>
<div className="flex flex-col gap-3 mb-3">
{form.watch("tarif")?.map((tr, idx) => {
const transportItem = tariff?.data?.data?.results?.find(
(item: any) => item.id === tr.tariff,
);
return (
<div
key={idx}
className="flex items-center justify-between border rounded-lg p-2"
>
<div className="flex items-center gap-2">
<Badge
variant="secondary"
className="px-3 py-1 text-sm"
>
{transportItem?.name || `Tarif #${tr.tariff}`}
</Badge>
<Input
type="text"
inputMode="numeric"
placeholder="1 500 000"
value={tarifdisplayPrice[idx] || ""}
onChange={(e) => {
const raw = e.target.value.replace(/\D/g, "");
const num = Number(raw);
const currentTarifs =
form.getValues("tarif") || [];
const updatedTransport = currentTarifs.map(
(t, i) =>
i === idx ? { ...t, price: num } : t,
);
form.setValue("tarif", updatedTransport);
// Local holatda ham yangilaymiz:
const updatedPrices = [...tarifdisplayPrice];
updatedPrices[idx] = raw ? formatPrice(num) : "0";
setTarifDisplayPrice(updatedPrices);
}}
className="h-12 !text-md"
/>
</div>
<button
type="button"
onClick={() => {
const current = form.getValues("tarif") || [];
form.setValue(
"tarif",
current.filter((_, i) => i !== idx),
);
const updatedPrices = tarifdisplayPrice.filter(
(_, i) => i !== idx,
);
setTarifDisplayPrice(updatedPrices);
}}
className="ml-2 text-muted-foreground hover:text-destructive"
>
<XIcon className="size-4" />
</button>
</div>
);
})}
</div>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full h-12 justify-between font-normal"
>
{t("Tarifni tanlang")}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[250px]" align="start">
<Command>
<CommandList>
<CommandGroup heading={t("Mavjud tariflar")}>
{tariff?.data?.data?.results?.map((item: any) => {
const currentTarifs = form.getValues("tarif") || [];
const selected = currentTarifs.some(
(t) => t.tariff === item.id,
);
return (
<CommandItem
key={item.id}
onSelect={() => {
const current = form.getValues("tarif") || [];
if (selected) {
form.setValue(
"tarif",
current.filter(
(t) => t.tariff !== item.id,
),
);
} else {
form.setValue("tarif", [
...current,
{ tariff: item.id, price: 0 },
]);
}
}}
className="flex items-center gap-2"
>
<SquareCheckBig
className={`size-4 ${
selected
? "text-green-500"
: "text-muted-foreground"
}`}
/>
{item.name}
</CommandItem>
);
})}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/> */}
</div>
<div className="grid grid-cols-2 max-lg:grid-cols-1 gap-6 items-end">
<FormField
control={form.control}
name="transport"
render={() => (
<FormItem>
<Label className="text-md">{t("Transportlar")}</Label>
<div className="flex flex-col gap-3 mb-3">
{form.watch("transport")?.map((tr, idx) => {
const transportItem = transport?.data?.data?.results?.find(
(item: any) => item.id === tr.transport,
);
return (
<div
key={idx}
className="flex items-center justify-between border rounded-lg p-2"
>
<div className="flex items-center gap-2">
<Badge
variant="secondary"
className="px-3 py-1 text-sm"
>
{transportItem?.name ||
`Transport #${tr.transport}`}
</Badge>
{/* <Input
type="text"
inputMode="numeric"
placeholder="1 500 000"
value={transportPrices[idx] || ""}
onChange={(e) => {
const raw = e.target.value.replace(/\D/g, "");
const num = Number(raw);
const currentTransports =
form.getValues("transport") || [];
const updatedTransport = currentTransports.map(
(t, i) =>
i === idx ? { ...t, price: num } : t,
);
form.setValue("transport", updatedTransport);
// Local holatda ham yangilaymiz:
const updatedPrices = [...transportPrices];
updatedPrices[idx] = raw ? formatPrice(num) : "";
setTransportPrices(updatedPrices);
}}
className="h-12 !text-md"
/> */}
</div>
<button
type="button"
onClick={() => {
const current = form.getValues("transport") || [];
form.setValue(
"transport",
current.filter((_, i) => i !== idx),
);
const updatedPrices = transportPrices.filter(
(_, i) => i !== idx,
);
setTransportPrices(updatedPrices);
}}
className="ml-2 text-muted-foreground hover:text-destructive"
>
<XIcon className="size-4" />
</button>
</div>
);
})}
</div>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className="w-full h-12 justify-between font-normal"
>
{t("Transport tanlang")}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[250px]" align="start">
<Command>
<CommandList>
<CommandGroup heading={t("Mavjud transportlar")}>
{transport?.data?.data?.results?.map((item: any) => {
const currentTransports =
form.getValues("transport") || [];
const selected = currentTransports.some(
(t) => t.transport === item.id,
);
return (
<CommandItem
key={item.id}
onSelect={() => {
const current =
form.getValues("transport") || [];
if (selected) {
form.setValue(
"transport",
current.filter(
(t) => t.transport !== item.id,
),
);
} else {
form.setValue("transport", [
...current,
{ transport: item.id, price: 0 },
]);
}
}}
className="flex items-center gap-2"
>
<SquareCheckBig
className={`size-4 ${
selected
? "text-green-500"
: "text-muted-foreground"
}`}
/>
{item.name}
</CommandItem>
);
})}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="visa_required"
render={({ field }) => (
<FormItem className="space-y-3">
<Label>{t("Visa talab qilinadimi")}?</Label>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex gap-6"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="yes" id="visa_yes" />
<label htmlFor="visa_yes">{t("Ha")}</label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="no" id="visa_no" />
<label htmlFor="visa_no">{t("Yo'q")}</label>
</div>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<TicketsImagesModel
form={form}
name="banner"
multiple={false}
label={t("Banner")}
imageUrl={data?.data?.image_banner}
/>
<TicketsImagesModel
form={form}
name="images"
label={t("Qo'shimcha rasmlar")}
includeId={true}
imageUrl={
isEditMode && data?.data?.ticket_images
? data.data.ticket_images.map((img) => ({
id: img.id,
image: img.image,
}))
: undefined
}
/>
<FormField
control={form.control}
name="extra_service"
render={() => (
<FormItem>
<Label className="text-md">{t("Bepul xizmatlar")}</Label>
<div className="flex flex-col gap-4">
{/* Ko'rsatilayotgan xizmatlar */}
<div className="flex flex-wrap gap-2">
{(form.watch("extra_service") ?? []).map((item, idx) => (
<Badge
key={idx}
variant="secondary"
className="px-3 py-1 text-sm flex items-center gap-2"
>
<span>{item.name}</span>
<button
type="button"
onClick={() => {
const current = form.getValues("extra_service") ?? [];
form.setValue(
"extra_service",
current.filter((_, i) => i !== idx),
);
}}
className="ml-1 text-muted-foreground hover:text-destructive"
>
<XIcon className="size-4" />
</button>
</Badge>
))}
</div>
{/* Yangi xizmat qo'shish */}
<div className="flex gap-3 items-end flex-wrap">
<Input
id="extra_service_name"
placeholder={t("Xizmat nomi")}
className="h-12 !text-md flex-1 min-w-[200px]"
/>
<Input
id="extra_service_name_ru"
placeholder={t("Xizmat nomi") + " (ru)"}
className="h-12 !text-md flex-1 min-w-[200px]"
/>
<Button
type="button"
onClick={() => {
const nameInput = document.getElementById(
"extra_service_name",
) as HTMLInputElement;
const nameRuInput = document.getElementById(
"extra_service_name_ru",
) as HTMLInputElement;
if (nameInput.value && nameRuInput.value) {
const current = form.getValues("extra_service") ?? [];
form.setValue("extra_service", [
...current,
{
name: nameInput.value,
name_ru: nameRuInput.value,
},
]);
nameInput.value = "";
nameRuInput.value = "";
}
}}
className="h-12"
>
{t("Qo'shish")}
</Button>
</div>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="paid_extra_service"
render={() => (
<FormItem>
<Label className="text-md">{t("Pullik xizmatlar")}</Label>
<div className="flex flex-col gap-4">
{/* Ro'yxat */}
<div className="flex flex-wrap gap-2">
{(form.watch("paid_extra_service") || []).map((item, idx) => (
<Badge
key={idx}
variant="secondary"
className="px-3 py-1 text-sm flex items-center gap-2"
>
<span>
{item.name} {" "}
<strong>{formatPrice(item.price, true)}</strong>
</span>
<button
type="button"
onClick={() => {
const current =
form.getValues("paid_extra_service") ?? [];
form.setValue(
"paid_extra_service",
current.filter((_, i) => i !== idx),
);
}}
className="ml-1 text-muted-foreground hover:text-destructive"
>
<XIcon className="size-4" />
</button>
</Badge>
))}
</div>
{/* Qo'shish formasi */}
<div className="flex gap-3 items-end flex-wrap">
<Input
id="paid_service_name"
placeholder={t("Xizmat nomi")}
className="h-12 !text-md flex-1 min-w-[200px]"
/>
<Input
id="paid_service_name_ru"
placeholder={t("Xizmat nomi") + " (ru)"}
className="h-12 !text-md flex-1 min-w-[200px]"
/>
<Input
id="paid_service_price"
type="text"
inputMode="numeric"
placeholder="1 500 000"
className="h-12 !text-md w-[150px]"
onInput={(e) => {
const input = e.target as HTMLInputElement;
const raw = input.value.replace(/\D/g, "");
input.value = raw
? Number(raw).toLocaleString("ru-RU")
: "";
}}
/>
{/* Qo'shish tugmasi */}
<Button
type="button"
onClick={() => {
const nameInput = document.getElementById(
"paid_service_name",
) as HTMLInputElement;
const nameRuInput = document.getElementById(
"paid_service_name_ru",
) as HTMLInputElement;
const priceInput = document.getElementById(
"paid_service_price",
) as HTMLInputElement;
const raw = priceInput.value.replace(/\D/g, "");
const num = Number(raw);
if (
nameInput.value.trim() &&
nameRuInput.value.trim() &&
!isNaN(num)
) {
const current =
form.getValues("paid_extra_service") ?? [];
form.setValue("paid_extra_service", [
...current,
{
name: nameInput.value.trim(),
name_ru: nameRuInput.value.trim(),
price: num, // 🟢 0 ham bolishi mumkin
},
]);
// inputlarni tozalaymiz
nameInput.value = "";
nameRuInput.value = "";
priceInput.value = "";
}
}}
className="h-12"
>
{t("Qo'shish")}
</Button>
</div>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="hotel_info"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Mehmonxona haqida")}</Label>
<FormControl>
<Textarea
placeholder={t("Mehmonxona haqida")}
{...field}
className="min-h-48 max-h-60 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="hotel_info_ru"
render={({ field }) => (
<FormItem>
<Label className="text-md">{t("Mehmonxona haqida")} (ru)</Label>
<FormControl>
<Textarea
placeholder={t("Mehmonxona haqida") + " (ru)"}
{...field}
className="min-h-48 max-h-60 !text-md"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="hotel_services"
render={() => (
<FormItem>
<Label className="text-md">{t("Mehmonxona xizmatlari")}</Label>
<div className="flex flex-col gap-4">
<div className="flex flex-wrap gap-4">
{form.watch("hotel_services")?.map((service, idx) => (
<div
key={idx}
className="relative w-48 border rounded-xl overflow-hidden shadow-sm"
>
<img
src={
service.image instanceof File
? URL.createObjectURL(service.image)
: service.image
}
alt={service.title}
className="object-cover w-full h-32"
/>
<div className="p-2 text-center">
<p className="font-semibold">{service.title}</p>
<p className="text-sm text-muted-foreground line-clamp-2">
{service.description}
</p>
</div>
<button
type="button"
onClick={() => {
const current =
form.getValues("hotel_services") ?? [];
form.setValue(
"hotel_services",
current.filter((_, i) => i !== idx),
);
}}
className="absolute top-1 right-1 bg-white/80 rounded-full p-1 hover:bg-white shadow"
>
<XIcon className="size-4 text-destructive" />
</button>
</div>
))}
</div>
<div className="flex flex-col gap-3 border rounded-xl p-4 bg-muted/10">
<Label className="text-md font-semibold">
{t("Yangi xizmat qo'shish")}
</Label>
<Input
type="file"
id="hotel_service_image"
accept="image/*"
className="h-12"
/>
<Input
id="hotel_service_title"
placeholder={t("Xizmat nomi")}
className="h-12 !text-md"
/>
<Input
id="hotel_service_title_ru"
placeholder={t("Xizmat nomi") + " (ru)"}
className="h-12 !text-md"
/>
<Textarea
id="hotel_service_desc"
placeholder={t("Xizmat tavsifi")}
className="min-h-24 !text-md"
/>
<Textarea
id="hotel_service_desc_ru"
placeholder={t("Xizmat tavsifi") + " (ru)"}
className="min-h-24 !text-md"
/>
<Button
type="button"
onClick={() => {
const imgInput = document.getElementById(
"hotel_service_image",
) as HTMLInputElement;
const titleInput = document.getElementById(
"hotel_service_title",
) as HTMLInputElement;
const titleRuInput = document.getElementById(
"hotel_service_title_ru",
) as HTMLInputElement;
const descInput = document.getElementById(
"hotel_service_desc",
) as HTMLTextAreaElement;
const descRuInput = document.getElementById(
"hotel_service_desc_ru",
) as HTMLTextAreaElement;
const file = imgInput.files?.[0];
if (file && titleInput.value && descInput.value) {
const current = form.getValues("hotel_services") || [];
form.setValue("hotel_services", [
...current,
{
image: file,
title: titleInput.value,
title_ru: titleRuInput.value,
description: descInput.value,
desc_ru: descRuInput.value,
},
]);
imgInput.value = "";
titleInput.value = "";
titleRuInput.value = "";
descInput.value = "";
descRuInput.value = "";
}
}}
className="h-12"
>
{t("Qo'shish")}
</Button>
</div>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="hotel_meals_info"
render={({ field }) => (
<FormItem>
<Label className="text-md">
{t("Mehmonxona taomlari haqida")}
</Label>
<FormControl>
<Textarea
placeholder={t("Mehmonxona taomlari haqida")}
{...field}
className="min-h-48 max-h-60"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="hotel_meals_info_ru"
render={({ field }) => (
<FormItem>
<Label className="text-md">
{t("Mehmonxona taomlari haqida")} (ru)
</Label>
<FormControl>
<Textarea
placeholder={t("Mehmonxona taomlari haqida") + " (ru)"}
{...field}
className="min-h-48 max-h-60"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="hotel_meals"
render={() => (
<FormItem>
<Label className="text-md">{t("Mehmonxona taomlari")}</Label>
<div className="flex flex-col gap-4">
<div className="flex flex-wrap gap-4">
{form.watch("hotel_meals")?.map((service, idx) => (
<div
key={idx}
className="relative w-48 border rounded-xl overflow-hidden shadow-sm"
>
<img
src={
service.image instanceof File
? URL.createObjectURL(service.image)
: service.image
}
alt={service.title}
className="object-cover w-full h-32"
/>
<div className="p-2 text-center">
<p className="font-semibold">{service.title}</p>
<p className="text-sm text-muted-foreground line-clamp-2">
{service.description}
</p>
</div>
<button
type="button"
onClick={() => {
const current = form.getValues("hotel_meals") || [];
form.setValue(
"hotel_meals",
current.filter((_, i) => i !== idx),
);
}}
className="absolute top-1 right-1 bg-white/80 rounded-full p-1 hover:bg-white shadow"
>
<XIcon className="size-4 text-destructive" />
</button>
</div>
))}
</div>
<div className="flex flex-col gap-3 border rounded-xl p-4 bg-muted/10">
<Label className="text-md font-semibold">
{t("Mehmonxona taomlari ro'yxati")}
</Label>
<Input
type="file"
id="hotel_meals_image"
accept="image/*"
className="h-12"
/>
<Input
id="hotel_meals_title"
placeholder={t("Taom nomi")}
className="h-12 !text-md"
/>
<Input
id="hotel_meals_title_ru"
placeholder={t("Taom nomi") + " (ru)"}
className="h-12 !text-md"
/>
<Textarea
id="hotel_meals_desc"
placeholder={t("Taom tavsifi")}
className="min-h-24 !text-md"
/>
<Textarea
id="hotel_meals_desc_ru"
placeholder={t("Taom tavsifi") + " (ru)"}
className="min-h-24 !text-md"
/>
<Button
type="button"
onClick={() => {
const imgInput = document.getElementById(
"hotel_meals_image",
) as HTMLInputElement;
const titleInput = document.getElementById(
"hotel_meals_title",
) as HTMLInputElement;
const titleRuInput = document.getElementById(
"hotel_meals_title_ru",
) as HTMLInputElement;
const descInput = document.getElementById(
"hotel_meals_desc",
) as HTMLTextAreaElement;
const descRuInput = document.getElementById(
"hotel_meals_desc_ru",
) as HTMLTextAreaElement;
const file = imgInput.files?.[0];
if (file && titleInput.value && descInput.value) {
const current = form.getValues("hotel_meals") ?? [];
form.setValue("hotel_meals", [
...current,
{
image: file,
title: titleInput.value,
title_ru: titleRuInput.value,
description: descInput.value,
desc_ru: descRuInput.value,
},
]);
imgInput.value = "";
titleInput.value = "";
titleRuInput.value = "";
descInput.value = "";
descRuInput.value = "";
}
}}
className="h-12"
>
{t("Qo'shish")}
</Button>
</div>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ticket_itinerary"
render={() => {
// Helper function to get image source
const getImageSrc = (imageItem: any): string => {
// Agar object bo'lsa va image property bo'lsa
if (
imageItem &&
typeof imageItem === "object" &&
"image" in imageItem
) {
const img = imageItem.image;
// File bo'lsa
if (img instanceof File) {
return URL.createObjectURL(img);
}
// String (URL) bo'lsa
if (typeof img === "string") {
return img;
}
}
// Agar to'g'ridan-to'g'ri File bo'lsa
if (imageItem instanceof File) {
return URL.createObjectURL(imageItem);
}
// Agar string bo'lsa
if (typeof imageItem === "string") {
return imageItem;
}
// Default fallback
return "";
};
return (
<FormItem>
<Label className="text-md">{t("Yo'nalishlar")}</Label>
<div className="flex flex-col gap-4">
{/* mavjud yo'nalishlar */}
<div className="flex flex-wrap gap-4">
{form.watch("ticket_itinerary")?.map((item, idx) => (
<div
key={idx}
className="relative w-48 border rounded-xl overflow-hidden shadow-sm"
>
<img
src={getImageSrc(item.ticket_itinerary_image[0])}
alt={item.title}
className="object-cover w-full h-32"
/>
<div className="p-2 text-center">
<p className="font-semibold">{item.title}</p>
<p className="text-sm text-muted-foreground">
{item.ticket_itinerary_destinations[0]?.name}
</p>
<p className="text-sm text-muted-foreground">
{item.duration} {t("kun")}
</p>
</div>
<button
type="button"
onClick={() => {
const current =
form.getValues("ticket_itinerary") || [];
form.setValue(
"ticket_itinerary",
current.filter((_, i) => i !== idx),
);
}}
className="absolute top-1 right-1 bg-white/80 rounded-full p-1 hover:bg-white shadow"
>
<XIcon className="size-4 text-destructive" />
</button>
</div>
))}
</div>
{/* yangi yo'nalish qo'shish formasi */}
<div className="flex flex-col gap-3 border rounded-xl p-4 bg-muted/10">
<Label className="text-md font-semibold">
{t("Yo'nalish qo'shish")}
</Label>
<Input
type="file"
id="ticket_itinerary_image"
accept="image/*"
className="h-12"
/>
<Input
id="ticket_itinerary_title"
placeholder={t("Sarlavha")}
className="h-12 !text-md"
/>
<Input
id="ticket_itinerary_title_ru"
placeholder={t("Sarlavha") + " (ru)"}
className="h-12 !text-md"
/>
<Input
id="ticket_itinerary_duration"
type="number"
min={1}
placeholder={t("Davomiylik (kun)")}
className="h-12 !text-md"
/>
<Input
id="ticket_itinerary_destination"
placeholder={t("Manzil")}
className="h-12 !text-md"
/>
<Input
id="ticket_itinerary_destination_ru"
placeholder={t("Manzil") + " (ru)"}
className="h-12 !text-md"
/>
<Button
type="button"
onClick={() => {
const imgInput = document.getElementById(
"ticket_itinerary_image",
) as HTMLInputElement;
const titleInput = document.getElementById(
"ticket_itinerary_title",
) as HTMLInputElement;
const titleRuInput = document.getElementById(
"ticket_itinerary_title_ru",
) as HTMLInputElement;
const durationInput = document.getElementById(
"ticket_itinerary_duration",
) as HTMLInputElement;
const destInput = document.getElementById(
"ticket_itinerary_destination",
) as HTMLInputElement;
const destRuInput = document.getElementById(
"ticket_itinerary_destination_ru",
) as HTMLInputElement;
const file = imgInput.files?.[0];
const title = titleInput.value.trim();
const titleRu = titleRuInput.value.trim();
const duration = Number(durationInput.value);
const dest = destInput.value.trim();
const destRu = destRuInput.value.trim();
if (
file &&
title &&
titleRu &&
duration &&
dest &&
destRu
) {
const current =
form.getValues("ticket_itinerary") || [];
form.setValue("ticket_itinerary", [
...current,
{
ticket_itinerary_image: [{ image: file }],
title,
title_ru: titleRu,
duration,
ticket_itinerary_destinations: [
{ name: dest, name_ru: destRu },
],
},
]);
imgInput.value = "";
titleInput.value = "";
titleRuInput.value = "";
durationInput.value = "";
destInput.value = "";
destRuInput.value = "";
}
}}
className="h-12"
>
{t("Qo'shish")}
</Button>
</div>
</div>
<FormMessage />
</FormItem>
);
}}
/>
<div className="w-full flex justify-end">
<Button
type="submit"
className="mt-6 px-8 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-600 cursor-pointer"
>
{isPending || updatePending ? (
<Loader2 className="animate-spin" />
) : (
t("Saqlash")
)}
</Button>
</div>
</form>
</Form>
);
};
export default StepOne;