added pagination params page
This commit is contained in:
@@ -26,16 +26,23 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export default function TourAgenciesPage() {
|
export default function TourAgenciesPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const itemsPerPage = 4;
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [currentPage, setCurrentPage] = useState(initialPage);
|
||||||
|
const itemsPerPage = 6;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setCurrentPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const { data, refetch, isLoading, isError } = useQuery({
|
const { data, refetch, isLoading, isError } = useQuery({
|
||||||
queryKey: ["all_agency", currentPage],
|
queryKey: ["all_agency", currentPage],
|
||||||
queryFn: () => getAllAgency({ page: currentPage, page_size: itemsPerPage }),
|
queryFn: () => getAllAgency({ page: currentPage, page_size: itemsPerPage }),
|
||||||
@@ -309,37 +316,40 @@ export default function TourAgenciesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={currentPage === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(currentPage - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(data?.data.data.total_pages)].map((_, i) => (
|
{[...Array(data?.data.data.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
currentPage === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === data?.data.data.total_pages}
|
disabled={currentPage === data?.data.data.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(
|
||||||
Math.min(p + 1, data ? data?.data.data.total_pages : 0),
|
Math.min(currentPage + 1, data?.data.data.total_pages ?? 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -25,17 +25,26 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const EmployeesManagement = () => {
|
const EmployeesManagement = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ["employees", currentPage],
|
queryKey: ["employees", page],
|
||||||
queryFn: () => getAllEmployees({ page: currentPage, page_size: 10 }),
|
queryFn: () => getAllEmployees({ page: page, page_size: 12 }),
|
||||||
select: (data) => data.data.data,
|
select: (data) => data.data.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -193,37 +202,38 @@ const EmployeesManagement = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(data?.total_pages || 0)].map((_, i) => (
|
{[...Array(data?.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === data?.total_pages}
|
disabled={page === data?.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(Math.min(page + 1, data?.total_pages ?? 1))
|
||||||
Math.min(p + 1, 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import {
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
@@ -67,7 +68,10 @@ const faqForm = z.object({
|
|||||||
|
|
||||||
const Faq = () => {
|
const Faq = () => {
|
||||||
const [activeTab, setActiveTab] = useState<string>("");
|
const [activeTab, setActiveTab] = useState<string>("");
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
const [editFaq, setEditFaq] = useState<number | null>(null);
|
const [editFaq, setEditFaq] = useState<number | null>(null);
|
||||||
@@ -75,6 +79,11 @@ const Faq = () => {
|
|||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const loaderRef = useRef<HTMLDivElement>(null);
|
const loaderRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
// Infinite scroll uchun useInfiniteQuery
|
// Infinite scroll uchun useInfiniteQuery
|
||||||
const {
|
const {
|
||||||
data: categoryData,
|
data: categoryData,
|
||||||
@@ -96,20 +105,16 @@ const Faq = () => {
|
|||||||
initialPageParam: 1,
|
initialPageParam: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentPage(1);
|
|
||||||
}, [activeTab]);
|
|
||||||
|
|
||||||
const category = useMemo(() => {
|
const category = useMemo(() => {
|
||||||
return categoryData?.pages.flatMap((page) => page.data.data.results) ?? [];
|
return categoryData?.pages.flatMap((page) => page.data.data.results) ?? [];
|
||||||
}, [categoryData]);
|
}, [categoryData]);
|
||||||
|
|
||||||
const { data: faq } = useQuery({
|
const { data: faq } = useQuery({
|
||||||
queryKey: ["all_faq", activeTab, currentPage],
|
queryKey: ["all_faq", activeTab, page],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
return getAllFaq({
|
return getAllFaq({
|
||||||
page: currentPage,
|
page,
|
||||||
page_size: 10,
|
page_size: 1,
|
||||||
category: Number(activeTab),
|
category: Number(activeTab),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -301,7 +306,14 @@ const Faq = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
<Tabs
|
||||||
|
value={activeTab}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
setActiveTab(value);
|
||||||
|
updatePage(1);
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<TabsList
|
<TabsList
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
@@ -390,35 +402,36 @@ const Faq = () => {
|
|||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(faq?.total_pages)].map((_, i) => (
|
{[...Array(faq?.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === faq?.total_pages}
|
disabled={page === faq?.total_pages}
|
||||||
onClick={() =>
|
onClick={() => updatePage(Math.min(page + 1, faq?.total_pages ?? 1))}
|
||||||
setCurrentPage((p) => Math.min(p + 1, faq ? faq.total_pages : 0))
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
}
|
|
||||||
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" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import {
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
@@ -55,13 +56,22 @@ const categoryFormSchema = z.object({
|
|||||||
|
|
||||||
const FaqCategory = () => {
|
const FaqCategory = () => {
|
||||||
const [deleteId, setDeleteId] = useState<number | null>(null);
|
const [deleteId, setDeleteId] = useState<number | null>(null);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
const [categories, setCategories] = useState<number | null>(null);
|
const [categories, setCategories] = useState<number | null>(null);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const { data: category } = useQuery({
|
const { data: category } = useQuery({
|
||||||
queryKey: ["all_faqcategory", currentPage],
|
queryKey: ["all_faqcategory", page],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
return getAllFaqCategory({ page: currentPage, page_size: 10 });
|
return getAllFaqCategory({ page: page, page_size: 20 });
|
||||||
},
|
},
|
||||||
select(data) {
|
select(data) {
|
||||||
return data.data.data;
|
return data.data.data;
|
||||||
@@ -255,37 +265,38 @@ const FaqCategory = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(category?.total_pages)].map((_, i) => (
|
{[...Array(category?.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === category?.total_pages}
|
disabled={page === category?.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(Math.min(page + 1, category?.total_pages ?? 1))
|
||||||
Math.min(p + 1, category ? category.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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -40,28 +40,23 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const tabParam = searchParams.get("tab") as "bookings" | "agencies" | null;
|
const tabParam = searchParams.get("tab") as "bookings" | "agencies" | null;
|
||||||
const pageParam = Number(searchParams.get("page")) || 1;
|
const pageParam = Number(searchParams.get("page")) || 1;
|
||||||
|
const filterParam = String(searchParams.get("filter")) || "";
|
||||||
const pageAgencyParam = Number(searchParams.get("page_agency")) || 1;
|
const pageAgencyParam = Number(searchParams.get("page_agency")) || 1;
|
||||||
const [currentPage, setCurrentPage] = useState(pageParam);
|
const [currentPage, setCurrentPage] = useState(pageParam);
|
||||||
const [currentPageAgency, setCurrentPageAgency] = useState(pageAgencyParam);
|
const [currentPageAgency, setCurrentPageAgency] = useState(pageAgencyParam);
|
||||||
const [tab, setTab] = useState<"bookings" | "agencies">(
|
const [tab, setTab] = useState<"bookings" | "agencies">(
|
||||||
tabParam ?? "bookings",
|
tabParam ?? "bookings",
|
||||||
);
|
);
|
||||||
const [filterStatus, setFilterStatus] = useState<
|
const [filterStatus, setFilterStatus] = useState(filterParam);
|
||||||
| ""
|
|
||||||
| "pending_payment"
|
|
||||||
| "pending_confirmation"
|
|
||||||
| "confirmed"
|
|
||||||
| "completed"
|
|
||||||
| "cancelled"
|
|
||||||
>("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSearchParams({
|
setSearchParams({
|
||||||
tab,
|
tab,
|
||||||
page: String(currentPage),
|
page: String(currentPage),
|
||||||
page_agency: String(currentPageAgency),
|
page_agency: String(currentPageAgency),
|
||||||
|
filter: String(filterStatus),
|
||||||
});
|
});
|
||||||
}, [tab, currentPage, currentPageAgency, setSearchParams]);
|
}, [tab, currentPage, currentPageAgency, setSearchParams, filterStatus]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tabParam && tabParam !== tab) {
|
if (tabParam && tabParam !== tab) {
|
||||||
@@ -69,18 +64,19 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
}
|
}
|
||||||
}, [tabParam]);
|
}, [tabParam]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentPage(1);
|
|
||||||
setCurrentPageAgency(1);
|
|
||||||
}, [filterStatus, tab]);
|
|
||||||
|
|
||||||
const { data, isLoading, isError, refetch } = useQuery({
|
const { data, isLoading, isError, refetch } = useQuery({
|
||||||
queryKey: ["list_order_user", currentPage, filterStatus],
|
queryKey: ["list_order_user", currentPage, filterStatus],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getAllOrder({
|
getAllOrder({
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
page_size: 10,
|
page_size: 12,
|
||||||
order_status: filterStatus,
|
order_status: filterStatus as
|
||||||
|
| ""
|
||||||
|
| "pending_payment"
|
||||||
|
| "pending_confirmation"
|
||||||
|
| "confirmed"
|
||||||
|
| "completed"
|
||||||
|
| "cancelled",
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,7 +90,7 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getAllOrderAgecy({
|
getAllOrderAgecy({
|
||||||
page: currentPageAgency,
|
page: currentPageAgency,
|
||||||
page_size: 10,
|
page_size: 12,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -277,7 +273,7 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
? "bg-blue-600 text-white shadow-md"
|
? "bg-blue-600 text-white shadow-md"
|
||||||
: "bg-gray-800 text-gray-300 hover:bg-gray-700"
|
: "bg-gray-800 text-gray-300 hover:bg-gray-700"
|
||||||
}`}
|
}`}
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
setFilterStatus(
|
setFilterStatus(
|
||||||
s as
|
s as
|
||||||
| ""
|
| ""
|
||||||
@@ -286,8 +282,9 @@ export default function FinancePage({ user }: { user: Role }) {
|
|||||||
| "confirmed"
|
| "confirmed"
|
||||||
| "completed"
|
| "completed"
|
||||||
| "cancelled",
|
| "cancelled",
|
||||||
)
|
);
|
||||||
}
|
setCurrentPage(1);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{s === ""
|
{s === ""
|
||||||
? t("Barcha bandlovlar")
|
? t("Barcha bandlovlar")
|
||||||
|
|||||||
@@ -26,22 +26,31 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const News = () => {
|
const News = () => {
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [deleteId, setDeleteId] = useState<number | null>(null);
|
const [deleteId, setDeleteId] = useState<number | null>(null);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: allNews,
|
data: allNews,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ["all_news", currentPage],
|
queryKey: ["all_news", page],
|
||||||
queryFn: () => getAllNews({ page: currentPage, page_size: 10 }),
|
queryFn: () => getAllNews({ page, page_size: 12 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutate: deleteMutate, isPending } = useMutation({
|
const { mutate: deleteMutate, isPending } = useMutation({
|
||||||
@@ -303,37 +312,38 @@ const News = () => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
<div className="flex justify-end gap-2 w-[90%] mx-auto mt-8">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(allNews?.data.data.total_pages)].map((_, i) => (
|
{[...Array(allNews?.data.data.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === allNews?.data.data.total_pages}
|
disabled={page === allNews?.data.data.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(Math.min(page + 1, allNews?.data.data.total_pages ?? 1))
|
||||||
Math.min(p + 1, allNews ? allNews?.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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export default function WithdrawRequests({
|
export default function WithdrawRequests({
|
||||||
@@ -53,7 +54,16 @@ export default function WithdrawRequests({
|
|||||||
| "user";
|
| "user";
|
||||||
}) {
|
}) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const [statusFilter, setStatusFilter] = useState<
|
const [statusFilter, setStatusFilter] = useState<
|
||||||
"pending" | "approved" | "cancelled" | ""
|
"pending" | "approved" | "cancelled" | ""
|
||||||
>("");
|
>("");
|
||||||
@@ -70,11 +80,11 @@ export default function WithdrawRequests({
|
|||||||
const [closeId, setCloseId] = useState<number | null>(null);
|
const [closeId, setCloseId] = useState<number | null>(null);
|
||||||
|
|
||||||
const { data, isLoading, isError, refetch } = useQuery({
|
const { data, isLoading, isError, refetch } = useQuery({
|
||||||
queryKey: ["withdraw-requests", currentPage, statusFilter],
|
queryKey: ["withdraw-requests", page, statusFilter],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getPayoutList({
|
getPayoutList({
|
||||||
page: currentPage,
|
page,
|
||||||
page_size: 10,
|
page_size: 20,
|
||||||
status: statusFilter,
|
status: statusFilter,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@@ -178,7 +188,7 @@ export default function WithdrawRequests({
|
|||||||
key={tab.value}
|
key={tab.value}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setStatusFilter(tab.value);
|
setStatusFilter(tab.value);
|
||||||
setCurrentPage(1);
|
updatePage(1);
|
||||||
}}
|
}}
|
||||||
className={`rounded-xl px-4 py-2 font-medium transition-all ${
|
className={`rounded-xl px-4 py-2 font-medium transition-all ${
|
||||||
statusFilter === tab.value
|
statusFilter === tab.value
|
||||||
@@ -360,37 +370,38 @@ export default function WithdrawRequests({
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 🔹 PAGINATION */}
|
{/* 🔹 PAGINATION */}
|
||||||
<div className="flex justify-end gap-2 mt-5">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(data?.data.data.total_pages)].map((_, i) => (
|
{[...Array(data?.data.data.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === data?.data.data.total_pages}
|
disabled={page === data?.data.data.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(Math.min(page + 1, data?.data.data.total_pages ?? 1))
|
||||||
Math.min(p + 1, data ? data?.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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -92,15 +93,24 @@ const positions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const SiteBannerAdmin = () => {
|
const SiteBannerAdmin = () => {
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: banner,
|
data: banner,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
refetch,
|
refetch,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ["all_banner", currentPage],
|
queryKey: ["all_banner", page],
|
||||||
queryFn: () => getBanner({ page: currentPage, page_size: 10 }),
|
queryFn: () => getBanner({ page, page_size: 20 }),
|
||||||
select(data) {
|
select(data) {
|
||||||
return data.data.data;
|
return data.data.data;
|
||||||
},
|
},
|
||||||
@@ -385,37 +395,38 @@ const SiteBannerAdmin = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2 mt-5">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(banner?.total_pages)].map((_, i) => (
|
{[...Array(banner?.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === banner?.total_pages}
|
disabled={page === banner?.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(Math.min(page + 1, banner?.total_pages ?? 1))
|
||||||
Math.min(p + 1, banner ? banner?.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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -15,15 +15,25 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/shared/ui/dialog";
|
} from "@/shared/ui/dialog";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { AlertTriangle, Loader2, XIcon } from "lucide-react";
|
import {
|
||||||
|
AlertTriangle,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Loader2,
|
||||||
|
XIcon,
|
||||||
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link, useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const SupportAgency = () => {
|
const SupportAgency = () => {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
const [openUser, setOpenUser] = useState<boolean>(false);
|
const [openUser, setOpenUser] = useState<boolean>(false);
|
||||||
const [user, setUser] = useState<{
|
const [user, setUser] = useState<{
|
||||||
status: boolean;
|
status: boolean;
|
||||||
@@ -38,10 +48,15 @@ const SupportAgency = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selected, setSelected] = useState<GetSupportAgencyRes | null>(null);
|
const [selected, setSelected] = useState<GetSupportAgencyRes | null>(null);
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const { data, isLoading, isError, refetch } = useQuery({
|
const { data, isLoading, isError, refetch } = useQuery({
|
||||||
queryKey: ["support_agency"],
|
queryKey: ["support_agency", page],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getSupportAgency({ page: 1, page_size: 10, search: "", status: "" }),
|
getSupportAgency({ page: page, page_size: 12, search: "", status: "" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutate: updateTours } = useMutation({
|
const { mutate: updateTours } = useMutation({
|
||||||
@@ -117,7 +132,10 @@ const SupportAgency = () => {
|
|||||||
<div className="flex gap-3 mb-6">
|
<div className="flex gap-3 mb-6">
|
||||||
<input
|
<input
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => {
|
||||||
|
setQuery(e.target.value);
|
||||||
|
updatePage(1);
|
||||||
|
}}
|
||||||
placeholder={t("Qidiruv (ism, email yoki telefon)...")}
|
placeholder={t("Qidiruv (ism, email yoki telefon)...")}
|
||||||
className="flex-1 p-2 border rounded-md focus:outline-none focus:ring"
|
className="flex-1 p-2 border rounded-md focus:outline-none focus:ring"
|
||||||
/>
|
/>
|
||||||
@@ -188,6 +206,43 @@ const SupportAgency = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
|
<button
|
||||||
|
disabled={page === 1}
|
||||||
|
onClick={() => updatePage(Math.max(page - 1, 1))}
|
||||||
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{[...Array(data?.data.data.total_pages)].map((_, i) => {
|
||||||
|
const pageNum = i + 1;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={() => updatePage(pageNum)}
|
||||||
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
|
page === pageNum
|
||||||
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={page === data?.data.data.total_pages}
|
||||||
|
onClick={() =>
|
||||||
|
updatePage(Math.min(page + 1, data?.data.data.total_pages ?? 1))
|
||||||
|
}
|
||||||
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<ChevronRight className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{selected && (
|
{selected && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-4"
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-4"
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ const StepOne = ({
|
|||||||
|
|
||||||
function onSubmit(value: z.infer<typeof TourformSchema>) {
|
function onSubmit(value: z.infer<typeof TourformSchema>) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
const tour = data ? data.data : null;
|
||||||
// Asosiy ma'lumotlar
|
// Asosiy ma'lumotlar
|
||||||
formData.append("title", value.title);
|
formData.append("title", value.title);
|
||||||
formData.append("location_name", value.location_name);
|
formData.append("location_name", value.location_name);
|
||||||
@@ -315,7 +315,7 @@ const StepOne = ({
|
|||||||
formData.append("hotel_meals_ru", value.hotel_meals_info_ru);
|
formData.append("hotel_meals_ru", value.hotel_meals_info_ru);
|
||||||
}
|
}
|
||||||
formData.append("duration_days", String(value.duration));
|
formData.append("duration_days", String(value.duration));
|
||||||
formData.append("rating", String("0.0"));
|
formData.append("rating", tour ? String(tour.rating) : "0.0");
|
||||||
|
|
||||||
if (value.banner instanceof File) {
|
if (value.banner instanceof File) {
|
||||||
formData.append("image_banner", value.banner);
|
formData.append("image_banner", value.banner);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
type Role =
|
type Role =
|
||||||
@@ -51,15 +51,17 @@ type Role =
|
|||||||
|
|
||||||
const Tours = ({ user }: { user: Role }) => {
|
const Tours = ({ user }: { user: Role }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const [deleteId, setDeleteId] = useState<number | null>(null);
|
const [deleteId, setDeleteId] = useState<number | null>(null);
|
||||||
const [showPopularDialog, setShowPopularDialog] = useState(false);
|
const [showPopularDialog, setShowPopularDialog] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
|
|
||||||
const { data, isLoading, isError, refetch } = useQuery({
|
const { data, isLoading, isError, refetch } = useQuery({
|
||||||
queryKey: ["all_tours", page],
|
queryKey: ["all_tours", page],
|
||||||
queryFn: () => getAllTours({ page: page, page_size: 10 }),
|
queryFn: () => getAllTours({ page, page_size: 10 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: popularTour } = useQuery({
|
const { data: popularTour } = useQuery({
|
||||||
@@ -110,6 +112,11 @@ const Tours = ({ user }: { user: Role }) => {
|
|||||||
setShowPopularDialog(false);
|
setShowPopularDialog(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<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">
|
||||||
@@ -343,32 +350,35 @@ const Tours = ({ user }: { user: Role }) => {
|
|||||||
<div className="flex justify-end mt-10 gap-3">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={page === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
{[...Array(data?.data.data.total_pages)].map((_, i) => (
|
|
||||||
<button
|
{[...Array(data?.data.data.total_pages)].map((_, i) => {
|
||||||
key={i}
|
const pageNum = i + 1;
|
||||||
onClick={() => setPage(i + 1)}
|
return (
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
<button
|
||||||
page === i + 1
|
key={i}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
onClick={() => updatePage(pageNum)}
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
}`}
|
page === pageNum
|
||||||
>
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
{i + 1}
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
</button>
|
}`}
|
||||||
))}
|
>
|
||||||
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={page === data?.data.data.total_pages}
|
disabled={page === data?.data.data.total_pages}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setPage((p) =>
|
updatePage(Math.min(page + 1, data?.data.data.total_pages ?? 1))
|
||||||
Math.min(p + 1, data ? data.data.data.total_pages : 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -25,6 +25,39 @@ import React, { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
|
// Error Component
|
||||||
|
const ErrorDisplay: React.FC<{ message: string; onRetry: () => void }> = ({
|
||||||
|
message,
|
||||||
|
onRetry,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-[300px] bg-slate-800/50 rounded-lg text-center text-white gap-4 p-6">
|
||||||
|
<AlertTriangle className="w-10 h-10 text-red-500" />
|
||||||
|
<p className="text-lg">{message}</p>
|
||||||
|
<Button
|
||||||
|
onClick={onRetry}
|
||||||
|
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")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loading Component
|
||||||
|
const LoadingDisplay: React.FC<{ message?: string }> = ({ message }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-[300px] bg-slate-800/50 rounded-lg text-white gap-4">
|
||||||
|
<Loader2 className="w-10 h-10 animate-spin text-cyan-400" />
|
||||||
|
<p className="text-slate-400">
|
||||||
|
{message || t("Ma'lumotlar yuklanmoqda...")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const ToursSetting: React.FC = () => {
|
const ToursSetting: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
@@ -134,61 +167,12 @@ const ToursSetting: React.FC = () => {
|
|||||||
isError: amenitiesError,
|
isError: amenitiesError,
|
||||||
refetch: amenitiesRef,
|
refetch: amenitiesRef,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ["all_amenities", page, pageSize],
|
queryKey: ["all_amenities", pageAmenities, pageSizeAmenities],
|
||||||
queryFn: () => getAllAmenities({ page, page_size: pageSize }),
|
queryFn: () =>
|
||||||
|
getAllAmenities({ page: pageAmenities, page_size: pageSizeAmenities }),
|
||||||
select: (res) => res.data.data,
|
select: (res) => res.data.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
|
||||||
isLoading ||
|
|
||||||
tarifLoad ||
|
|
||||||
transportLoad ||
|
|
||||||
typeLoad ||
|
|
||||||
featureLoad ||
|
|
||||||
featureTypeLoad ||
|
|
||||||
amenitiesLoad
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<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" />
|
|
||||||
<p className="text-slate-400">{t("Ma'lumotlar yuklanmoqda...")}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
isError ||
|
|
||||||
tarifError ||
|
|
||||||
transportError ||
|
|
||||||
typeError ||
|
|
||||||
featureError ||
|
|
||||||
featureTypeError ||
|
|
||||||
amenitiesError
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<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" />
|
|
||||||
<p className="text-lg">
|
|
||||||
{t("Ma'lumotlarni yuklashda xatolik yuz berdi.")}
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
refetch();
|
|
||||||
tarifRef();
|
|
||||||
transportRef();
|
|
||||||
typeRef();
|
|
||||||
featureRef();
|
|
||||||
featureTypeRef();
|
|
||||||
amenitiesRef();
|
|
||||||
}}
|
|
||||||
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")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTabChange = (value: string) => {
|
const handleTabChange = (value: string) => {
|
||||||
setActiveTab(value);
|
setActiveTab(value);
|
||||||
navigate({
|
navigate({
|
||||||
@@ -217,52 +201,121 @@ const ToursSetting: React.FC = () => {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="badge" className="space-y-4">
|
<TabsContent value="badge" className="space-y-4">
|
||||||
<BadgeTable data={data} page={page} pageSize={pageSize} />
|
{isLoading ? (
|
||||||
|
<LoadingDisplay />
|
||||||
|
) : isError ? (
|
||||||
|
<ErrorDisplay
|
||||||
|
message={t("Belgilarni yuklashda xatolik yuz berdi.")}
|
||||||
|
onRetry={refetch}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<BadgeTable data={data} page={page} pageSize={pageSize} />
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="tarif" className="space-y-4">
|
<TabsContent value="tarif" className="space-y-4">
|
||||||
<TarifTable
|
{tarifLoad ? (
|
||||||
data={tarifData}
|
<LoadingDisplay />
|
||||||
page={pageTarif}
|
) : tarifError ? (
|
||||||
pageSize={pageSizeTarif}
|
<ErrorDisplay
|
||||||
/>
|
message={t("Tariflarni yuklashda xatolik yuz berdi.")}
|
||||||
|
onRetry={tarifRef}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<TarifTable
|
||||||
|
data={tarifData}
|
||||||
|
page={pageTarif}
|
||||||
|
pageSize={pageSizeTarif}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="transport" className="space-y-4">
|
<TabsContent value="transport" className="space-y-4">
|
||||||
<TransportTable
|
{transportLoad ? (
|
||||||
data={transportData}
|
<LoadingDisplay />
|
||||||
page={pageTransport}
|
) : transportError ? (
|
||||||
pageSize={pageSizeTransport}
|
<ErrorDisplay
|
||||||
/>
|
message={t("Transportlarni yuklashda xatolik yuz berdi.")}
|
||||||
|
onRetry={transportRef}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<TransportTable
|
||||||
|
data={transportData}
|
||||||
|
page={pageTransport}
|
||||||
|
pageSize={pageSizeTransport}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="meal" className="space-y-4">
|
<TabsContent value="meal" className="space-y-4">
|
||||||
<Amenities
|
{amenitiesLoad ? (
|
||||||
data={amenitiesData}
|
<LoadingDisplay />
|
||||||
page={pageAmenities}
|
) : amenitiesError ? (
|
||||||
pageSize={pageSizeAmenities}
|
<ErrorDisplay
|
||||||
/>
|
message={t("Qulayliklarni yuklashda xatolik yuz berdi.")}
|
||||||
|
onRetry={amenitiesRef}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Amenities
|
||||||
|
data={amenitiesData}
|
||||||
|
page={pageAmenities}
|
||||||
|
pageSize={pageSizeAmenities}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="hotel_type" className="space-y-4">
|
<TabsContent value="hotel_type" className="space-y-4">
|
||||||
<MealTable
|
{typeLoad ? (
|
||||||
data={typeData}
|
<LoadingDisplay />
|
||||||
page={pageTransport}
|
) : typeError ? (
|
||||||
pageSize={pageSizeTransport}
|
<ErrorDisplay
|
||||||
/>
|
message={t("Otel turlarini yuklashda xatolik yuz berdi.")}
|
||||||
|
onRetry={typeRef}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MealTable
|
||||||
|
data={typeData}
|
||||||
|
page={pageType}
|
||||||
|
pageSize={pageSizeType}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="hotel_features" className="space-y-4">
|
<TabsContent value="hotel_features" className="space-y-4">
|
||||||
<FeaturesTable
|
{featureLoad ? (
|
||||||
data={featureData}
|
<LoadingDisplay />
|
||||||
page={pageFeature}
|
) : featureError ? (
|
||||||
pageSize={pageSizeFeature}
|
<ErrorDisplay
|
||||||
setActiveTab={setActiveTab}
|
message={t("Otel sharoitlarini yuklashda xatolik yuz berdi.")}
|
||||||
setFeatureId={setFeatureId}
|
onRetry={featureRef}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<FeaturesTable
|
||||||
|
data={featureData}
|
||||||
|
page={pageFeature}
|
||||||
|
pageSize={pageSizeFeature}
|
||||||
|
setActiveTab={setActiveTab}
|
||||||
|
setFeatureId={setFeatureId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="feature_type" className="space-y-4">
|
<TabsContent value="feature_type" className="space-y-4">
|
||||||
<FeaturesTableType
|
{featureTypeLoad ? (
|
||||||
data={featureTypeData}
|
<LoadingDisplay />
|
||||||
page={pageFeature}
|
) : featureTypeError ? (
|
||||||
featureId={featureId}
|
<ErrorDisplay
|
||||||
pageSize={pageSizeFeature}
|
message={t("Sharoit turlarini yuklashda xatolik yuz berdi.")}
|
||||||
/>
|
onRetry={featureTypeRef}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FeaturesTableType
|
||||||
|
data={featureTypeData}
|
||||||
|
page={pageFeature}
|
||||||
|
featureId={featureId}
|
||||||
|
pageSize={pageSizeFeature}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,20 +18,28 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
export default function UserList() {
|
export default function UserList() {
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const initialPage = Number(searchParams.get("page")) || 1;
|
||||||
|
const [page, setPage] = useState(initialPage);
|
||||||
const usersPerPage = 6;
|
const usersPerPage = 6;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const updatePage = (newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
setSearchParams({ page: newPage.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
const { data, isLoading, isError, refetch } = useQuery({
|
const { data, isLoading, isError, refetch } = useQuery({
|
||||||
queryKey: ["user_all", currentPage, searchQuery],
|
queryKey: ["user_all", page, searchQuery],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getAllUsers({
|
getAllUsers({
|
||||||
page: currentPage,
|
page,
|
||||||
page_size: usersPerPage,
|
page_size: usersPerPage,
|
||||||
search: searchQuery,
|
search: searchQuery,
|
||||||
}),
|
}),
|
||||||
@@ -119,7 +127,7 @@ export default function UserList() {
|
|||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSearchQuery(e.target.value);
|
setSearchQuery(e.target.value);
|
||||||
setCurrentPage(1);
|
updatePage(1);
|
||||||
}}
|
}}
|
||||||
className="w-full pl-14 pr-4 py-3 bg-slate-700/30 border border-slate-600/50 text-white placeholder-slate-400 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
|
className="w-full pl-14 pr-4 py-3 bg-slate-700/30 border border-slate-600/50 text-white placeholder-slate-400 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
|
||||||
/>
|
/>
|
||||||
@@ -214,41 +222,40 @@ export default function UserList() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end mt-10 gap-3">
|
||||||
<button
|
<button
|
||||||
disabled={currentPage === 1}
|
disabled={page === 1}
|
||||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
onClick={() => updatePage(Math.max(page - 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{[...Array(data?.data.data.total_pages)].map((_, i) => (
|
{[...Array(data?.data.data.total_pages)].map((_, i) => {
|
||||||
<button
|
const pageNum = i + 1;
|
||||||
key={i}
|
return (
|
||||||
onClick={() => setCurrentPage(i + 1)}
|
<button
|
||||||
className={`px-4 py-2 rounded-lg border transition-all font-medium ${
|
key={i}
|
||||||
currentPage === i + 1
|
onClick={() => updatePage(pageNum)}
|
||||||
? "bg-gradient-to-r from-blue-600 to-cyan-600 border-blue-500 text-white shadow-lg shadow-cyan-500/50"
|
className={`px-4 py-2 rounded-lg border font-medium transition-all ${
|
||||||
: "border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500"
|
page === pageNum
|
||||||
}`}
|
? "bg-gradient-to-r from-blue-600 to-cyan-600 text-white border-blue-500"
|
||||||
>
|
: "border-slate-600 text-slate-300 hover:bg-slate-700/50"
|
||||||
{i + 1}
|
}`}
|
||||||
</button>
|
>
|
||||||
))}
|
{pageNum}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={
|
disabled={page === data?.data.data.total_pages}
|
||||||
data
|
|
||||||
? currentPage === data.data.data.total_pages
|
|
||||||
: currentPage === 1
|
|
||||||
}
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCurrentPage((p) =>
|
updatePage(
|
||||||
Math.min(p + 1, data ? data.data.data.total_pages : 1),
|
Math.min(page + 1, data?.data.data.total_pages ?? 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"
|
className="p-2 rounded-lg border border-slate-600 text-slate-300 hover:bg-slate-700/50 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-5 h-5" />
|
<ChevronRight className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user