api ulandi

This commit is contained in:
Samandar Turgunboyev
2025-10-29 18:41:59 +05:00
parent a9e99f9755
commit 2d0285dafc
64 changed files with 6319 additions and 2352 deletions

View File

@@ -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 toldirish
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 boyicha)
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>