/** * @ai-summary Admin Vehicle Catalog mobile screen with search-first UI * @ai-context Uses server-side search and pagination, optimized for touch */ import React, { useState, useCallback, useRef } from 'react'; import { Navigate } from 'react-router-dom'; import { Search, FileDownload, FileUpload, Delete, MoreVert, Close, History, ExpandMore, ExpandLess, } from '@mui/icons-material'; import toast from 'react-hot-toast'; import { useAdminAccess } from '../../../core/auth/useAdminAccess'; import { MobileContainer } from '../../../shared-minimal/components/mobile/MobileContainer'; import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard'; import { AuditLogDrawer } from '../components/AuditLogDrawer'; import { useCatalogSearch, useExportCatalog, useImportPreview, useImportApply, } from '../hooks/useCatalog'; import { adminApi } from '../api/admin.api'; import { CatalogSearchResult, ImportPreviewResult, ImportApplyResult, } from '../types/admin.types'; export const AdminCatalogMobileScreen: React.FC = () => { const { loading: authLoading, isAdmin } = useAdminAccess(); // Search state const [searchInput, setSearchInput] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [page, setPage] = useState(1); const pageSize = 25; // UI state const [menuOpen, setMenuOpen] = useState(false); const [auditDrawerOpen, setAuditDrawerOpen] = useState(false); // Delete state const [deleteSheet, setDeleteSheet] = useState<{ open: boolean; item: CatalogSearchResult | null; }>({ open: false, item: null }); const [deleting, setDeleting] = useState(false); // Import state const [importSheet, setImportSheet] = useState(false); const [importPreview, setImportPreview] = useState(null); const [importResult, setImportResult] = useState(null); const [errorsExpanded, setErrorsExpanded] = useState(false); const fileInputRef = useRef(null); // Hooks const { data: searchData, isLoading: searchLoading, refetch } = useCatalogSearch( searchQuery, page, pageSize ); const exportMutation = useExportCatalog(); const importPreviewMutation = useImportPreview(); const importApplyMutation = useImportApply(); // Search handlers const handleSearch = useCallback(() => { setPage(1); setSearchQuery(searchInput); }, [searchInput]); const handleSearchKeyDown = useCallback( (event: React.KeyboardEvent) => { if (event.key === 'Enter') { handleSearch(); } }, [handleSearch] ); const handleClearSearch = useCallback(() => { setSearchInput(''); setSearchQuery(''); setPage(1); }, []); // Pagination const handleLoadMore = useCallback(() => { setPage((prev) => prev + 1); }, []); // Delete handlers const handleDeleteClick = useCallback((item: CatalogSearchResult) => { setDeleteSheet({ open: true, item }); setMenuOpen(false); }, []); const handleDeleteConfirm = useCallback(async () => { if (!deleteSheet.item) return; setDeleting(true); try { await adminApi.deleteEngine(deleteSheet.item.id.toString()); toast.success('Configuration deleted'); setDeleteSheet({ open: false, item: null }); refetch(); } catch { toast.error('Failed to delete configuration'); } finally { setDeleting(false); } }, [deleteSheet.item, refetch]); // Import handlers const handleImportClick = useCallback(() => { setMenuOpen(false); fileInputRef.current?.click(); }, []); const handleFileSelect = useCallback( async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; try { const result = await importPreviewMutation.mutateAsync(file); setImportPreview(result); setImportSheet(true); } catch { // Error handled by mutation } if (fileInputRef.current) { fileInputRef.current.value = ''; } }, [importPreviewMutation] ); const handleImportConfirm = useCallback(async () => { if (!importPreview?.previewId) return; try { const result = await importApplyMutation.mutateAsync(importPreview.previewId); setImportResult(result); if (result.errors.length > 0) { toast.error( `Import completed with ${result.errors.length} error(s): ${result.created} created, ${result.updated} updated` ); // Keep sheet open for error review } else { toast.success( `Import completed successfully: ${result.created} created, ${result.updated} updated` ); // Auto-close on complete success setImportSheet(false); setImportPreview(null); setImportResult(null); } refetch(); } catch { // Error handled by mutation's onError } }, [importPreview, importApplyMutation, refetch]); const handleImportSheetClose = useCallback(() => { if (importApplyMutation.isPending) return; setImportSheet(false); setImportPreview(null); setImportResult(null); setErrorsExpanded(false); }, [importApplyMutation.isPending]); // Export handler const handleExport = useCallback(() => { setMenuOpen(false); exportMutation.mutate(); }, [exportMutation]); // Auth loading if (authLoading) { return (
Loading admin access...
); } // Not admin if (!isAdmin) { return ; } const items = searchData?.items || []; const total = searchData?.total || 0; const hasMore = items.length < total; const hasResults = searchQuery.length > 0 && items.length > 0; const noResults = searchQuery.length > 0 && items.length === 0 && !searchLoading; return (
{/* Header */}

Vehicle Catalog

{searchQuery ? `${total} results` : 'Search to view vehicles'}

{/* Search Bar */}
setSearchInput(e.target.value)} onKeyDown={handleSearchKeyDown} className="w-full pl-10 pr-10 py-3 border border-slate-300 rounded-lg text-slate-800 focus:outline-none focus:ring-2 focus:ring-blue-500" style={{ minHeight: '44px' }} /> {searchInput && ( )}
{/* Instructions when no search */} {!searchQuery && (

Search for Vehicles

Enter a search term like "2024 Toyota Camry" to find vehicles in the catalog.

Use the menu for import/export options.

)} {/* No Results */} {noResults && (

No Results Found

No vehicles match "{searchQuery}". Try a different search term.

)} {/* Search Results */} {hasResults && (
{items.map((item) => (

{item.year} {item.make} {item.model}

{item.trim}

{item.engineName && ( {item.engineName} )} {item.transmissionType && ( {item.transmissionType} )}
))} {/* Load More */} {hasMore && ( )}
)} {/* Hidden file input */}
{/* Menu Sheet */} {menuOpen && (
e.stopPropagation()} >

Options

)} {/* Delete Confirmation Sheet */} {deleteSheet.open && deleteSheet.item && (

Delete Configuration?

Are you sure you want to delete{' '} {deleteSheet.item.year} {deleteSheet.item.make} {deleteSheet.item.model}{' '} {deleteSheet.item.trim} ?

)} {/* Import Preview/Results Sheet */} {importSheet && (importPreview || importResult) && (

{importResult ? 'Import Results' : 'Import Preview'}

{/* Preview Mode */} {importPreview && !importResult && ( <>
{importPreview.toCreate.length} to create
{importPreview.toUpdate.length} to update
{importPreview.errors.length > 0 && (

{importPreview.errors.length} Error(s) Found:

    {importPreview.errors.slice(0, 5).map((err, idx) => (
  • Row {err.row}: {err.error}
  • ))} {importPreview.errors.length > 5 && (
  • ...and {importPreview.errors.length - 5} more errors
  • )}
)} {importPreview.valid ? (

The import file is valid and ready to be applied.

) : (

Please fix the errors above before importing.

)} )} {/* Results Mode */} {importResult && ( <>
{importResult.created} created
{importResult.updated} updated
{importResult.errors.length > 0 && (
{errorsExpanded && (
    {importResult.errors.map((err, idx) => (
  • Row {err.row}: {err.error}
  • ))}
)}
)} {importResult.errors.length === 0 && (

Import completed successfully with no errors.

)} )} {/* Action Buttons */}
{!importResult && ( )}
)} {/* Audit Log Drawer */} setAuditDrawerOpen(false)} resourceType="catalog" />
); };