Merge pull request 'fix: Maintenance dates display one day off due to timezone conversion (#237)' (#238) from issue-237-fix-date-timezone into main
All checks were successful
Deploy to Staging / Build Images (push) Successful in 18s
Deploy to Staging / Deploy to Staging (push) Successful in 23s
Deploy to Staging / Verify Staging (push) Successful in 4s
Deploy to Staging / Notify Staging Ready (push) Successful in 4s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
Mirror Base Images / Mirror Base Images (push) Successful in 39s

Reviewed-on: #238
This commit was merged in pull request #238.
This commit is contained in:
2026-03-24 12:54:59 +00:00
6 changed files with 18 additions and 14 deletions

View File

@@ -190,7 +190,7 @@ export const MaintenanceRecordForm: React.FC<MaintenanceRecordFormProps> = ({ ve
const doc = await documentsApi.create({ const doc = await documentsApi.create({
vehicleId: data.vehicle_id, vehicleId: data.vehicle_id,
documentType: 'manual', documentType: 'manual',
title: `Maintenance Receipt - ${new Date(data.date).toLocaleDateString()}`, title: `Maintenance Receipt - ${dayjs(data.date).format('M/D/YYYY')}`,
}); });
await documentsApi.upload(doc.id, capturedReceiptFile); await documentsApi.upload(doc.id, capturedReceiptFile);
receiptDocumentId = doc.id; receiptDocumentId = doc.id;

View File

@@ -4,6 +4,7 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import dayjs from 'dayjs';
import { import {
Card, Card,
CardContent, CardContent,
@@ -74,14 +75,14 @@ export const MaintenanceRecordsList: React.FC<MaintenanceRecordsListProps> = ({
// Sort records by date DESC (newest first) // Sort records by date DESC (newest first)
const sortedRecords = [...records].sort( const sortedRecords = [...records].sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() (a, b) => b.date.localeCompare(a.date)
); );
return ( return (
<> <>
<Stack spacing={2}> <Stack spacing={2}>
{sortedRecords.map((record) => { {sortedRecords.map((record) => {
const dateText = new Date(record.date).toLocaleDateString(); const dateText = dayjs(record.date).format('M/D/YYYY');
const categoryDisplay = getCategoryDisplayName(record.category); const categoryDisplay = getCategoryDisplayName(record.category);
const subtypeCount = record.subtypeCount || record.subtypes?.length || 0; const subtypeCount = record.subtypeCount || record.subtypes?.length || 0;
@@ -204,7 +205,7 @@ export const MaintenanceRecordsList: React.FC<MaintenanceRecordsListProps> = ({
</Typography> </Typography>
{recordToDelete && ( {recordToDelete && (
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{new Date(recordToDelete.date).toLocaleDateString()} -{' '} {dayjs(recordToDelete.date).format('M/D/YYYY')} -{' '}
{getCategoryDisplayName(recordToDelete.category)} {getCategoryDisplayName(recordToDelete.category)}
</Typography> </Typography>
)} )}

View File

@@ -107,17 +107,17 @@ function parseServiceDate(value: string | number | null): string | undefined {
// Try standard parsing // Try standard parsing
const date = new Date(dateStr); const date = new Date(dateStr);
if (!isNaN(date.getTime())) { if (!isNaN(date.getTime())) {
return date.toISOString().split('T')[0]; const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
} }
// Try MM/DD/YYYY format // Try MM/DD/YYYY format
const mdyMatch = dateStr.match(/(\d{1,2})\/(\d{1,2})\/(\d{4})/); const mdyMatch = dateStr.match(/(\d{1,2})\/(\d{1,2})\/(\d{4})/);
if (mdyMatch) { if (mdyMatch) {
const [, month, day, year] = mdyMatch; const [, month, day, year] = mdyMatch;
const parsed = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
if (!isNaN(parsed.getTime())) {
return parsed.toISOString().split('T')[0];
}
} }
return undefined; return undefined;

View File

@@ -3,6 +3,7 @@
*/ */
import React from 'react'; import React from 'react';
import dayjs from 'dayjs';
import type { OwnershipCost, OwnershipCostType } from '../types/ownership-costs.types'; import type { OwnershipCost, OwnershipCostType } from '../types/ownership-costs.types';
import { useOwnershipCostsList, useDeleteOwnershipCost } from '../hooks/useOwnershipCosts'; import { useOwnershipCostsList, useDeleteOwnershipCost } from '../hooks/useOwnershipCosts';
@@ -42,7 +43,7 @@ export const OwnershipCostsList: React.FC<OwnershipCostsListProps> = ({ vehicleI
}; };
const formatDate = (dateString: string): string => { const formatDate = (dateString: string): string => {
return new Date(dateString).toLocaleDateString(); return dayjs(dateString).format('M/D/YYYY');
}; };
if (isLoading) { if (isLoading) {

View File

@@ -3,6 +3,7 @@
*/ */
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import dayjs from 'dayjs';
import { Box, Typography, Button, Card, CardContent, Divider, FormControl, InputLabel, Select, MenuItem, List, ListItem, ListItemButton, Dialog, DialogTitle, DialogContent } from '@mui/material'; import { Box, Typography, Button, Card, CardContent, Divider, FormControl, InputLabel, Select, MenuItem, List, ListItem, ListItemButton, Dialog, DialogTitle, DialogContent } from '@mui/material';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { Vehicle } from '../types/vehicles.types'; import { Vehicle } from '../types/vehicles.types';
@@ -123,7 +124,7 @@ export const VehicleDetailMobile: React.FC<VehicleDetailMobileProps> = ({
}); });
} }
} }
return list.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); return list.sort((a, b) => b.date.localeCompare(a.date));
}, [fuelLogs]); }, [fuelLogs]);
const filteredRecords = useMemo(() => { const filteredRecords = useMemo(() => {
@@ -286,7 +287,7 @@ export const VehicleDetailMobile: React.FC<VehicleDetailMobileProps> = ({
</Box> </Box>
{/* Secondary line: Grade • Date • Type */} {/* Secondary line: Grade • Date • Type */}
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.25 }}> <Typography variant="body2" color="text.secondary" sx={{ mt: 0.25 }}>
{rec.secondary || `${new Date(rec.date).toLocaleDateString()}${rec.type}`} {rec.secondary || `${dayjs(rec.date).format('M/D/YYYY')}${rec.type}`}
</Typography> </Typography>
</Box> </Box>
</ListItemButton> </ListItemButton>

View File

@@ -3,6 +3,7 @@
*/ */
import React, { useMemo, useState, useEffect } from 'react'; import React, { useMemo, useState, useEffect } from 'react';
import dayjs from 'dayjs';
import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { Box, Typography, Button as MuiButton, Divider, FormControl, InputLabel, Select, MenuItem, Table, TableHead, TableRow, TableCell, TableBody, Dialog, DialogTitle, DialogContent, useMediaQuery, IconButton } from '@mui/material'; import { Box, Typography, Button as MuiButton, Divider, FormControl, InputLabel, Select, MenuItem, Table, TableHead, TableRow, TableCell, TableBody, Dialog, DialogTitle, DialogContent, useMediaQuery, IconButton } from '@mui/material';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
@@ -132,7 +133,7 @@ export const VehicleDetailPage: React.FC = () => {
} }
} }
return list.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); return list.sort((a, b) => b.date.localeCompare(a.date));
}, [fuelLogs, documents]); }, [fuelLogs, documents]);
const filteredRecords = useMemo(() => { const filteredRecords = useMemo(() => {
@@ -490,7 +491,7 @@ export const VehicleDetailPage: React.FC = () => {
)} )}
{!isFuelLoading && !isDocumentsLoading && filteredRecords.map((rec) => ( {!isFuelLoading && !isDocumentsLoading && filteredRecords.map((rec) => (
<TableRow key={rec.id} hover sx={{ cursor: rec.type === 'Documents' ? 'default' : 'pointer' }} onClick={() => handleRowClick(rec.id, rec.type)}> <TableRow key={rec.id} hover sx={{ cursor: rec.type === 'Documents' ? 'default' : 'pointer' }} onClick={() => handleRowClick(rec.id, rec.type)}>
<TableCell>{new Date(rec.date).toLocaleDateString()}</TableCell> <TableCell>{dayjs(rec.date).format('M/D/YYYY')}</TableCell>
<TableCell>{rec.type}</TableCell> <TableCell>{rec.type}</TableCell>
<TableCell>{rec.summary}</TableCell> <TableCell>{rec.summary}</TableCell>
<TableCell align="right">{rec.amount || '—'}</TableCell> <TableCell align="right">{rec.amount || '—'}</TableCell>