api ulandi
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
import {
|
||||
createHotel,
|
||||
editHotel,
|
||||
getHotel,
|
||||
hotelFeature,
|
||||
hotelFeatureType,
|
||||
hotelType,
|
||||
@@ -30,7 +32,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/shared/ui/select";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { X } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -64,9 +66,18 @@ const StepTwo = ({
|
||||
isEditMode: boolean;
|
||||
}) => {
|
||||
const { amenities, id: ticketId } = useTicketStore();
|
||||
const navigator = useNavigate();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// 🧩 Query - Hotel detail
|
||||
const { data: hotelDetail } = useQuery({
|
||||
queryKey: ["hotel_detail", data?.data.id],
|
||||
queryFn: () => getHotel(data?.data.id!),
|
||||
select: (res) => res.data.data.results,
|
||||
enabled: !!data?.data.id,
|
||||
});
|
||||
|
||||
// 🧩 React Hook Form
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
@@ -79,78 +90,94 @@ const StepTwo = ({
|
||||
},
|
||||
});
|
||||
|
||||
// 🧩 Edit holati uchun formni to‘ldirish
|
||||
useEffect(() => {
|
||||
if (isEditMode && data?.data) {
|
||||
const tourData = data.data;
|
||||
if (isEditMode && hotelDetail?.[0]) {
|
||||
const hotel = hotelDetail[0];
|
||||
|
||||
form.setValue("title", tourData.hotel_name);
|
||||
form.setValue("rating", tourData.hotel_rating);
|
||||
form.setValue("mealPlan", tourData.hotel_meals);
|
||||
form.setValue("title", hotel.name);
|
||||
form.setValue("rating", String(hotel.rating));
|
||||
|
||||
const mealPlan =
|
||||
hotel.meal_plan === "breakfast"
|
||||
? "Breakfast Only"
|
||||
: hotel.meal_plan === "all_inclusive"
|
||||
? "All Inclusive"
|
||||
: hotel.meal_plan === "half_board"
|
||||
? "Half Board"
|
||||
: hotel.meal_plan === "full_board"
|
||||
? "Full Board"
|
||||
: "All Inclusive";
|
||||
|
||||
form.setValue("mealPlan", mealPlan);
|
||||
|
||||
form.setValue(
|
||||
"hotelType",
|
||||
hotel.hotel_type?.map((t) => String(t.id)) ?? [],
|
||||
);
|
||||
form.setValue(
|
||||
"hotelFeatures",
|
||||
hotel.hotel_features?.map((f) => String(f.feature_type.id)) ?? [],
|
||||
);
|
||||
form.setValue("hotelFeaturesType", [
|
||||
...new Set(hotel.hotel_features?.map((f) => String(f.id)) ?? []),
|
||||
]);
|
||||
}
|
||||
}, [isEditMode, data, form]);
|
||||
}, [isEditMode, hotelDetail, form, data]);
|
||||
|
||||
const mealPlans = [
|
||||
"Breakfast Only",
|
||||
"Half Board",
|
||||
"Full Board",
|
||||
"All Inclusive",
|
||||
];
|
||||
// 🧩 Select ma'lumotlari
|
||||
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");
|
||||
|
||||
// 🔹 Hotel Types yuklash
|
||||
useEffect(() => {
|
||||
const loadAll = async () => {
|
||||
try {
|
||||
let page = 1;
|
||||
let results: Type[] = [];
|
||||
let hasNext = true;
|
||||
const loadHotelTypes = async () => {
|
||||
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);
|
||||
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);
|
||||
};
|
||||
loadAll();
|
||||
loadHotelTypes();
|
||||
}, []);
|
||||
|
||||
// 🔹 Hotel Features yuklash
|
||||
useEffect(() => {
|
||||
const loadAll = async () => {
|
||||
try {
|
||||
let page = 1;
|
||||
let results: HotelFeatures[] = [];
|
||||
let hasNext = true;
|
||||
const loadHotelFeatures = async () => {
|
||||
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);
|
||||
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);
|
||||
};
|
||||
loadAll();
|
||||
loadHotelFeatures();
|
||||
}, []);
|
||||
|
||||
// 🔹 Feature type'larni yuklash (tanlangan feature bo‘yicha)
|
||||
useEffect(() => {
|
||||
if (selectedHotelFeatures.length === 0) {
|
||||
setAllHotelFeatureType([]);
|
||||
@@ -158,107 +185,118 @@ const StepTwo = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const loadAll = async () => {
|
||||
try {
|
||||
const selectedFeatureIds = selectedHotelFeatures
|
||||
.map((featureId) => Number(featureId))
|
||||
.filter((id) => !isNaN(id));
|
||||
const loadFeatureTypes = async () => {
|
||||
const selectedIds = selectedHotelFeatures.map(Number).filter(Boolean);
|
||||
let allResults: HotelFeaturesType[] = [];
|
||||
const mapping: Record<string, string[]> = {};
|
||||
|
||||
if (selectedFeatureIds.length === 0) return;
|
||||
for (const id of selectedIds) {
|
||||
let page = 1;
|
||||
let hasNext = true;
|
||||
const featureTypes: string[] = [];
|
||||
|
||||
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;
|
||||
while (hasNext) {
|
||||
const res = await hotelFeatureType({
|
||||
page,
|
||||
page_size: 50,
|
||||
feature_type: id,
|
||||
});
|
||||
const data = res.data.data;
|
||||
allResults = [...allResults, ...data.results];
|
||||
data.results.forEach((ft: HotelFeaturesType) =>
|
||||
featureTypes.push(String(ft.id)),
|
||||
);
|
||||
hasNext = !!data.links.next;
|
||||
page++;
|
||||
}
|
||||
|
||||
const uniqueResults = allResults.filter(
|
||||
(item, index, self) =>
|
||||
index === self.findIndex((t) => t.id === item.id),
|
||||
);
|
||||
|
||||
setAllHotelFeatureType(uniqueResults);
|
||||
setFeatureTypeMapping(newMapping);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
mapping[String(id)] = featureTypes;
|
||||
}
|
||||
|
||||
const uniqueResults = allResults.filter(
|
||||
(v, i, a) => a.findIndex((t) => t.id === v.id) === i,
|
||||
);
|
||||
|
||||
setAllHotelFeatureType(uniqueResults);
|
||||
setFeatureTypeMapping(mapping);
|
||||
};
|
||||
|
||||
loadAll();
|
||||
loadFeatureTypes();
|
||||
}, [selectedHotelFeatures]);
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: (body: FormData) => createHotel({ body }),
|
||||
onSuccess: () => {
|
||||
navigator("/tours");
|
||||
toast.success(t("Muvaffaqiyatli saqlandi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
toast.success(t("Muvaffaqiyatli saqlandi"));
|
||||
navigate("/tours");
|
||||
},
|
||||
onError: () => {
|
||||
onError: () =>
|
||||
toast.error(t("Xatolik yuz berdi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const removeHotelType = (typeId: string) => {
|
||||
const current = form.getValues("hotelType");
|
||||
const { mutate: edit, isPending: editPending } = useMutation({
|
||||
mutationFn: ({ body, id }: { id: number; body: FormData }) =>
|
||||
editHotel({ body, id }),
|
||||
onSuccess: () => {
|
||||
toast.success(t("Muvaffaqiyatli saqlandi"));
|
||||
navigate("/tours");
|
||||
},
|
||||
onError: () =>
|
||||
toast.error(t("Xatolik yuz berdi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
}),
|
||||
});
|
||||
|
||||
const removeHotelType = (id: string) =>
|
||||
form.setValue(
|
||||
"hotelType",
|
||||
current.filter((val) => val !== typeId),
|
||||
form.getValues("hotelType").filter((v) => v !== id),
|
||||
);
|
||||
|
||||
const removeHotelFeature = (id: string) => {
|
||||
const current = form.getValues("hotelFeatures");
|
||||
const types = form.getValues("hotelFeaturesType");
|
||||
const toRemove = featureTypeMapping[id] || [];
|
||||
|
||||
form.setValue(
|
||||
"hotelFeatures",
|
||||
current.filter((v) => v !== id),
|
||||
);
|
||||
form.setValue(
|
||||
"hotelFeaturesType",
|
||||
types.filter((v) => !toRemove.includes(v)),
|
||||
);
|
||||
};
|
||||
|
||||
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",
|
||||
const removeFeatureType = (id: string) =>
|
||||
form.setValue(
|
||||
"hotelFeaturesType",
|
||||
form.getValues("hotelFeaturesType").filter((v) => v !== id),
|
||||
);
|
||||
|
||||
data.hotelType.forEach((typeId) => {
|
||||
formData.append("hotel_type", typeId);
|
||||
});
|
||||
// 🧩 Submit
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
const formData = new FormData();
|
||||
|
||||
data.hotelFeaturesType.forEach((typeId) => {
|
||||
formData.append("hotel_features", typeId);
|
||||
});
|
||||
formData.append("ticket", ticketId ? String(ticketId) : "");
|
||||
formData.append("name", data.title);
|
||||
formData.append("rating", data.rating);
|
||||
|
||||
const mealPlan =
|
||||
data.mealPlan === "Breakfast Only"
|
||||
? "breakfast"
|
||||
: data.mealPlan === "Half Board"
|
||||
? "half_board"
|
||||
: data.mealPlan === "Full Board"
|
||||
? "full_board"
|
||||
: "all_inclusive";
|
||||
formData.append("meal_plan", mealPlan);
|
||||
|
||||
data.hotelType.forEach((id) => formData.append("hotel_type", id));
|
||||
data.hotelFeatures.forEach((id) => formData.append("hotel_features", id));
|
||||
|
||||
amenities.forEach((e, i) => {
|
||||
formData.append(`hotel_amenities[${i}]name`, e.name);
|
||||
@@ -266,33 +304,22 @@ const StepTwo = ({
|
||||
formData.append(`hotel_amenities[${i}]icon_name`, e.icon_name);
|
||||
});
|
||||
|
||||
mutate(formData);
|
||||
if (isEditMode && hotelDetail) {
|
||||
edit({
|
||||
body: formData,
|
||||
id: Number(hotelDetail[0].id),
|
||||
});
|
||||
} else {
|
||||
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),
|
||||
);
|
||||
};
|
||||
const mealPlans = [
|
||||
"Breakfast Only",
|
||||
"Half Board",
|
||||
"Full Board",
|
||||
"All Inclusive",
|
||||
];
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
@@ -465,6 +492,8 @@ const StepTwo = ({
|
||||
const selectedItem = allHotelFeature.find(
|
||||
(item) => String(item.id) === selectedValue,
|
||||
);
|
||||
console.log(allHotelFeature);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={selectedValue}
|
||||
@@ -608,7 +637,7 @@ const StepTwo = ({
|
||||
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")}
|
||||
{isPending || editPending ? t("Yuklanmoqda...") : t("Saqlash")}
|
||||
</button>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user