diff --git a/frontend/src/features/documents/mobile/DocumentsMobileScreen.tsx b/frontend/src/features/documents/mobile/DocumentsMobileScreen.tsx index da0aaa7..fc2a42c 100644 --- a/frontend/src/features/documents/mobile/DocumentsMobileScreen.tsx +++ b/frontend/src/features/documents/mobile/DocumentsMobileScreen.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React, { useRef, useMemo } from 'react'; import { useAuth0 } from '@auth0/auth0-react'; import { isAxiosError } from 'axios'; import { useNavigate } from 'react-router-dom'; @@ -7,6 +7,8 @@ import { useDocumentsList } from '../hooks/useDocuments'; import { useUploadWithProgress } from '../hooks/useUploadWithProgress'; import { Button } from '../../../shared-minimal/components/Button'; import { AddDocumentDialog } from '../components/AddDocumentDialog'; +import { useVehicles } from '../../vehicles/hooks/useVehicles'; +import { getVehicleLabel } from '../utils/vehicleLabel'; export const DocumentsMobileScreen: React.FC = () => { console.log('[DocumentsMobileScreen] Component initializing'); @@ -17,12 +19,15 @@ export const DocumentsMobileScreen: React.FC = () => { // Data hooks (unconditional per React rules) const { data, isLoading, error } = useDocumentsList(); + const { data: vehicles } = useVehicles(); const inputRef = useRef(null); const [currentId, setCurrentId] = React.useState(null); const upload = useUploadWithProgress(currentId || ''); const navigate = useNavigate(); const [isAddOpen, setIsAddOpen] = React.useState(false); + const vehiclesMap = useMemo(() => new Map(vehicles?.map(v => [v.id, v]) || []), [vehicles]); + const triggerUpload = (docId: string) => { try { setCurrentId(docId); @@ -170,14 +175,25 @@ export const DocumentsMobileScreen: React.FC = () => { {!isLoading && !hasError && data && data.length > 0 && (
{data.map((doc) => { - const vehicleLabel = doc.vehicleId ? `${doc.vehicleId.slice(0, 8)}...` : '—'; + const vehicle = vehiclesMap.get(doc.vehicleId); + const vehicleLabel = getVehicleLabel(vehicle); + const isShared = doc.sharedVehicleIds.length > 0; return ( -
+
{doc.title}
-
{doc.documentType} • {vehicleLabel}
+
+ {doc.documentType} + {isShared && ' • Shared'} +
+
-
+
{upload.isPending && currentId === doc.id && ( diff --git a/frontend/src/features/documents/pages/DocumentDetailPage.tsx b/frontend/src/features/documents/pages/DocumentDetailPage.tsx index d151e2a..d8da13f 100644 --- a/frontend/src/features/documents/pages/DocumentDetailPage.tsx +++ b/frontend/src/features/documents/pages/DocumentDetailPage.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React, { useRef, useMemo } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useAuth0 } from '@auth0/auth0-react'; import { isAxiosError } from 'axios'; @@ -8,15 +8,21 @@ import { useDocument } from '../hooks/useDocuments'; import { useUploadWithProgress } from '../hooks/useUploadWithProgress'; import { documentsApi } from '../api/documents.api'; import { DocumentPreview } from '../components/DocumentPreview'; +import { useVehicle, useVehicles } from '../../vehicles/hooks/useVehicles'; +import { getVehicleLabel } from '../utils/vehicleLabel'; export const DocumentDetailPage: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { isAuthenticated, isLoading: authLoading, loginWithRedirect } = useAuth0(); const { data: doc, isLoading, error } = useDocument(id); + const { data: vehicle } = useVehicle(doc?.vehicleId || ''); + const { data: vehicles } = useVehicles(); const inputRef = useRef(null); const upload = useUploadWithProgress(id!); + const vehiclesMap = useMemo(() => new Map(vehicles?.map(v => [v.id, v]) || []), [vehicles]); + const handleDownload = async () => { if (!id) return; const blob = await documentsApi.download(id); @@ -141,7 +147,35 @@ export const DocumentDetailPage: React.FC = () => {

{doc.title}

Type: {doc.documentType}
-
Vehicle: {doc.vehicleId}
+
+ Vehicle: + +
+ {doc.sharedVehicleIds.length > 0 && ( +
+
Shared with:
+
    + {doc.sharedVehicleIds.map((vehicleId) => { + const sharedVehicle = vehiclesMap.get(vehicleId); + return ( +
  • + +
  • + ); + })} +
+
+ )}
diff --git a/frontend/src/features/documents/pages/DocumentsPage.tsx b/frontend/src/features/documents/pages/DocumentsPage.tsx index 4aed867..8cc7d07 100644 --- a/frontend/src/features/documents/pages/DocumentsPage.tsx +++ b/frontend/src/features/documents/pages/DocumentsPage.tsx @@ -1,18 +1,23 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { useAuth0 } from '@auth0/auth0-react'; import { useDocumentsList, useDeleteDocument } from '../hooks/useDocuments'; import { Card } from '../../../shared-minimal/components/Card'; import { Button } from '../../../shared-minimal/components/Button'; import { useNavigate } from 'react-router-dom'; import { AddDocumentDialog } from '../components/AddDocumentDialog'; +import { useVehicles } from '../../vehicles/hooks/useVehicles'; +import { getVehicleLabel } from '../utils/vehicleLabel'; export const DocumentsPage: React.FC = () => { const { isAuthenticated, isLoading: authLoading, loginWithRedirect } = useAuth0(); const { data, isLoading, error } = useDocumentsList(); + const { data: vehicles } = useVehicles(); const navigate = useNavigate(); const removeDoc = useDeleteDocument(); const [isAddOpen, setIsAddOpen] = React.useState(false); + const vehiclesMap = useMemo(() => new Map(vehicles?.map(v => [v.id, v]) || []), [vehicles]); + // Show loading while auth is initializing if (authLoading) { return ( @@ -124,19 +129,36 @@ export const DocumentsPage: React.FC = () => { {!isLoading && !error && data && data.length > 0 && (
- {data.map((doc) => ( - -
-
{doc.title}
-
Type: {doc.documentType}
-
Vehicle: {doc.vehicleId}
-
- - + {data.map((doc) => { + const vehicle = vehiclesMap.get(doc.vehicleId); + const vehicleLabel = getVehicleLabel(vehicle); + return ( + +
+
{doc.title}
+
Type: {doc.documentType}
+
+ Vehicle: + +
+ {doc.sharedVehicleIds.length > 0 && ( +
+ Shared with {doc.sharedVehicleIds.length} other vehicle{doc.sharedVehicleIds.length > 1 ? 's' : ''} +
+ )} +
+ + +
-
- - ))} + + ); + })}
)}
diff --git a/frontend/src/features/documents/utils/vehicleLabel.ts b/frontend/src/features/documents/utils/vehicleLabel.ts new file mode 100644 index 0000000..b040fbb --- /dev/null +++ b/frontend/src/features/documents/utils/vehicleLabel.ts @@ -0,0 +1,11 @@ +import type { Vehicle } from '../../vehicles/types/vehicles.types'; + +export const getVehicleLabel = (vehicle: Vehicle | undefined): string => { + 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); + const primary = parts.join(' ').trim(); + if (primary.length > 0) return primary; + if (vehicle.vin?.length > 0) return vehicle.vin; + return vehicle.id.slice(0, 8) + '...'; +};