Notification updates

This commit is contained in:
Eric Gullickson
2025-12-21 19:56:52 -06:00
parent 144f1d5bb0
commit 719c80ecd8
80 changed files with 7552 additions and 678 deletions

View File

@@ -63,9 +63,9 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
category: record.category,
subtypes: record.subtypes,
date: record.date,
odometer_reading: record.odometer_reading || undefined,
odometerReading: record.odometerReading || undefined,
cost: record.cost ? Number(record.cost) : undefined,
shop_name: record.shop_name || undefined,
shopName: record.shopName || undefined,
notes: record.notes || undefined,
});
setError(null);
@@ -172,7 +172,7 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
fullWidth
disabled
value={(() => {
const vehicle = vehicles?.find((v: Vehicle) => v.id === record.vehicle_id);
const vehicle = vehicles?.find((v: Vehicle) => v.id === record.vehicleId);
if (!vehicle) return 'Unknown Vehicle';
if (vehicle.nickname?.trim()) return vehicle.nickname.trim();
const parts = [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean);
@@ -246,10 +246,10 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
label="Odometer Reading"
type="number"
fullWidth
value={formData.odometer_reading || ''}
value={formData.odometerReading || ''}
onChange={(e) =>
handleInputChange(
'odometer_reading',
'odometerReading',
e.target.value ? parseInt(e.target.value) : undefined
)
}
@@ -278,8 +278,8 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
<TextField
label="Shop/Location"
fullWidth
value={formData.shop_name || ''}
onChange={(e) => handleInputChange('shop_name', e.target.value || undefined)}
value={formData.shopName || ''}
onChange={(e) => handleInputChange('shopName', e.target.value || undefined)}
helperText="Service location"
inputProps={{ maxLength: 200 }}
/>

View File

@@ -92,13 +92,13 @@ export const MaintenanceRecordForm: React.FC = () => {
const onSubmit = async (data: FormData) => {
try {
const payload: CreateMaintenanceRecordRequest = {
vehicle_id: data.vehicle_id,
vehicleId: data.vehicle_id,
category: data.category as MaintenanceCategory,
subtypes: data.subtypes,
date: data.date,
odometer_reading: data.odometer_reading ? Number(data.odometer_reading) : undefined,
odometerReading: data.odometer_reading ? Number(data.odometer_reading) : undefined,
cost: data.cost ? Number(data.cost) : undefined,
shop_name: data.shop_name || undefined,
shopName: data.shop_name || undefined,
notes: data.notes || undefined,
};

View File

@@ -83,7 +83,7 @@ export const MaintenanceRecordsList: React.FC<MaintenanceRecordsListProps> = ({
{sortedRecords.map((record) => {
const dateText = new Date(record.date).toLocaleDateString();
const categoryDisplay = getCategoryDisplayName(record.category);
const subtypeCount = record.subtype_count || record.subtypes?.length || 0;
const subtypeCount = record.subtypeCount || record.subtypes?.length || 0;
return (
<Card key={record.id} variant="outlined">
@@ -105,9 +105,9 @@ export const MaintenanceRecordsList: React.FC<MaintenanceRecordsListProps> = ({
{categoryDisplay} ({subtypeCount})
</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap" sx={{ mt: 1 }}>
{record.odometer_reading && (
{record.odometerReading && (
<Chip
label={`${Number(record.odometer_reading).toLocaleString()} miles`}
label={`${Number(record.odometerReading).toLocaleString()} miles`}
size="small"
variant="outlined"
/>
@@ -120,9 +120,9 @@ export const MaintenanceRecordsList: React.FC<MaintenanceRecordsListProps> = ({
variant="outlined"
/>
)}
{record.shop_name && (
{record.shopName && (
<Chip
label={record.shop_name}
label={record.shopName}
size="small"
variant="outlined"
/>

View File

@@ -81,9 +81,9 @@ export const useMaintenanceRecords = (vehicleId?: string) => {
const createRecordMutation = useMutation({
mutationFn: (data: CreateMaintenanceRecordRequest) => maintenanceApi.createRecord(data),
onSuccess: (_res, variables) => {
queryClient.invalidateQueries({ queryKey: ['maintenanceRecords', variables.vehicle_id] });
queryClient.invalidateQueries({ queryKey: ['maintenanceRecords', variables.vehicleId] });
queryClient.invalidateQueries({ queryKey: ['maintenanceRecords', 'all'] });
queryClient.invalidateQueries({ queryKey: ['maintenanceUpcoming', variables.vehicle_id] });
queryClient.invalidateQueries({ queryKey: ['maintenanceUpcoming', variables.vehicleId] });
},
});
@@ -110,8 +110,8 @@ export const useMaintenanceRecords = (vehicleId?: string) => {
const createScheduleMutation = useMutation({
mutationFn: (data: CreateScheduleRequest) => maintenanceApi.createSchedule(data),
onSuccess: (_res, variables) => {
queryClient.invalidateQueries({ queryKey: ['maintenanceSchedules', variables.vehicle_id] });
queryClient.invalidateQueries({ queryKey: ['maintenanceUpcoming', variables.vehicle_id] });
queryClient.invalidateQueries({ queryKey: ['maintenanceSchedules', variables.vehicleId] });
queryClient.invalidateQueries({ queryKey: ['maintenanceUpcoming', variables.vehicleId] });
},
});

View File

@@ -53,48 +53,49 @@ export const PERFORMANCE_UPGRADE_SUBTYPES = [
'Exterior'
] as const;
// Database record types
// Database record types (camelCase)
export interface MaintenanceRecord {
id: string;
user_id: string;
vehicle_id: string;
userId: string;
vehicleId: string;
category: MaintenanceCategory;
subtypes: string[];
date: string;
odometer_reading?: number;
odometerReading?: number;
cost?: number;
shop_name?: string;
shopName?: string;
notes?: string;
created_at: string;
updated_at: string;
createdAt: string;
updatedAt: string;
}
export interface MaintenanceSchedule {
id: string;
user_id: string;
vehicle_id: string;
userId: string;
vehicleId: string;
category: MaintenanceCategory;
subtypes: string[];
interval_months?: number;
interval_miles?: number;
last_service_date?: string;
last_service_mileage?: number;
next_due_date?: string;
next_due_mileage?: number;
is_active: boolean;
created_at: string;
updated_at: string;
intervalMonths?: number;
intervalMiles?: number;
lastServiceDate?: string;
lastServiceMileage?: number;
nextDueDate?: string;
nextDueMileage?: number;
isActive: boolean;
emailNotifications?: boolean;
createdAt: string;
updatedAt: string;
}
// Request types
// Request types (camelCase)
export interface CreateMaintenanceRecordRequest {
vehicle_id: string;
vehicleId: string;
category: MaintenanceCategory;
subtypes: string[];
date: string;
odometer_reading?: number;
odometerReading?: number;
cost?: number;
shop_name?: string;
shopName?: string;
notes?: string;
}
@@ -102,37 +103,39 @@ export interface UpdateMaintenanceRecordRequest {
category?: MaintenanceCategory;
subtypes?: string[];
date?: string;
odometer_reading?: number | null;
odometerReading?: number | null;
cost?: number | null;
shop_name?: string | null;
shopName?: string | null;
notes?: string | null;
}
export interface CreateScheduleRequest {
vehicle_id: string;
vehicleId: string;
category: MaintenanceCategory;
subtypes: string[];
interval_months?: number;
interval_miles?: number;
intervalMonths?: number;
intervalMiles?: number;
emailNotifications?: boolean;
}
export interface UpdateScheduleRequest {
category?: MaintenanceCategory;
subtypes?: string[];
interval_months?: number | null;
interval_miles?: number | null;
is_active?: boolean;
intervalMonths?: number | null;
intervalMiles?: number | null;
isActive?: boolean;
emailNotifications?: boolean;
}
// Response types
// Response types (camelCase)
export interface MaintenanceRecordResponse extends MaintenanceRecord {
subtype_count: number;
subtypeCount: number;
}
export interface MaintenanceScheduleResponse extends MaintenanceSchedule {
subtype_count: number;
is_due_soon?: boolean;
is_overdue?: boolean;
subtypeCount: number;
isDueSoon?: boolean;
isOverdue?: boolean;
}
// Validation helpers