diff --git a/backend/src/controllers/appointment.controller.js b/backend/src/controllers/appointment.controller.js index 6181182..446fdfc 100644 --- a/backend/src/controllers/appointment.controller.js +++ b/backend/src/controllers/appointment.controller.js @@ -147,7 +147,7 @@ export const getAppointments = async (req, res) => { const limit = parseInt(req.query.limit) || 10; const skip = (page - 1) * limit; - const { date, startDate, endDate, search } = req.query; + const { date, startDate, endDate, createdDate, createdStartDate, createdEndDate, search } = req.query; const where = {}; @@ -161,11 +161,7 @@ export const getAppointments = async (req, res) => { const end = new Date(date); end.setHours(23, 59, 59, 999); - - where.date = { - gte: start, - lte: end, - }; + where.date = { gte: start, lte: end }; } if (!hasSingleDate && hasRange) { @@ -188,6 +184,33 @@ export const getAppointments = async (req, res) => { where.date = dateFilter; } + const hasSingleCreatedDate = createdDate && createdDate.trim() !== ''; + const hasCreatedRange = + (createdStartDate && createdStartDate.trim() !== '') || (createdEndDate && createdEndDate.trim() !== ''); + + if (hasSingleCreatedDate) { + const start = new Date(createdDate); + start.setHours(0, 0, 0, 0); + const end = new Date(createdDate); + end.setHours(23, 59, 59, 999); + where.createdAt = { gte: start, lte: end }; + } + + if (!hasSingleCreatedDate && hasCreatedRange) { + const createdDateFilter = {}; + if (createdStartDate && createdStartDate.trim() !== '') { + const start = new Date(createdStartDate); + start.setHours(0, 0, 0, 0); + createdDateFilter.gte = start; + } + if (createdEndDate && createdEndDate.trim() !== '') { + const end = new Date(createdEndDate); + end.setHours(23, 59, 59, 999); + createdDateFilter.lte = end; + } + where.createdAt = createdDateFilter; + } + if (search && search.trim() !== '') { where.OR = [ { name: { contains: search, mode: 'insensitive' } }, diff --git a/backend/src/controllers/healthCheck.controller.js b/backend/src/controllers/healthCheck.controller.js index eb0361a..4e0e691 100644 --- a/backend/src/controllers/healthCheck.controller.js +++ b/backend/src/controllers/healthCheck.controller.js @@ -431,7 +431,17 @@ export const getPackageBySlug = async (req, res) => { export const getAllInquiries = async (req, res) => { try { - const { page = 1, limit = 10, filterDate, startDate, endDate } = req.query; + const { + page = 1, + limit = 10, + filterDate, + startDate, + endDate, + createdDate, + createdStartDate, + createdEndDate, + search, + } = req.query; const queryPage = parseInt(page); const queryLimit = parseInt(limit); @@ -439,6 +449,15 @@ export const getAllInquiries = async (req, res) => { let where = {}; + if (search && search.trim() !== '') { + where.OR = [ + { fullName: { contains: search, mode: 'insensitive' } }, + { mobileNumber: { contains: search } }, + { email: { contains: search, mode: 'insensitive' } }, + { healthPackage: { name: { contains: search, mode: 'insensitive' } } }, + ]; + } + if (filterDate) { where.preferredDate = { gte: new Date(`${filterDate}T00:00:00.000Z`), @@ -454,6 +473,21 @@ export const getAllInquiries = async (req, res) => { } } + if (createdDate) { + where.createdAt = { + gte: new Date(`${createdDate}T00:00:00.000Z`), + lte: new Date(`${createdDate}T23:59:59.999Z`), + }; + } else if (createdStartDate || createdEndDate) { + where.createdAt = {}; + if (createdStartDate) { + where.createdAt.gte = new Date(`${createdStartDate}T00:00:00.000Z`); + } + if (createdEndDate) { + where.createdAt.lte = new Date(`${createdEndDate}T23:59:59.999Z`); + } + } + const [total, inquiries] = await prisma.$transaction([ prisma.healthPackageInquiry.count({ where }), prisma.healthPackageInquiry.findMany({ diff --git a/frontend/src/api/appointment.ts b/frontend/src/api/appointment.ts index 1ce2652..84ca425 100644 --- a/frontend/src/api/appointment.ts +++ b/frontend/src/api/appointment.ts @@ -6,6 +6,9 @@ export const getAppointmentsApi = async ( date = '', startDate = '', endDate = '', + createdDate = '', + createdStartDate = '', + createdEndDate = '', search = '' ) => { const params = new URLSearchParams({ @@ -14,6 +17,9 @@ export const getAppointmentsApi = async ( ...(date && { date }), ...(startDate && { startDate }), ...(endDate && { endDate }), + ...(createdDate && { createdDate }), + ...(createdStartDate && { createdStartDate }), + ...(createdEndDate && { createdEndDate }), ...(search && { search }), }); const res = await apiClient.get(`/appointments/getall?${params}`); diff --git a/frontend/src/api/healthCheck.ts b/frontend/src/api/healthCheck.ts index 1410d2c..fad5475 100644 --- a/frontend/src/api/healthCheck.ts +++ b/frontend/src/api/healthCheck.ts @@ -140,7 +140,17 @@ export const deleteCategoryApi = async (id: number) => { } }; -export const getAllInquiriesApi = async (page = 1, limit = 10, filterDate = '', startDate = '', endDate = '') => { +export const getAllInquiriesApi = async ( + page = 1, + limit = 10, + filterDate = '', + startDate = '', + endDate = '', + createdDate = '', + createdStartDate = '', + createdEndDate = '', + search = '' +) => { const params = new URLSearchParams({ page: page.toString(), limit: limit.toString(), @@ -149,6 +159,10 @@ export const getAllInquiriesApi = async (page = 1, limit = 10, filterDate = '', if (filterDate) params.append('filterDate', filterDate); if (startDate) params.append('startDate', startDate); if (endDate) params.append('endDate', endDate); + if (createdDate) params.append('createdDate', createdDate); + if (createdStartDate) params.append('createdStartDate', createdStartDate); + if (createdEndDate) params.append('createdEndDate', createdEndDate); + if (search) params.append('search', search); const res = await apiClient.get(`/health-check/inquiries?${params.toString()}`); return res.data; diff --git a/frontend/src/components/PackageInquiriesTab/PackageInquiriesTab.tsx b/frontend/src/components/PackageInquiriesTab/PackageInquiriesTab.tsx index 65c4637..71a1e1c 100644 --- a/frontend/src/components/PackageInquiriesTab/PackageInquiriesTab.tsx +++ b/frontend/src/components/PackageInquiriesTab/PackageInquiriesTab.tsx @@ -7,16 +7,24 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; -import { Loader2, RefreshCw, ChevronLeft, ChevronRight } from 'lucide-react'; +import { Loader2, RefreshCw, ChevronLeft, ChevronRight, CalendarDays } from 'lucide-react'; export default function PackageInquiriesTab() { const [inquiries, setInquiries] = useState([]); const [loading, setLoading] = useState(true); + const [searchText, setSearchText] = useState(''); + + const [dateMode, setDateMode] = useState<'preferred' | 'booked'>('preferred'); + const [filterDate, setFilterDate] = useState(''); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); + const [createdDate, setCreatedDate] = useState(''); + const [createdStartDate, setCreatedStartDate] = useState(''); + const [createdEndDate, setCreatedEndDate] = useState(''); + const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); const [totalItems, setTotalItems] = useState(0); @@ -25,7 +33,17 @@ export default function PackageInquiriesTab() { const fetchInquiries = useCallback(async () => { setLoading(true); try { - const res = await getAllInquiriesApi(currentPage, itemsPerPage, filterDate, startDate, endDate); + const res = await getAllInquiriesApi( + currentPage, + itemsPerPage, + filterDate, + startDate, + endDate, + createdDate, + createdStartDate, + createdEndDate, + searchText + ); setInquiries(res.data || []); setTotalItems(res.pagination?.total || 0); setTotalPages(res.pagination?.totalPages || 1); @@ -34,144 +52,216 @@ export default function PackageInquiriesTab() { } finally { setLoading(false); } - }, [currentPage, itemsPerPage, filterDate, startDate, endDate]); + }, [ + currentPage, + itemsPerPage, + filterDate, + startDate, + endDate, + createdDate, + createdStartDate, + createdEndDate, + searchText, + ]); useEffect(() => { fetchInquiries(); }, [fetchInquiries]); - const handleFilterChange = (setter: React.Dispatch>, value: string) => { - setter(value); + const handleModeChange = (newMode: 'preferred' | 'booked') => { + setDateMode(newMode); + setFilterDate(''); + setStartDate(''); + setEndDate(''); + setCreatedDate(''); + setCreatedStartDate(''); + setCreatedEndDate(''); setCurrentPage(1); }; + useEffect(() => { + setCurrentPage(1); + }, [searchText, filterDate, startDate, endDate, createdDate, createdStartDate, createdEndDate, itemsPerPage]); + const indexOfFirstItem = (currentPage - 1) * itemsPerPage; const indexOfLastItem = Math.min(currentPage * itemsPerPage, totalItems); return ( - - - Package Inquiries - -
-
- - handleFilterChange(setFilterDate, e.target.value)} - className="w-[140px] text-sm" - disabled={!!startDate || !!endDate} - /> -
- -
- - handleFilterChange(setStartDate, e.target.value)} - className="w-[140px] text-sm" - disabled={!!filterDate} - /> -
- -
- - handleFilterChange(setEndDate, e.target.value)} - className="w-[140px] text-sm" - disabled={!!filterDate} - /> -
- -
- - -
- -
+ +
+
+ + setSearchText(e.target.value)} + className="text-base h-10 bg-background w-full" + /> +
+ +
+ + +
+ +
+
+ Specific Day + + dateMode === 'preferred' ? setFilterDate(e.target.value) : setCreatedDate(e.target.value) + } + className="text-sm h-8" + disabled={dateMode === 'preferred' ? !!startDate || !!endDate : !!createdStartDate || !!createdEndDate} + /> +
+ +
+ From Range + + dateMode === 'preferred' ? setStartDate(e.target.value) : setCreatedStartDate(e.target.value) + } + className="text-sm h-8" + disabled={dateMode === 'preferred' ? !!filterDate : !!createdDate} + /> +
+ +
+ To Range + + dateMode === 'preferred' ? setEndDate(e.target.value) : setCreatedEndDate(e.target.value) + } + className="text-sm h-8" + disabled={dateMode === 'preferred' ? !!filterDate : !!createdDate} + /> +
+
+ +
+ + +
+
- +
- +
- Requested Date - Patient Details - Requested Package - Age/Gender - Message + Preferred Date + Booked Date + Patient Details + Requested Package + Demographics + Message {loading ? ( - - + + ) : inquiries.length === 0 ? ( - - No inquiries found for the selected criteria + + No package inquiries matched your active search filters. ) : ( inquiries.map((inq) => ( - + -
- {new Date(inq.preferredDate).toLocaleDateString()} -
-
- Submitted: {new Date(inq.createdAt).toLocaleDateString()} + {inq.preferredDate ? ( +
+ {new Date(inq.preferredDate).toLocaleDateString('en-GB')} +
+ ) : ( + Not specified + )} + + +
+ {new Date(inq.createdAt).toLocaleDateString('en-GB')}
-
{inq.fullName}
-
{inq.mobileNumber}
-
{inq.email || '-'}
+
{inq.fullName}
+
{inq.mobileNumber}
+ {inq.email &&
{inq.email}
}
-
{inq.healthPackage?.name || 'N/A'}
+
+ {inq.healthPackage?.name || 'N/A'} +
+ {inq.healthPackage?.category?.name && ( + + {inq.healthPackage.category.name} + + )}
-
- {inq.age} yrs / {inq.gender} +
{inq.age ? `${inq.age} yrs` : '—'}
+
+ {inq.gender || 'unspecified'}
- - - -
+ + + + +
+ {inq.message || 'No message provided.'} +
+
+ {inq.message || 'No message provided.'} -
-
- - - {inq.message || 'No message provided.'} - -
-
+ + + + )) )} @@ -187,10 +277,10 @@ export default function PackageInquiriesTab() { {totalItems} inquiries
-
+
Page {currentPage} of {totalPages || 1}
-
+
-
+ + +
+ + setSearchText(e.target.value)} + className="text-base h-10" + /> +
+ +
+ + +
+ +
+
+ Specific Day + + dateMode === 'scheduled' ? setFilterDate(e.target.value) : setCreatedDate(e.target.value) + } + className="text-sm h-8 bg-background" + disabled={dateMode === 'scheduled' ? !!startDate || !!endDate : !!createdStartDate || !!createdEndDate} + /> +
+ +
+ From Range + + dateMode === 'scheduled' ? setStartDate(e.target.value) : setCreatedStartDate(e.target.value) + } + className="text-sm h-8 bg-background" + disabled={dateMode === 'scheduled' ? !!filterDate : !!createdDate} + /> +
+ +
+ To Range + + dateMode === 'scheduled' ? setEndDate(e.target.value) : setCreatedEndDate(e.target.value) + } + className="text-sm h-8 bg-background" + disabled={dateMode === 'scheduled' ? !!filterDate : !!createdDate} + /> +
+
+ +
+ + +
+
+
+ - - Appointment List + + + Appointment Registrations +
-
+
ID Patient Doctor - Date - Message - Actions + Scheduled Date + Booked Date + Message + Actions {loading ? ( - - + + ) : filteredAppointments.length === 0 ? ( - - No appointments found + + No appointments found matching your selected criteria. ) : ( filteredAppointments.map((item) => ( - + {item.id}
{item.name}
@@ -218,13 +272,24 @@ export default function AppointmentPage() {
{item.department?.name}
-
{new Date(item.date).toLocaleDateString()}
+
+ {new Date(item.date).toLocaleDateString()} +
+
+ +
+ {new Date(item.createdAt).toLocaleDateString()} +
{item.message || '-'}
-
+
@@ -246,34 +311,34 @@ export default function AppointmentPage() {
{!loading && totalItems > 0 && ( -
-
+
+
Showing {indexOfFirstItem + 1} to{' '} {Math.min(currentPage * itemsPerPage, totalItems)} of{' '} - {totalItems} + {totalItems} entries
-
+
Page {currentPage} of {totalPages}
-
+
@@ -298,10 +363,12 @@ export default function AppointmentPage() {

{viewData.email || 'No email provided'}

-

Appointment Date

-

{new Date(viewData.date).toLocaleDateString()}

-

- Booked on: {new Date(viewData.createdAt).toLocaleString()} +

Key Datestamps

+

+ Scheduled: {new Date(viewData.date).toLocaleDateString()} +

+

+ Record Created: {new Date(viewData.createdAt).toLocaleString()}