Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; | |
| import { Badge } from '@/components/ui/badge'; | |
| import { Alert, AlertDescription } from '@/components/ui/alert'; | |
| import { Button } from '@/components/ui/button'; | |
| import { | |
| CheckCircle, | |
| XCircle, | |
| AlertTriangle, | |
| Download, | |
| FileText | |
| } from 'lucide-react'; | |
| import { BulkImportResponse } from '@/types/asset-management'; | |
| interface ImportResultsProps { | |
| result: BulkImportResponse; | |
| onClose?: () => void; | |
| onDownloadErrors?: () => void; | |
| } | |
| const ImportResults: React.FC<ImportResultsProps> = ({ | |
| result, | |
| onClose, | |
| onDownloadErrors | |
| }) => { | |
| const { results } = result; | |
| const hasErrors = results.errors && results.errors.length > 0; | |
| const hasSuccessfulImports = results.successful > 0; | |
| const downloadErrorReport = () => { | |
| if (!hasErrors) return; | |
| const errorReport = results.errors.map(error => ({ | |
| 'Row': error.index + 1, | |
| 'Asset Type': error.asset.assetType, | |
| 'Brand/Model': error.asset.brandModel, | |
| 'Serial Number': error.asset.serialNumber, | |
| 'Error': error.error, | |
| 'Field': error.field || 'General' | |
| })); | |
| const csvContent = [ | |
| Object.keys(errorReport[0]).join(','), | |
| ...errorReport.map(row => Object.values(row).map(val => `"${val}"`).join(',')) | |
| ].join('\n'); | |
| const blob = new Blob([csvContent], { type: 'text/csv' }); | |
| const url = URL.createObjectURL(blob); | |
| const link = document.createElement('a'); | |
| link.href = url; | |
| link.download = `asset_import_errors_${new Date().toISOString().split('T')[0]}.csv`; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| URL.revokeObjectURL(url); | |
| if (onDownloadErrors) { | |
| onDownloadErrors(); | |
| } | |
| }; | |
| return ( | |
| <div className="space-y-6"> | |
| {/* Summary Card */} | |
| <Card> | |
| <CardHeader className="pb-4"> | |
| <CardTitle className="flex items-center gap-2"> | |
| {result.success ? ( | |
| <CheckCircle className="h-5 w-5 text-green-600" /> | |
| ) : ( | |
| <XCircle className="h-5 w-5 text-red-600" /> | |
| )} | |
| Import Results | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| {/* Summary Stats */} | |
| <div className="grid grid-cols-3 gap-4"> | |
| <div className="text-center p-4 bg-muted rounded-lg"> | |
| <div className="text-2xl font-bold">{results.total}</div> | |
| <div className="text-sm text-muted-foreground">Total Assets</div> | |
| </div> | |
| <div className="text-center p-4 bg-green-50 dark:bg-green-950 rounded-lg"> | |
| <div className="text-2xl font-bold text-green-600">{results.successful}</div> | |
| <div className="text-sm text-muted-foreground">Successful</div> | |
| </div> | |
| <div className="text-center p-4 bg-red-50 dark:bg-red-950 rounded-lg"> | |
| <div className="text-2xl font-bold text-red-600">{results.failed}</div> | |
| <div className="text-sm text-muted-foreground">Failed</div> | |
| </div> | |
| </div> | |
| {/* Status Message */} | |
| <Alert variant={result.success ? "default" : "destructive"}> | |
| <AlertTriangle className="h-4 w-4" /> | |
| <AlertDescription> | |
| <div className="font-medium">{result.message}</div> | |
| {hasSuccessfulImports && ( | |
| <div className="mt-1 text-sm"> | |
| {results.successful} asset{results.successful !== 1 ? 's' : ''} have been successfully added to your inventory. | |
| </div> | |
| )} | |
| </AlertDescription> | |
| </Alert> | |
| </CardContent> | |
| </Card> | |
| {/* Successful Imports */} | |
| {hasSuccessfulImports && results.createdAssets && results.createdAssets.length > 0 && ( | |
| <Card> | |
| <CardHeader className="pb-4"> | |
| <CardTitle className="flex items-center gap-2"> | |
| <CheckCircle className="h-5 w-5 text-green-600" /> | |
| Successfully Imported Assets ({results.successful}) | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="space-y-2 max-h-60 overflow-y-auto"> | |
| {results.createdAssets.map((asset, index) => ( | |
| <div key={asset.assetID} className="flex items-center justify-between p-3 bg-green-50 dark:bg-green-950 rounded-lg"> | |
| <div className="flex items-center gap-3"> | |
| <Badge variant="outline">{asset.assetCode}</Badge> | |
| <div> | |
| <div className="font-medium">{asset.assetType}</div> | |
| <div className="text-sm text-muted-foreground"> | |
| {asset.brandModel} - {asset.serialNumber} | |
| </div> | |
| </div> | |
| </div> | |
| <Badge variant="secondary">{asset.assetCondition}</Badge> | |
| </div> | |
| ))} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| {/* Import Errors */} | |
| {hasErrors && ( | |
| <Card> | |
| <CardHeader className="pb-4"> | |
| <CardTitle className="flex items-center justify-between"> | |
| <div className="flex items-center gap-2"> | |
| <XCircle className="h-5 w-5 text-red-600" /> | |
| Import Errors ({results.failed}) | |
| </div> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={downloadErrorReport} | |
| className="flex items-center gap-2" | |
| > | |
| <Download className="h-3 w-3" /> | |
| Download Error Report | |
| </Button> | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="space-y-3 max-h-60 overflow-y-auto"> | |
| {results.errors.map((error, index) => ( | |
| <div key={index} className="p-3 bg-red-50 dark:bg-red-950 rounded-lg"> | |
| <div className="flex items-start justify-between gap-3"> | |
| <div className="flex-1"> | |
| <div className="flex items-center gap-2 mb-1"> | |
| <Badge variant="destructive">Row {error.index + 1}</Badge> | |
| {error.field && ( | |
| <Badge variant="outline">{error.field}</Badge> | |
| )} | |
| </div> | |
| <div className="text-sm font-medium mb-1"> | |
| {error.asset.assetType} - {error.asset.brandModel} | |
| </div> | |
| <div className="text-sm text-muted-foreground mb-1"> | |
| Serial: {error.asset.serialNumber} | |
| </div> | |
| <div className="text-sm text-red-600"> | |
| {error.error} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| {/* Action Buttons */} | |
| <div className="flex justify-end gap-2"> | |
| {hasErrors && ( | |
| <Button | |
| variant="outline" | |
| onClick={downloadErrorReport} | |
| className="flex items-center gap-2" | |
| > | |
| <FileText className="h-4 w-4" /> | |
| Export Error Details | |
| </Button> | |
| )} | |
| {onClose && ( | |
| <Button onClick={onClose}> | |
| {hasSuccessfulImports ? 'Close' : 'Try Again'} | |
| </Button> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default ImportResults; |