[1.0.4] #29
@@ -143,18 +143,51 @@ export const getAppointments = async (req, res) => {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 10;
|
||||
const skip = (page - 1) * limit;
|
||||
const { date, search } = req.query;
|
||||
|
||||
const { date, startDate, endDate, search } = req.query;
|
||||
|
||||
const where = {};
|
||||
|
||||
if (date) {
|
||||
const hasSingleDate = date && date.trim() !== "";
|
||||
|
||||
const hasRange =
|
||||
(startDate && startDate.trim() !== "") ||
|
||||
(endDate && endDate.trim() !== "");
|
||||
|
||||
if (hasSingleDate) {
|
||||
const start = new Date(date);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
|
||||
const end = new Date(date);
|
||||
end.setDate(end.getDate() + 1);
|
||||
where.date = { gte: start, lt: end };
|
||||
end.setHours(23, 59, 59, 999);
|
||||
|
||||
where.date = {
|
||||
gte: start,
|
||||
lte: end,
|
||||
};
|
||||
}
|
||||
|
||||
if (search) {
|
||||
if (!hasSingleDate && hasRange) {
|
||||
const dateFilter = {};
|
||||
|
||||
if (startDate && startDate.trim() !== "") {
|
||||
const start = new Date(startDate);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
|
||||
dateFilter.gte = start;
|
||||
}
|
||||
|
||||
if (endDate && endDate.trim() !== "") {
|
||||
const end = new Date(endDate);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
|
||||
dateFilter.lte = end;
|
||||
}
|
||||
|
||||
where.date = dateFilter;
|
||||
}
|
||||
|
||||
if (search && search.trim() !== "") {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: "insensitive" } },
|
||||
{ mobileNumber: { contains: search } },
|
||||
@@ -165,24 +198,39 @@ export const getAppointments = async (req, res) => {
|
||||
const [appointments, total] = await Promise.all([
|
||||
prisma.appointment.findMany({
|
||||
where,
|
||||
include: { doctor: true, department: true },
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: {
|
||||
doctor: true,
|
||||
department: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
}),
|
||||
prisma.appointment.count({ where }),
|
||||
|
||||
prisma.appointment.count({
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: appointments,
|
||||
pagination: { total, page, limit, totalPages: Math.ceil(total / limit) },
|
||||
pagination: {
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res
|
||||
.status(500)
|
||||
.json({ success: false, message: "Failed to fetch appointments" });
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to fetch appointments",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,12 +4,16 @@ export const getAppointmentsApi = async (
|
||||
page = 1,
|
||||
limit = 10,
|
||||
date = "",
|
||||
startDate = "",
|
||||
endDate = "",
|
||||
search = "",
|
||||
) => {
|
||||
const params = new URLSearchParams({
|
||||
page: String(page),
|
||||
limit: String(limit),
|
||||
...(date && { date }),
|
||||
...(startDate && { startDate }),
|
||||
...(endDate && { endDate }),
|
||||
...(search && { search }),
|
||||
});
|
||||
const res = await apiClient.get(`/appointments/getall?${params}`);
|
||||
|
||||
@@ -40,7 +40,8 @@ export default function AppointmentPage() {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [filterDoctor, setFilterDoctor] = useState("");
|
||||
const [filterDate, setFilterDate] = useState("");
|
||||
|
||||
const [startDate, setStartDate] = useState("");
|
||||
const [endDate, setEndDate] = useState("");
|
||||
const [viewOpen, setViewOpen] = useState(false);
|
||||
const [viewData, setViewData] = useState<any>(null);
|
||||
|
||||
@@ -56,6 +57,8 @@ export default function AppointmentPage() {
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
filterDate,
|
||||
startDate,
|
||||
endDate,
|
||||
searchText,
|
||||
);
|
||||
setAppointments(res?.data || []);
|
||||
@@ -66,7 +69,7 @@ export default function AppointmentPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, itemsPerPage, filterDate, searchText]);
|
||||
}, [currentPage, itemsPerPage, filterDate, startDate, endDate, searchText]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAll();
|
||||
@@ -116,39 +119,87 @@ export default function AppointmentPage() {
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
|
||||
<h1 className="text-3xl font-bold">Appointments</h1>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Input
|
||||
placeholder="Search name / phone..."
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[220px] text-base"
|
||||
/>
|
||||
<div className="flex flex-wrap gap-4 items-end">
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
Search
|
||||
</label>
|
||||
<Input
|
||||
placeholder="Search name / phone..."
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[220px] text-base"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
value={filterDate}
|
||||
onChange={(e) => {
|
||||
setFilterDate(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[160px] text-base"
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
Date
|
||||
</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={filterDate}
|
||||
onChange={(e) => {
|
||||
setFilterDate(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[160px] text-base"
|
||||
disabled={!!startDate || !!endDate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
From
|
||||
</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={startDate}
|
||||
onChange={(e) => {
|
||||
setStartDate(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[160px] text-base"
|
||||
disabled={!!filterDate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
To
|
||||
</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={endDate}
|
||||
onChange={(e) => {
|
||||
setEndDate(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-[160px] text-base"
|
||||
disabled={!!filterDate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
Rows
|
||||
</label>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
Reference in New Issue
Block a user