fix: coerce numeric/decimal columns in repository mappers (refs #241)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 1m49s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 43s
Deploy to Staging / Verify Staging (pull_request) Successful in 4s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 3s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 1m49s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 43s
Deploy to Staging / Verify Staging (pull_request) Successful in 4s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 3s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
node-postgres returns numeric/decimal columns as JavaScript strings, but the TypeScript interfaces for MaintenanceRecord and OwnershipCost declare numeric fields as number. The mappers were passing values through raw, breaking type-safe arithmetic and display (e.g. the amount column on the vehicle summary screen was empty until the recent frontend workaround in PR #240, and OwnershipCostsList silently no-ops toLocaleString on the string). Backend - mapMaintenanceRecord: coerce cost via Number() when non-null. - ownership-costs mapRow: coerce amount via Number(). Frontend (remove now-redundant workarounds) - MaintenanceRecordsList: drop Number() coercion on cost and odometerReading; use the number values directly. - VehicleDetailPage / VehicleDetailMobile: revert the PR #240 cost coercion to the simple typeof number guard now that the backend honors the type. Scope notes - Other repositories with the same pattern (stations, community-stations, fuel-logs enhanced methods) are tracked separately because they have unclear downstream consumers and warrant their own investigation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,16 +115,16 @@ export const MaintenanceRecordsList: React.FC<MaintenanceRecordsListProps> = ({
|
||||
{categoryDisplay} ({subtypeCount})
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" sx={{ mt: 1 }}>
|
||||
{record.odometerReading && (
|
||||
{record.odometerReading != null && (
|
||||
<Chip
|
||||
label={`${Number(record.odometerReading).toLocaleString()} miles`}
|
||||
label={`${record.odometerReading.toLocaleString()} miles`}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
{record.cost && (
|
||||
{record.cost != null && (
|
||||
<Chip
|
||||
label={`$${Number(record.cost).toFixed(2)}`}
|
||||
label={`$${record.cost.toFixed(2)}`}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
|
||||
@@ -142,9 +142,7 @@ export const VehicleDetailMobile: React.FC<VehicleDetailMobileProps> = ({
|
||||
secondaryParts.push(new Date(rec.date).toLocaleDateString());
|
||||
secondaryParts.push('Maintenance');
|
||||
const secondary = secondaryParts.join(' • ');
|
||||
// Backend returns numeric/decimal columns as strings via node-postgres; coerce.
|
||||
const costNum = rec.cost != null ? Number(rec.cost) : NaN;
|
||||
const amount = Number.isFinite(costNum) ? `$${costNum.toFixed(2)}` : undefined;
|
||||
const amount = typeof rec.cost === 'number' ? `$${rec.cost.toFixed(2)}` : undefined;
|
||||
list.push({
|
||||
id: rec.id,
|
||||
type: 'Maintenance',
|
||||
|
||||
@@ -148,9 +148,7 @@ export const VehicleDetailPage: React.FC = () => {
|
||||
if (subtypeText) parts.push(subtypeText);
|
||||
if (rec.shopName) parts.push(rec.shopName);
|
||||
const summary = parts.join(' • ');
|
||||
// Backend returns numeric/decimal columns as strings via node-postgres; coerce.
|
||||
const costNum = rec.cost != null ? Number(rec.cost) : NaN;
|
||||
const amount = Number.isFinite(costNum) ? `$${costNum.toFixed(2)}` : undefined;
|
||||
const amount = typeof rec.cost === 'number' ? `$${rec.cost.toFixed(2)}` : undefined;
|
||||
list.push({ id: rec.id, type: 'Maintenance', date: rec.date, summary, amount });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user