diff --git a/backend/src/routes/docket.ts b/backend/src/routes/docket.ts index 32a9375f..3d310bd4 100644 --- a/backend/src/routes/docket.ts +++ b/backend/src/routes/docket.ts @@ -7,6 +7,23 @@ import Joi from 'joi'; const router = Router(); +// Helper function to normalize date input to avoid timezone issues +function normalizeDate(dateInput: any): Date { + if (typeof dateInput === 'string') { + // If it's a string in YYYY-MM-DD format, create date in local time + const [year, month, day] = dateInput.split('-'); + return new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); + } + if (dateInput instanceof Date) { + // If it's already a Date object, extract date parts and recreate to avoid timezone issues + const year = dateInput.getFullYear(); + const month = dateInput.getMonth(); + const day = dateInput.getDate(); + return new Date(year, month, day); + } + return dateInput; +} + // Validation schemas const createDocketEntrySchema = Joi.object({ date: Joi.date().required(), @@ -89,8 +106,14 @@ router.post('/', authenticateToken, asyncHandler(async (req: AuthenticatedReques }); } + // Normalize date to avoid timezone issues + const normalizedData = { ...value }; + if (value.date) { + normalizedData.date = normalizeDate(value.date); + } + const entry = await prisma.docketEntry.create({ - data: value, + data: normalizedData, include: { documents: { orderBy: { @@ -139,10 +162,16 @@ router.put('/:id', authenticateToken, asyncHandler(async (req: AuthenticatedRequ }); } + // Normalize date to avoid timezone issues + const normalizedData = { ...value }; + if (value.date) { + normalizedData.date = normalizeDate(value.date); + } + try { const entry = await prisma.docketEntry.update({ where: { id: entryId }, - data: value, + data: normalizedData, include: { documents: { orderBy: { diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index a96ae626..e17f655c 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -34,11 +34,23 @@ export default function HomePage() { title: string; url: string; } | null>(null); + const [isMobile, setIsMobile] = useState(false); useEffect(() => { fetchDocketEntries(); }, []); + useEffect(() => { + const checkMobile = () => { + setIsMobile(window.innerWidth < 768); + }; + + checkMobile(); + window.addEventListener('resize', checkMobile); + + return () => window.removeEventListener('resize', checkMobile); + }, []); + const fetchDocketEntries = async () => { try { const response = await fetch('/api/docket-entries'); @@ -94,7 +106,18 @@ export default function HomePage() { }; const formatDate = (dateString: string) => { - return new Date(dateString).toLocaleDateString('en-US', { + // Extract date parts directly to avoid timezone conversion issues + const datePart = dateString.split('T')[0]; // Get just YYYY-MM-DD part + if (!datePart) return 'Invalid Date'; + + const parts = datePart.split('-'); + if (parts.length !== 3) return 'Invalid Date'; + + const [year, month, day] = parts; + if (!year || !month || !day) return 'Invalid Date'; + + const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', @@ -103,10 +126,26 @@ export default function HomePage() { const getLastUpdated = () => { if (docketEntries.length === 0) return 'No entries'; - const latest = docketEntries[docketEntries.length - 1]; + const latest = docketEntries.length > 0 ? docketEntries[docketEntries.length - 1] : null; return latest ? formatDate(latest.date) : 'No entries'; }; + const handleDocumentClick = (doc: Document) => { + const documentUrl = `/api/documents/${doc.id}/download`; + + if (isMobile) { + // On mobile, open PDF directly in new tab (MCDI approach) + window.open(documentUrl, '_blank'); + } else { + // On desktop, use modal viewer + setSelectedDocument({ + id: doc.id, + title: doc.title, + url: documentUrl + }); + } + }; + if (loading) { return (
@@ -446,11 +485,7 @@ export default function HomePage() { .map((doc) => (