api ulandi
This commit is contained in:
@@ -1,3 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
createHotel,
|
||||
hotelFeature,
|
||||
hotelFeatureType,
|
||||
hotelType,
|
||||
} from "@/pages/tours/lib/api";
|
||||
import { useTicketStore } from "@/pages/tours/lib/store";
|
||||
import type {
|
||||
GetOneTours,
|
||||
HotelFeatures,
|
||||
HotelFeaturesType,
|
||||
Type,
|
||||
} from "@/pages/tours/lib/type";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -15,45 +30,64 @@ import {
|
||||
SelectValue,
|
||||
} from "@/shared/ui/select";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { X } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import z from "zod";
|
||||
|
||||
const formSchema = z.object({
|
||||
title: z.string().min(2, {
|
||||
message: "Sarlavha kamida 2 ta belgidan iborat bo‘lishi kerak.",
|
||||
message: "Sarlavha kamida 2 ta belgidan iborat bo'lishi kerak",
|
||||
}),
|
||||
rating: z.number(),
|
||||
rating: z.number().min(1).max(5),
|
||||
mealPlan: z.string().min(1, { message: "Taom rejasi tanlanishi majburiy" }),
|
||||
hotelType: z
|
||||
.string()
|
||||
.min(1, { message: "Mehmonxona turi tanlanishi majburiy" }),
|
||||
.array(z.string())
|
||||
.min(1, { message: "Kamida 1 ta mehmonxona turi tanlang" }),
|
||||
hotelFeatures: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Kamida 1 ta xususiyat tanlanishi kerak" }),
|
||||
.min(1, { message: "Kamida 1 ta xususiyat tanlang" }),
|
||||
hotelFeaturesType: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Kamida 1 ta tur tanlang" }),
|
||||
});
|
||||
|
||||
const StepTwo = ({
|
||||
setStep,
|
||||
data,
|
||||
isEditMode,
|
||||
}: {
|
||||
setStep: Dispatch<SetStateAction<number>>;
|
||||
data: GetOneTours | undefined;
|
||||
isEditMode: boolean;
|
||||
}) => {
|
||||
const { amenities, id: ticketId } = useTicketStore();
|
||||
const navigator = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
title: "",
|
||||
rating: 3.0,
|
||||
mealPlan: "",
|
||||
hotelType: "",
|
||||
hotelType: [],
|
||||
hotelFeatures: [],
|
||||
hotelFeaturesType: [],
|
||||
},
|
||||
});
|
||||
|
||||
function onSubmit() {
|
||||
navigator("tours");
|
||||
}
|
||||
useEffect(() => {
|
||||
if (isEditMode && data?.data) {
|
||||
const tourData = data.data;
|
||||
|
||||
form.setValue("title", tourData.hotel_name);
|
||||
form.setValue("rating", Number(tourData.hotel_rating));
|
||||
form.setValue("mealPlan", tourData.hotel_meals);
|
||||
}
|
||||
}, [isEditMode, data, form]);
|
||||
|
||||
const mealPlans = [
|
||||
"Breakfast Only",
|
||||
@@ -61,24 +95,221 @@ const StepTwo = ({
|
||||
"Full Board",
|
||||
"All Inclusive",
|
||||
];
|
||||
const hotelTypes = ["Hotel", "Resort", "Guest House", "Apartment"];
|
||||
const [allHotelTypes, setAllHotelTypes] = useState<Type[]>([]);
|
||||
const [allHotelFeature, setAllHotelFeature] = useState<HotelFeatures[]>([]);
|
||||
const [allHotelFeatureType, setAllHotelFeatureType] = useState<
|
||||
HotelFeaturesType[]
|
||||
>([]);
|
||||
|
||||
const [featureTypeMapping, setFeatureTypeMapping] = useState<
|
||||
Record<string, string[]>
|
||||
>({});
|
||||
|
||||
const selectedHotelFeatures = form.watch("hotelFeatures");
|
||||
|
||||
useEffect(() => {
|
||||
const loadAll = async () => {
|
||||
try {
|
||||
let page = 1;
|
||||
let results: Type[] = [];
|
||||
let hasNext = true;
|
||||
|
||||
while (hasNext) {
|
||||
const res = await hotelType({ page, page_size: 50 });
|
||||
const data = res.data.data;
|
||||
results = [...results, ...data.results];
|
||||
hasNext = !!data.links.next;
|
||||
page++;
|
||||
}
|
||||
setAllHotelTypes(results);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
loadAll();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const loadAll = async () => {
|
||||
try {
|
||||
let page = 1;
|
||||
let results: HotelFeatures[] = [];
|
||||
let hasNext = true;
|
||||
|
||||
while (hasNext) {
|
||||
const res = await hotelFeature({ page, page_size: 50 });
|
||||
const data = res.data.data;
|
||||
results = [...results, ...data.results];
|
||||
hasNext = !!data.links.next;
|
||||
page++;
|
||||
}
|
||||
setAllHotelFeature(results);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
loadAll();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedHotelFeatures.length === 0) {
|
||||
setAllHotelFeatureType([]);
|
||||
setFeatureTypeMapping({});
|
||||
return;
|
||||
}
|
||||
|
||||
const loadAll = async () => {
|
||||
try {
|
||||
const selectedFeatureIds = selectedHotelFeatures
|
||||
.map((featureId) => Number(featureId))
|
||||
.filter((id) => !isNaN(id));
|
||||
|
||||
if (selectedFeatureIds.length === 0) return;
|
||||
|
||||
let allResults: HotelFeaturesType[] = [];
|
||||
const newMapping: Record<string, string[]> = {};
|
||||
|
||||
for (const featureId of selectedFeatureIds) {
|
||||
let page = 1;
|
||||
let hasNext = true;
|
||||
const featureTypes: string[] = [];
|
||||
|
||||
while (hasNext) {
|
||||
const res = await hotelFeatureType({
|
||||
page,
|
||||
page_size: 50,
|
||||
feature_type: featureId,
|
||||
});
|
||||
const data = res.data.data;
|
||||
allResults = [...allResults, ...data.results];
|
||||
|
||||
data.results.forEach((item: HotelFeaturesType) => {
|
||||
featureTypes.push(String(item.id));
|
||||
});
|
||||
|
||||
hasNext = !!data.links.next;
|
||||
page++;
|
||||
}
|
||||
|
||||
newMapping[String(featureId)] = featureTypes;
|
||||
}
|
||||
|
||||
const uniqueResults = allResults.filter(
|
||||
(item, index, self) =>
|
||||
index === self.findIndex((t) => t.id === item.id),
|
||||
);
|
||||
|
||||
setAllHotelFeatureType(uniqueResults);
|
||||
setFeatureTypeMapping(newMapping);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
loadAll();
|
||||
}, [selectedHotelFeatures]);
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: (body: FormData) => createHotel({ body }),
|
||||
onSuccess: () => {
|
||||
navigator("/tours");
|
||||
toast.success(t("Muvaffaqiyatli saqlandi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(t("Xatolik yuz berdi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const removeHotelType = (typeId: string) => {
|
||||
const current = form.getValues("hotelType");
|
||||
form.setValue(
|
||||
"hotelType",
|
||||
current.filter((val) => val !== typeId),
|
||||
);
|
||||
};
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
const formData = new FormData();
|
||||
formData.append("ticket", ticketId ? ticketId?.toString() : "");
|
||||
formData.append("name", data.title);
|
||||
formData.append("rating", String(data.rating));
|
||||
formData.append(
|
||||
"meal_plan",
|
||||
data.mealPlan === "Breakfast Only"
|
||||
? "breakfast"
|
||||
: data.mealPlan === "All Inclusive"
|
||||
? "all_inclusive"
|
||||
: data.mealPlan === "Half Board"
|
||||
? "half_board"
|
||||
: data.mealPlan === "Full Board"
|
||||
? "full_board"
|
||||
: "all_inclusive",
|
||||
);
|
||||
|
||||
data.hotelType.forEach((typeId) => {
|
||||
formData.append("hotel_type", typeId);
|
||||
});
|
||||
|
||||
data.hotelFeaturesType.forEach((typeId) => {
|
||||
formData.append("hotel_features", typeId);
|
||||
});
|
||||
|
||||
amenities.forEach((e, i) => {
|
||||
formData.append(`hotel_amenities[${i}]name`, e.name);
|
||||
formData.append(`hotel_amenities[${i}]name_ru`, e.name_ru);
|
||||
formData.append(`hotel_amenities[${i}]icon_name`, e.icon_name);
|
||||
});
|
||||
|
||||
mutate(formData);
|
||||
};
|
||||
|
||||
const removeHotelFeature = (featureId: string) => {
|
||||
const currentFeatures = form.getValues("hotelFeatures");
|
||||
const currentFeatureTypes = form.getValues("hotelFeaturesType");
|
||||
|
||||
const typesToRemove = featureTypeMapping[featureId] || [];
|
||||
|
||||
form.setValue(
|
||||
"hotelFeatures",
|
||||
currentFeatures.filter((val) => val !== featureId),
|
||||
);
|
||||
|
||||
form.setValue(
|
||||
"hotelFeaturesType",
|
||||
currentFeatureTypes.filter((val) => !typesToRemove.includes(val)),
|
||||
);
|
||||
};
|
||||
|
||||
const removeFeatureType = (typeId: string) => {
|
||||
const currentValues = form.getValues("hotelFeaturesType");
|
||||
form.setValue(
|
||||
"hotelFeaturesType",
|
||||
currentValues.filter((val) => val !== typeId),
|
||||
);
|
||||
};
|
||||
|
||||
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">
|
||||
<div className="grid grid-cols-2 gap-4 max-lg:grid-cols-1 items-end justify-end">
|
||||
{/* Mehmonxona nomi */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Mehmonxona nomi</Label>
|
||||
<Label>{t("Mehmonxona nomi")}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Toshkent - Dubay"
|
||||
{...field}
|
||||
className="h-12 !text-md"
|
||||
className="h-12"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -86,25 +317,36 @@ const StepTwo = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Mehmonxona rating */}
|
||||
{/* Rating */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="rating"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Mehmonxona raytingi</Label>
|
||||
<Label>{t("Mehmonxona reytingi")}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="3.0"
|
||||
{...field}
|
||||
className="h-12 !text-md"
|
||||
value={field.value}
|
||||
className="h-12"
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (/^\d*\.?\d*$/.test(val)) {
|
||||
// Faqat raqam va nuqta kiritishga ruxsat berish
|
||||
if (/^\d*\.?\d*$/.test(val) || val === "") {
|
||||
field.onChange(val);
|
||||
}
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val && !isNaN(parseFloat(val))) {
|
||||
// Agar 1 xonali bo'lsa, .0 qo'shish
|
||||
const num = parseFloat(val);
|
||||
if (val.indexOf(".") === -1) {
|
||||
field.onChange(num.toFixed(1));
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -116,31 +358,22 @@ const StepTwo = ({
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mealPlan"
|
||||
render={() => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Meal Plan</Label>
|
||||
<Label>{t("Taom rejasi")}</Label>
|
||||
<FormControl>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="mealPlan"
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<SelectTrigger className="!h-12 w-full">
|
||||
<SelectValue placeholder="Taom rejasini tanlang" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{mealPlans.map((plan) => (
|
||||
<SelectItem key={plan} value={plan}>
|
||||
{plan}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger className="!h-12 w-full">
|
||||
<SelectValue placeholder={t("Tanlang")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{mealPlans.map((plan) => (
|
||||
<SelectItem key={plan} value={plan}>
|
||||
{t(plan)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -151,134 +384,232 @@ const StepTwo = ({
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="hotelType"
|
||||
render={() => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Mehmonxona turi</Label>
|
||||
<Label>{t("Mehmonxona turlari")}</Label>
|
||||
<FormControl>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="hotelType"
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<SelectTrigger className="!h-12 w-full">
|
||||
<SelectValue placeholder="Mehmonxona turini tanlang" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{hotelTypes.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{type}
|
||||
<div className="space-y-2">
|
||||
{field.value.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 p-2 border rounded-md">
|
||||
{field.value.map((selectedValue) => {
|
||||
const selectedItem = allHotelTypes.find(
|
||||
(item) => String(item.id) === selectedValue,
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={selectedValue}
|
||||
className="flex items-center gap-1 bg-purple-600 text-white px-3 py-1 rounded-md text-sm"
|
||||
>
|
||||
<span>{selectedItem?.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeHotelType(selectedValue)}
|
||||
className="hover:bg-purple-700 rounded-full p-0.5"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Select
|
||||
value=""
|
||||
onValueChange={(value) => {
|
||||
if (!field.value.includes(value)) {
|
||||
field.onChange([...field.value, value]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="!h-12 w-full">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
field.value.length > 0
|
||||
? t("Yana tanlang...")
|
||||
: t("Tanlang")
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{allHotelTypes
|
||||
.filter(
|
||||
(type) => !field.value.includes(String(type.id)),
|
||||
)
|
||||
.map((type) => (
|
||||
<SelectItem key={type.id} value={String(type.id)}>
|
||||
{type.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* <FormField
|
||||
{/* Hotel Features */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="hotelFeatures"
|
||||
render={() => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Mehmonxona qulayliklar</Label>
|
||||
<Label>{t("Mehmonxona xususiyatlari")}</Label>
|
||||
<FormControl>
|
||||
<div className="space-y-2">
|
||||
{field.value.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 p-2 border rounded-md">
|
||||
{field.value.map((selectedValue) => {
|
||||
const selectedItem = allHotelFeature.find(
|
||||
(item) => String(item.id) === selectedValue,
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={selectedValue}
|
||||
className="flex items-center gap-1 bg-blue-600 text-white px-3 py-1 rounded-md text-sm"
|
||||
>
|
||||
<span>
|
||||
{selectedItem?.hotel_feature_type_name}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
removeHotelFeature(selectedValue)
|
||||
}
|
||||
className="hover:bg-blue-700 rounded-full p-0.5"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{form.watch("amenities").map((item, idx) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const Icon =
|
||||
(LucideIcons as any)[item.icon_name] || XIcon;
|
||||
return (
|
||||
<Badge
|
||||
key={idx}
|
||||
variant="secondary"
|
||||
className="px-3 py-1 text-sm flex items-center gap-2"
|
||||
>
|
||||
<Icon className="size-4" />
|
||||
<span>{item.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const current = form.getValues("amenities");
|
||||
form.setValue(
|
||||
"amenities",
|
||||
current.filter((_, i: number) => i !== idx),
|
||||
);
|
||||
}}
|
||||
className="ml-1 text-muted-foreground hover:text-destructive"
|
||||
>
|
||||
<XIcon className="size-4" />
|
||||
</button>
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<IconSelect
|
||||
setSelectedIcon={setSelectedIcon}
|
||||
selectedIcon={selectedIcon}
|
||||
/>
|
||||
|
||||
<Input
|
||||
id="amenity_name"
|
||||
placeholder="Qulaylik nomi (masalan: Wi-Fi)"
|
||||
className="h-12 !text-md flex-1"
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const nameInput = document.getElementById(
|
||||
"amenity_name",
|
||||
) as HTMLInputElement;
|
||||
|
||||
if (selectedIcon && nameInput.value) {
|
||||
const current = form.getValues("amenities");
|
||||
form.setValue("amenities", [
|
||||
...current,
|
||||
{
|
||||
icon_name: selectedIcon,
|
||||
name: nameInput.value,
|
||||
},
|
||||
]);
|
||||
nameInput.value = "";
|
||||
setSelectedIcon("");
|
||||
<Select
|
||||
value=""
|
||||
onValueChange={(value) => {
|
||||
if (!field.value.includes(value)) {
|
||||
field.onChange([...field.value, value]);
|
||||
}
|
||||
}}
|
||||
className="h-12"
|
||||
>
|
||||
Qo‘shish
|
||||
</Button>
|
||||
<SelectTrigger className="!h-12 w-full">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
field.value.length > 0
|
||||
? t("Yana tanlang...")
|
||||
: t("Tanlang")
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{allHotelFeature
|
||||
.filter(
|
||||
(type) => !field.value.includes(String(type.id)),
|
||||
)
|
||||
.map((type) => (
|
||||
<SelectItem key={type.id} value={String(type.id)}>
|
||||
{type.hotel_feature_type_name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/> */}
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setStep(1)}
|
||||
className="mt-6 px-6 py-3 bg-gray-600 text-white rounded-md"
|
||||
>
|
||||
Ortga
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="mt-6 px-6 py-3 bg-blue-600 text-white rounded-md"
|
||||
>
|
||||
Saqlash
|
||||
</button>
|
||||
/>
|
||||
|
||||
{/* Hotel Feature Type */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="hotelFeaturesType"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label>{t("Xususiyat turlari")}</Label>
|
||||
<FormControl>
|
||||
<div className="space-y-2">
|
||||
{field.value.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 p-2 border rounded-md">
|
||||
{field.value.map((selectedValue) => {
|
||||
const selectedItem = allHotelFeatureType.find(
|
||||
(item) => String(item.id) === selectedValue,
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={selectedValue}
|
||||
className="flex items-center gap-1 bg-green-600 text-white px-3 py-1 rounded-md text-sm"
|
||||
>
|
||||
<span>{selectedItem?.feature_name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeFeatureType(selectedValue)}
|
||||
className="hover:bg-green-700 rounded-full p-0.5"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Select
|
||||
value=""
|
||||
onValueChange={(value) => {
|
||||
if (!field.value.includes(value)) {
|
||||
field.onChange([...field.value, value]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="!h-12 w-full">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
selectedHotelFeatures.length === 0
|
||||
? t("Avval xususiyat tanlang")
|
||||
: field.value.length > 0
|
||||
? t("Yana tanlang...")
|
||||
: t("Tanlang")
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{allHotelFeatureType.length === 0 ? (
|
||||
<div className="p-2 text-sm text-gray-500">
|
||||
{t("Avval mehmonxona xususiyatini tanlang")}
|
||||
</div>
|
||||
) : (
|
||||
allHotelFeatureType
|
||||
.filter(
|
||||
(type) => !field.value.includes(String(type.id)),
|
||||
)
|
||||
.map((type) => (
|
||||
<SelectItem key={type.id} value={String(type.id)}>
|
||||
{type.feature_name}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="mt-6 px-6 py-3 bg-blue-600 text-white rounded-md disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isPending ? t("Yuklanmoqda...") : t("Saqlash")}
|
||||
</button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user