feat: server-side pagination, search, and date filter for appointments
This commit is contained in:
@@ -45,52 +45,46 @@ export default function AppointmentPage() {
|
||||
const [viewData, setViewData] = useState<any>(null);
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const itemsPerPage = 10;
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [totalItems, setTotalItems] = useState(0);
|
||||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||||
|
||||
const fetchAll = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getAppointmentsApi();
|
||||
const res = await getAppointmentsApi(
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
filterDate,
|
||||
searchText,
|
||||
);
|
||||
setAppointments(res?.data || []);
|
||||
setTotalPages(res?.pagination?.totalPages || 1);
|
||||
setTotalItems(res?.pagination?.total || 0);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
}, [currentPage, itemsPerPage, filterDate, searchText]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAll();
|
||||
}, [fetchAll]);
|
||||
|
||||
const filteredAppointments = appointments.filter((item) => {
|
||||
const matchesSearch =
|
||||
item.name?.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
item.mobileNumber?.includes(searchText) ||
|
||||
item.email?.toLowerCase().includes(searchText.toLowerCase());
|
||||
|
||||
const matchesDoctor = filterDoctor
|
||||
? item.doctor?.name?.toLowerCase().includes(filterDoctor.toLowerCase())
|
||||
: true;
|
||||
|
||||
const matchesDate = filterDate
|
||||
? new Date(item.date).toISOString().split("T")[0] === filterDate
|
||||
: true;
|
||||
|
||||
return matchesSearch && matchesDoctor && matchesDate;
|
||||
return matchesDoctor;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [searchText, filterDoctor, filterDate]);
|
||||
|
||||
const totalPages = Math.ceil(filteredAppointments.length / itemsPerPage);
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = filteredAppointments.slice(
|
||||
indexOfFirstItem,
|
||||
indexOfLastItem,
|
||||
);
|
||||
const indexOfFirstItem = (currentPage - 1) * itemsPerPage;
|
||||
|
||||
function openView(item: any) {
|
||||
setViewData(item);
|
||||
@@ -126,17 +120,36 @@ export default function AppointmentPage() {
|
||||
<Input
|
||||
placeholder="Search name / phone..."
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[220px] text-base"
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
value={filterDate}
|
||||
onChange={(e) => setFilterDate(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setFilterDate(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[160px] text-base"
|
||||
/>
|
||||
|
||||
<select
|
||||
value={itemsPerPage}
|
||||
onChange={(e) => {
|
||||
setItemsPerPage(Number(e.target.value));
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="flex h-10 rounded-md border border-input bg-background px-3 py-2 text-sm focus:ring-2 focus:ring-primary"
|
||||
>
|
||||
<option value={5}>5 / page</option>
|
||||
<option value={10}>10 / page</option>
|
||||
<option value={20}>20 / page</option>
|
||||
</select>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={fetchAll}
|
||||
@@ -192,7 +205,7 @@ export default function AppointmentPage() {
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : currentItems.length === 0 ? (
|
||||
) : filteredAppointments.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={6}
|
||||
@@ -202,7 +215,7 @@ export default function AppointmentPage() {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
currentItems.map((item) => (
|
||||
filteredAppointments.map((item) => (
|
||||
<TableRow key={item.id} className="hover:bg-muted/50">
|
||||
<TableCell className="font-mono text-xs">
|
||||
{item.id}
|
||||
@@ -260,18 +273,15 @@ export default function AppointmentPage() {
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{!loading && filteredAppointments.length > 0 && (
|
||||
{!loading && totalItems > 0 && (
|
||||
<div className="flex items-center justify-between px-2 py-6 border-t">
|
||||
<div className="text-base text-muted-foreground">
|
||||
Showing{" "}
|
||||
<span className="font-semibold">{indexOfFirstItem + 1}</span> to{" "}
|
||||
<span className="font-semibold">
|
||||
{Math.min(indexOfLastItem, filteredAppointments.length)}
|
||||
{Math.min(currentPage * itemsPerPage, totalItems)}
|
||||
</span>{" "}
|
||||
of{" "}
|
||||
<span className="font-semibold">
|
||||
{filteredAppointments.length}
|
||||
</span>
|
||||
of <span className="font-semibold">{totalItems}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="text-base font-semibold">
|
||||
|
||||
Reference in New Issue
Block a user