Initial commit for new panel

This commit is contained in:
AmirReza Jamali
2025-09-09 11:20:26 +03:30
commit 1d4ccb3575
343 changed files with 20031 additions and 0 deletions
@@ -0,0 +1,42 @@
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export function TendersSkeleton() {
return (
<div className="rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card">
<h2 className="mb-5.5 text-body-2xlg font-bold text-dark dark:text-white">
Tenders
</h2>
<Table>
<TableHeader>
<TableRow className="border-none uppercase [&>th]:text-center">
<TableHead>Full name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Type</TableHead>
<TableHead>Status</TableHead>
<TableHead>Company</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Array.from({ length: 5 }).map((_, i) => (
<TableRow key={i}>
<TableCell colSpan={100}>
<Skeleton className="h-8" />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
+60
View File
@@ -0,0 +1,60 @@
"use client";
import useTenderListPresenter from "./useTenderListPresenter";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { TendersSkeleton } from "./Skeleton";
const TendersTable = () => {
const { allTenders, isPending } = useTenderListPresenter();
if (isPending) return <TendersSkeleton />;
return (
<div className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card">
<Table>
<TableHeader>
<TableRow className="border-none uppercase [&>th]:text-start">
<TableHead colSpan={100} className="text-start font-extrabold">
Title
</TableHead>
<TableHead colSpan={100} className="text-start font-extrabold">
Country Code
</TableHead>
<TableHead colSpan={100} className="text-start font-extrabold">
Status
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{allTenders.map((item) => {
return (
<TableRow
key={item.id}
className="odd:bg-gray-2 dark:odd:bg-gray-7"
>
<TableCell className="text-start" colSpan={100}>
{item.title}
</TableCell>
<TableCell className="text-start" colSpan={100}>
{item.country_code}
</TableCell>
<TableCell className="text-start" colSpan={100}>
{item.status}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
);
};
export default TendersTable;
@@ -0,0 +1,80 @@
"use client";
import { ConfirmationModalProps } from "@/components/ui/ConfirmationModal";
import { useTendersInfiniteQuery } from "@/hooks/queries";
import useDebounce from "@/hooks/useDebounce";
import { TCustomer } from "@/lib/api";
import { usePathname } from "next/navigation";
import { useEffect, useRef, useState } from "react";
import { useInView } from "react-intersection-observer";
import { z } from "zod";
const useTenderListPresenter = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [currentTender, setCurrentTender] = useState<TCustomer | null>(null);
const [search, setSearch] = useState("");
const [modalConfig, setModalConfig] = useState<
Partial<ConfirmationModalProps>
>({});
const [params, setParams] = useState<Record<string, any>>({
sort: "created_at",
});
const debouncedSearch = useDebounce(search, 1000);
const pathName = usePathname();
const {
data,
error,
isPending,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useTendersInfiniteQuery(params);
const { inView } = useInView({ threshold: 0.5 });
useEffect(() => {
if (inView && hasNextPage && data && data.pages.length < 5) {
fetchNextPage();
}
}, [inView, hasNextPage, data, fetchNextPage]);
useEffect(() => {
const result = z.string().safeParse(debouncedSearch);
if (result.success) {
setParams((prevParams) => ({
...prevParams,
q: result.data,
}));
}
}, [debouncedSearch]);
const handleFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value);
};
const onConfirmDelete = () => {
if (currentTender) {
deleteTender(currentTender.id);
}
};
const allTenders = data?.pages.flatMap((page) => page.data.tenders) ?? [];
return {
currentTender,
setCurrentTender,
search,
handleFilterChange,
onConfirmDelete,
allTenders,
error,
isPending,
hasNextPage,
data,
fetchNextPage,
isFetchingNextPage,
pathName,
isModalOpen,
setIsModalOpen,
modalConfig,
setModalConfig,
};
};
export default useTenderListPresenter;