bug fix
This commit is contained in:
@@ -242,22 +242,22 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
<CreditCard size={18} />
|
<CreditCard size={18} />
|
||||||
{t("Bandlovlar va to‘lovlar")}
|
{t("Bandlovlar va to‘lovlar")}
|
||||||
</button>
|
</button>
|
||||||
{user === "moderator" ||
|
{(user === "moderator" ||
|
||||||
user === "buxgalter" ||
|
user === "buxgalter" ||
|
||||||
user === "admin" ||
|
user === "admin" ||
|
||||||
(user === "superuser" && (
|
user === "superuser") && (
|
||||||
<button
|
<button
|
||||||
className={`px-6 py-3 rounded-lg flex items-center gap-2 transition-all ${
|
className={`px-6 py-3 rounded-lg flex items-center gap-2 transition-all ${
|
||||||
tab === "agencies"
|
tab === "agencies"
|
||||||
? "bg-blue-600 text-white shadow-md"
|
? "bg-blue-600 text-white shadow-md"
|
||||||
: "text-gray-400 hover:bg-gray-700"
|
: "text-gray-400 hover:bg-gray-700"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setTab("agencies")}
|
onClick={() => setTab("agencies")}
|
||||||
>
|
>
|
||||||
<Users size={18} />
|
<Users size={18} />
|
||||||
{t("Agentlik hisobotlari")}
|
{t("Agentlik hisobotlari")}
|
||||||
</button>
|
</button>
|
||||||
))}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{tab === "bookings" && (
|
{tab === "bookings" && (
|
||||||
@@ -443,148 +443,146 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{user === "moderator" ||
|
{(user === "moderator" ||
|
||||||
user === "buxgalter" ||
|
user === "buxgalter" ||
|
||||||
user === "admin" ||
|
user === "admin" ||
|
||||||
(user === "superuser" && (
|
user === "superuser") && (
|
||||||
<>
|
<>
|
||||||
{tab === "agencies" && (
|
{tab === "agencies" && (
|
||||||
<>
|
<>
|
||||||
{agenctLoad ? (
|
{agenctLoad ? (
|
||||||
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 text-white gap-4 w-full">
|
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 text-white gap-4 w-full">
|
||||||
<Loader2 className="w-10 h-10 animate-spin text-cyan-400" />
|
<Loader2 className="w-10 h-10 animate-spin text-cyan-400" />
|
||||||
<p className="text-slate-400">
|
<p className="text-slate-400">
|
||||||
{t("Ma'lumotlar yuklanmoqda...")}
|
{t("Ma'lumotlar yuklanmoqda...")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : agencyError ? (
|
) : agencyError ? (
|
||||||
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 w-full text-center text-white gap-4">
|
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 w-full text-center text-white gap-4">
|
||||||
<AlertTriangle className="w-10 h-10 text-red-500" />
|
<AlertTriangle className="w-10 h-10 text-red-500" />
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
{t("Ma'lumotlarni yuklashda xatolik yuz berdi.")}
|
{t("Ma'lumotlarni yuklashda xatolik yuz berdi.")}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => agencyRef()}
|
onClick={() => agencyRef()}
|
||||||
className="bg-gradient-to-r from-blue-600 to-cyan-600 text-white rounded-lg px-5 py-2 hover:opacity-90"
|
className="bg-gradient-to-r from-blue-600 to-cyan-600 text-white rounded-lg px-5 py-2 hover:opacity-90"
|
||||||
>
|
>
|
||||||
{t("Qayta urinish")}
|
{t("Qayta urinish")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
agencyData &&
|
agencyData &&
|
||||||
!agencyError &&
|
!agencyError &&
|
||||||
!agenctLoad && (
|
!agenctLoad && (
|
||||||
<>
|
<>
|
||||||
<h2 className="text-xl font-bold mb-6">
|
<h2 className="text-xl font-bold mb-6">
|
||||||
{t("Partner Agencies")}
|
{t("Partner Agencies")}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{agencyData?.data.data.results.map((a) => (
|
{agencyData?.data.data.results.map((a) => (
|
||||||
<div
|
<div
|
||||||
key={a.id}
|
key={a.id}
|
||||||
className="bg-gray-800 p-6 rounded-xl shadow hover:shadow-md transition-all"
|
className="bg-gray-800 p-6 rounded-xl shadow hover:shadow-md transition-all"
|
||||||
>
|
>
|
||||||
<h2 className="text-xl font-bold mb-3 flex items-center gap-2 text-gray-100">
|
<h2 className="text-xl font-bold mb-3 flex items-center gap-2 text-gray-100">
|
||||||
<Users className="text-blue-400" size={20} />
|
<Users className="text-blue-400" size={20} />
|
||||||
{a.name}
|
{a.name}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||||
<div className="bg-green-900 p-3 rounded-lg">
|
<div className="bg-green-900 p-3 rounded-lg">
|
||||||
<p className="text-gray-400 text-sm">
|
<p className="text-gray-400 text-sm">
|
||||||
{t("Paid")}
|
{t("Paid")}
|
||||||
</p>
|
|
||||||
<p className="text-green-400 font-bold text-lg">
|
|
||||||
{formatPrice(a.paid, true)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-yellow-900 p-3 rounded-lg">
|
|
||||||
<p className="text-gray-400 text-sm">
|
|
||||||
{t("Pending")}
|
|
||||||
</p>
|
|
||||||
<p className="text-yellow-400 font-bold text-lg">
|
|
||||||
{formatPrice(a.pending, true)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4 text-gray-400">
|
|
||||||
<p className="text-sm mb-1">
|
|
||||||
{t("Bookings")}:{" "}
|
|
||||||
<span className="font-medium text-gray-100">
|
|
||||||
{a.ticket_sold_count}
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm">
|
<p className="text-green-400 font-bold text-lg">
|
||||||
{t("Destinations")}:{" "}
|
{formatPrice(a.paid, true)}
|
||||||
<span className="font-medium text-gray-100">
|
|
||||||
{a.ticket_count}
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="bg-yellow-900 p-3 rounded-lg">
|
||||||
<div className="flex gap-2">
|
<p className="text-gray-400 text-sm">
|
||||||
<Link
|
{t("Pending")}
|
||||||
to={`/travel/booking/${a.id}`}
|
</p>
|
||||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 flex items-center gap-1 transition-colors flex-1 justify-center"
|
<p className="text-yellow-400 font-bold text-lg">
|
||||||
>
|
{formatPrice(a.pending, true)}
|
||||||
<Eye className="w-4 h-4" /> {t("Ko'rish")}
|
</p>
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
<div className="flex justify-end gap-2 mt-5">
|
|
||||||
<button
|
|
||||||
disabled={currentPageAgency === 1}
|
|
||||||
onClick={() =>
|
|
||||||
setCurrentPageAgency((p) => Math.max(p - 1, 1))
|
|
||||||
}
|
|
||||||
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50 disabled:cursor-not-allowed transition-all hover:border-slate-500"
|
|
||||||
>
|
|
||||||
<ChevronLeft className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{[...Array(agencyData?.data.data.total_pages)].map(
|
<div className="mb-4 text-gray-400">
|
||||||
(_, i) => (
|
<p className="text-sm mb-1">
|
||||||
<button
|
{t("Bookings")}:{" "}
|
||||||
key={i}
|
<span className="font-medium text-gray-100">
|
||||||
onClick={() => setCurrentPageAgency(i + 1)}
|
{a.ticket_sold_count}
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
</span>
|
||||||
currentPageAgency === i + 1
|
</p>
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
<p className="text-sm">
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
{t("Destinations")}:{" "}
|
||||||
}`}
|
<span className="font-medium text-gray-100">
|
||||||
>
|
{a.ticket_count}
|
||||||
{i + 1}
|
</span>
|
||||||
</button>
|
</p>
|
||||||
),
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Link
|
||||||
|
to={`/travel/booking/${a.id}`}
|
||||||
|
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 flex items-center gap-1 transition-colors flex-1 justify-center"
|
||||||
|
>
|
||||||
|
<Eye className="w-4 h-4" /> {t("Ko'rish")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
<div className="flex justify-end gap-2 mt-5">
|
||||||
|
<button
|
||||||
|
disabled={currentPageAgency === 1}
|
||||||
|
onClick={() =>
|
||||||
|
setCurrentPageAgency((p) => Math.max(p - 1, 1))
|
||||||
|
}
|
||||||
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50 disabled:cursor-not-allowed transition-all hover:border-slate-500"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{[...Array(agencyData?.data.data.total_pages)].map((_, i) => (
|
||||||
<button
|
<button
|
||||||
disabled={
|
key={i}
|
||||||
currentPageAgency === agencyData?.data.data.total_pages
|
onClick={() => setCurrentPageAgency(i + 1)}
|
||||||
}
|
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
||||||
onClick={() =>
|
currentPageAgency === i + 1
|
||||||
setCurrentPageAgency((p) =>
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
||||||
Math.min(
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
||||||
p + 1,
|
}`}
|
||||||
agencyData ? agencyData?.data.data.total_pages : 0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50 disabled:cursor-not-allowed transition-all hover:border-slate-500"
|
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
{i + 1}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
))}
|
||||||
</>
|
|
||||||
)}
|
<button
|
||||||
</>
|
disabled={
|
||||||
))}
|
currentPageAgency === agencyData?.data.data.total_pages
|
||||||
|
}
|
||||||
|
onClick={() =>
|
||||||
|
setCurrentPageAgency((p) =>
|
||||||
|
Math.min(
|
||||||
|
p + 1,
|
||||||
|
agencyData ? agencyData?.data.data.total_pages : 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50 disabled:cursor-not-allowed transition-all hover:border-slate-500"
|
||||||
|
>
|
||||||
|
<ChevronRight className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -109,20 +109,20 @@ export const TourformSchema = z.object({
|
|||||||
tariff: z.number().min(1, { message: "Transport ID majburiy" }),
|
tariff: z.number().min(1, { message: "Transport ID majburiy" }),
|
||||||
price: z
|
price: z
|
||||||
.number()
|
.number()
|
||||||
.min(0, { message: "Narx 0 dan kichik bo‘lishi mumkin emas" }),
|
.min(0, { message: "Narx 0 dan kichik bo'lishi mumkin emas" }),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.min(1, { message: "Kamida bitta transport tanlang." }),
|
.optional(),
|
||||||
transport: z
|
transport: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
transport: z.number().min(1, { message: "Transport ID majburiy" }),
|
transport: z.number().min(1, { message: "Transport ID majburiy" }),
|
||||||
price: z
|
price: z
|
||||||
.number()
|
.number()
|
||||||
.min(0, { message: "Narx 0 dan kichik bo‘lishi mumkin emas" }),
|
.min(0, { message: "Narx 0 dan kichik bo'lishi mumkin emas" }),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.min(1, { message: "Kamida bitta transport tanlang." }),
|
.optional(),
|
||||||
banner: z.any().nullable(),
|
banner: z.any().nullable(),
|
||||||
images: z
|
images: z
|
||||||
.array(z.union([z.instanceof(File), z.string()]))
|
.array(z.union([z.instanceof(File), z.string()]))
|
||||||
@@ -199,7 +199,7 @@ export const TourformSchema = z.object({
|
|||||||
name_ru: z.string().min(1, { message: "Xizmat nomi (RU) majburiy" }),
|
name_ru: z.string().min(1, { message: "Xizmat nomi (RU) majburiy" }),
|
||||||
price: z
|
price: z
|
||||||
.number()
|
.number()
|
||||||
.min(0, { message: "Narx manfiy bo‘lishi mumkin emas." }),
|
.min(0, { message: "Narx manfiy bo'lishi mumkin emas." }),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export interface GetOneTours {
|
|||||||
{
|
{
|
||||||
price: number;
|
price: number;
|
||||||
transport: {
|
transport: {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
icon_name: string;
|
icon_name: string;
|
||||||
};
|
};
|
||||||
@@ -394,6 +395,7 @@ export interface GetDetailTours {
|
|||||||
transports: {
|
transports: {
|
||||||
price: number;
|
price: number;
|
||||||
transport: {
|
transport: {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
icon_name: string;
|
icon_name: string;
|
||||||
};
|
};
|
||||||
@@ -447,9 +449,7 @@ export interface GetDetailTours {
|
|||||||
];
|
];
|
||||||
tariff: [
|
tariff: [
|
||||||
{
|
{
|
||||||
tariff: {
|
tariff: number;
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
price: number;
|
price: number;
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -330,11 +330,11 @@ const StepOne = ({
|
|||||||
if (value.banner instanceof File) {
|
if (value.banner instanceof File) {
|
||||||
formData.append("image_banner", value.banner);
|
formData.append("image_banner", value.banner);
|
||||||
}
|
}
|
||||||
value.tarif.forEach((e, i) => {
|
value.tarif?.forEach((e, i) => {
|
||||||
formData.append(`tariff[${i}]tariff`, String(e.tariff));
|
formData.append(`tariff[${i}]tariff`, String(e.tariff));
|
||||||
formData.append(`tariff[${i}]price`, String(e.price));
|
formData.append(`tariff[${i}]price`, String(e.price));
|
||||||
});
|
});
|
||||||
value.transport.forEach((e, i) => {
|
value.transport?.forEach((e, i) => {
|
||||||
formData.append(`transports[${i}]transport`, String(e.transport));
|
formData.append(`transports[${i}]transport`, String(e.transport));
|
||||||
formData.append(`transports[${i}]price`, String(e.price));
|
formData.append(`transports[${i}]price`, String(e.price));
|
||||||
});
|
});
|
||||||
@@ -1005,11 +1005,10 @@ const StepOne = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const raw = e.target.value.replace(/\D/g, "");
|
const raw = e.target.value.replace(/\D/g, "");
|
||||||
const num = Number(raw);
|
const num = Number(raw);
|
||||||
const updatedTransport = form
|
const currentTarifs = form.getValues("tarif") || [];
|
||||||
.getValues("tarif")
|
const updatedTransport = currentTarifs.map((t, i) =>
|
||||||
.map((t, i) =>
|
i === idx ? { ...t, price: num } : t,
|
||||||
i === idx ? { ...t, price: num } : t,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
form.setValue("tarif", updatedTransport);
|
form.setValue("tarif", updatedTransport);
|
||||||
|
|
||||||
@@ -1024,7 +1023,7 @@ const StepOne = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const current = form.getValues("tarif");
|
const current = form.getValues("tarif") || [];
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"tarif",
|
"tarif",
|
||||||
current.filter((_, i) => i !== idx),
|
current.filter((_, i) => i !== idx),
|
||||||
@@ -1057,15 +1056,14 @@ const StepOne = ({
|
|||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandGroup heading={t("Mavjud tariflar")}>
|
<CommandGroup heading={t("Mavjud tariflar")}>
|
||||||
{tariff?.data?.data?.results?.map((item: any) => {
|
{tariff?.data?.data?.results?.map((item: any) => {
|
||||||
const selected = form
|
const currentTarifs = form.getValues("tarif") || [];
|
||||||
.getValues("tarif")
|
const selected = currentTarifs.some((t) => t.tariff === item.id);
|
||||||
.some((t) => t.tariff === item.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
const current = form.getValues("tarif");
|
const current = form.getValues("tarif") || [];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"tarif",
|
"tarif",
|
||||||
@@ -1139,11 +1137,10 @@ const StepOne = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const raw = e.target.value.replace(/\D/g, "");
|
const raw = e.target.value.replace(/\D/g, "");
|
||||||
const num = Number(raw);
|
const num = Number(raw);
|
||||||
const updatedTransport = form
|
const currentTransports = form.getValues("transport") || [];
|
||||||
.getValues("transport")
|
const updatedTransport = currentTransports.map((t, i) =>
|
||||||
.map((t, i) =>
|
i === idx ? { ...t, price: num } : t,
|
||||||
i === idx ? { ...t, price: num } : t,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
form.setValue("transport", updatedTransport);
|
form.setValue("transport", updatedTransport);
|
||||||
|
|
||||||
@@ -1158,7 +1155,7 @@ const StepOne = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const current = form.getValues("transport");
|
const current = form.getValues("transport") || [];
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"transport",
|
"transport",
|
||||||
current.filter((_, i) => i !== idx),
|
current.filter((_, i) => i !== idx),
|
||||||
@@ -1193,15 +1190,14 @@ const StepOne = ({
|
|||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandGroup heading={t("Mavjud transportlar")}>
|
<CommandGroup heading={t("Mavjud transportlar")}>
|
||||||
{transport?.data?.data?.results?.map((item: any) => {
|
{transport?.data?.data?.results?.map((item: any) => {
|
||||||
const selected = form
|
const currentTransports = form.getValues("transport") || [];
|
||||||
.getValues("transport")
|
const selected = currentTransports.some((t) => t.transport === item.id);
|
||||||
.some((t) => t.transport === item.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
const current = form.getValues("transport");
|
const current = form.getValues("transport") || [];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"transport",
|
"transport",
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ const Tours = ({ user }: { user: Role }) => {
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => navigate("/tours/create")}
|
onClick={() => navigate("/tours/create")}
|
||||||
variant="default"
|
variant="default"
|
||||||
disabled={user !== "tour_admin"}
|
// disabled={user !== "tour_admin"}
|
||||||
>
|
>
|
||||||
<PlusCircle className="w-5 h-5 mr-2" /> {t("Yangi tur qo'shish")}
|
<PlusCircle className="w-5 h-5 mr-2" /> {t("Yangi tur qo'shish")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user