import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; import { json } from "@remix-run/node"; import { useLoaderData, useSearchParams } from "@remix-run/react"; import { requireAuthLevel } from "~/utils/auth.server"; import DashboardLayout from "~/components/DashboardLayout"; import ReportSheetViewModal from "~/components/ReportSheetViewModal"; import { useState } from "react"; import { prisma } from "~/utils/db.server"; export const meta: MetaFunction = () => [{ title: "Report Sheets - Alhaffer Reporting System" }]; interface ReportSheet { id: string; date: string; area: string; dredgerLocation: string; reclamationLocation: string; status: string; dayReport?: any; nightReport?: any; } export const loader = async ({ request }: LoaderFunctionArgs) => { const user = await requireAuthLevel(request, 1); // Parse URL search parameters for filters const url = new URL(request.url); const dateFrom = url.searchParams.get('dateFrom'); const dateTo = url.searchParams.get('dateTo'); const status = url.searchParams.get('status'); const areaId = url.searchParams.get('areaId'); const employeeId = url.searchParams.get('employeeId'); const dredgerLocationId = url.searchParams.get('dredgerLocationId'); // Build where clause based on filters const whereClause: any = {}; // Date range filter if (dateFrom || dateTo) { if (dateFrom) { whereClause.date = { gte: dateFrom }; } if (dateTo) { whereClause.date = { ...whereClause.date, lte: dateTo }; } } // Status filter if (status && status !== 'all') { whereClause.status = status; } // Area filter if (areaId && areaId !== 'all') { whereClause.areaId = parseInt(areaId); } // Dredger location filter if (dredgerLocationId && dredgerLocationId !== 'all') { whereClause.dredgerLocationId = parseInt(dredgerLocationId); } // Get filtered sheets with related data let sheets = await prisma.sheet.findMany({ where: whereClause, orderBy: { createdAt: 'desc' }, include: { area: { select: { name: true } }, dredgerLocation: { select: { name: true, class: true } }, reclamationLocation: { select: { name: true } }, dayShift: { include: { employee: { select: { name: true } }, area: { select: { name: true } }, dredgerLocation: { select: { name: true, class: true } }, reclamationLocation: { select: { name: true } } } }, nightShift: { include: { employee: { select: { name: true } }, area: { select: { name: true } }, dredgerLocation: { select: { name: true, class: true } }, reclamationLocation: { select: { name: true } } } } } }); // Apply employee filter if specified if (employeeId && employeeId !== 'all') { sheets = sheets.filter((sheet: any) => (sheet.dayShift && sheet.dayShift.employeeId === parseInt(employeeId)) || (sheet.nightShift && sheet.nightShift.employeeId === parseInt(employeeId)) ); } // Filter sheets for level 1 users (only show sheets where user has at least one shift) if (user.authLevel === 1) { sheets = sheets.filter((sheet: any) => (sheet.dayShift && sheet.dayShift.employeeId === user.id) || (sheet.nightShift && sheet.nightShift.employeeId === user.id) ); } // Get dropdown data for filters const [areas, dredgerLocations, employees] = await Promise.all([ prisma.area.findMany({ orderBy: { name: 'asc' } }), prisma.dredgerLocation.findMany({ orderBy: { name: 'asc' } }), prisma.employee.findMany({ where: { status: 'active' }, select: { id: true, name: true }, orderBy: { name: 'asc' } }) ]); // Transform sheets to match the expected interface const transformedSheets = sheets.map((sheet: any) => ({ id: sheet.id.toString(), date: sheet.date, area: sheet.area.name, dredgerLocation: sheet.dredgerLocation.name, reclamationLocation: sheet.reclamationLocation.name, status: sheet.status, dayReport: sheet.dayShift, nightReport: sheet.nightShift })); return json({ user, sheets: transformedSheets, areas, dredgerLocations, employees, filters: { dateFrom, dateTo, status, areaId, employeeId, dredgerLocationId } }); }; export default function ReportSheet() { const { user, sheets, areas, dredgerLocations, employees, filters } = useLoaderData(); const [viewingSheet, setViewingSheet] = useState(null); const [showViewModal, setShowViewModal] = useState(false); const [searchParams, setSearchParams] = useSearchParams(); const [showFilters, setShowFilters] = useState(false); const handleView = (sheet: ReportSheet) => { setViewingSheet(sheet); setShowViewModal(true); }; const handleCloseViewModal = () => { setShowViewModal(false); setViewingSheet(null); }; // Filter functions const handleFilterChange = (filterName: string, value: string) => { const newSearchParams = new URLSearchParams(searchParams); if (value === '' || value === 'all') { newSearchParams.delete(filterName); } else { newSearchParams.set(filterName, value); } setSearchParams(newSearchParams); }; const clearAllFilters = () => { setSearchParams(new URLSearchParams()); }; const hasActiveFilters = () => { return filters.dateFrom || filters.dateTo || filters.status || filters.areaId || filters.employeeId || filters.dredgerLocationId; }; // Get today's date for date input max values const today = new Date().toISOString().split('T')[0]; const getShiftBadge = (shift: string) => { return shift === "day" ? "bg-yellow-100 text-yellow-800" : "bg-blue-100 text-blue-800"; }; const getShiftIcon = (shift: string) => { return shift === "day" ? ( ) : ( ); }; return (

Report Sheets

View grouped reports by location and date

{/* Filter Section */}
{hasActiveFilters() && ( {Object.values(filters).filter(Boolean).length} active )}
{hasActiveFilters() && ( )}
{showFilters && (
{/* Date From */}
handleFilterChange('dateFrom', e.target.value)} className="block w-full text-sm border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" />
{/* Date To */}
handleFilterChange('dateTo', e.target.value)} className="block w-full text-sm border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" />
{/* Status */}
{/* Area */}
{/* Employee */}
{/* Dredger Location */}
{/* Results Summary */}

Showing {sheets.length} sheet{sheets.length !== 1 ? 's' : ''} {hasActiveFilters() && ' matching your filters'}

)}
{/* Report Sheets Table - Desktop */}
{sheets.map((sheet) => ( ))}
Date Area Locations Available Shifts Status Employees Actions
{new Date(sheet.date).toLocaleDateString('en-GB')}
{sheet.area}
Dredger: {sheet.dredgerLocation}
Reclamation: {sheet.reclamationLocation}
{sheet.dayReport && ( {getShiftIcon('day')} Day )} {sheet.nightReport && ( {getShiftIcon('night')} Night )}
{sheet.status === 'completed' ? ( ) : ( )} {sheet.status.charAt(0).toUpperCase() + sheet.status.slice(1)}
{sheet.dayReport && (
Day: {sheet.dayReport.employee.name}
)} {sheet.nightReport && (
Night: {sheet.nightReport.employee.name}
)}
{/* Report Sheets Cards - Mobile */}
{sheets.map((sheet) => (
{new Date(sheet.date).toLocaleDateString('en-GB')}
{sheet.area}
{sheet.status === 'completed' ? ( ) : ( )} {sheet.status.charAt(0).toUpperCase() + sheet.status.slice(1)}
Dredger: {sheet.dredgerLocation}
Reclamation: {sheet.reclamationLocation}
Shifts:
{sheet.dayReport && ( {getShiftIcon('day')} Day )} {sheet.nightReport && ( {getShiftIcon('night')} Night )}
{sheet.dayReport && (
Day Employee: {sheet.dayReport.employee.name}
)} {sheet.nightReport && (
Night Employee: {sheet.nightReport.employee.name}
)}
))}
{sheets.length === 0 && (

No report sheets

Report sheets will appear here when reports are created.

)}
{/* View Modal */}
); }