devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
9.78 kB
import React from 'react';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Monitor,
Laptop,
Smartphone,
Printer,
HardDrive,
Eye,
Edit,
Trash2,
User,
Calendar,
Mouse,
MousePointer2,
Keyboard,
Tablet,
Scan,
Computer,
UserPlus
} from 'lucide-react';
import { Asset, AssetWithDetails, AssetCondition } from '@/types/asset-management';
import { format } from 'date-fns';
// Helper function to get badge variant based on asset condition
const getConditionBadgeVariant = (condition: AssetCondition) => {
switch (condition) {
case 'Excellent':
return 'default';
case 'Good':
return 'secondary';
case 'Fair':
return 'outline';
case 'Poor':
return 'destructive';
default:
return 'outline';
}
};
// Helper function to get asset type icon
const getAssetTypeIcon = (assetType: string) => {
const type = assetType.toLowerCase();
// Exact matches first
if (type === 'laptop') return <Laptop className="h-4 w-4" />;
if (type === 'desktop') return <Computer className="h-4 w-4" />;
if (type === 'monitor') return <Monitor className="h-4 w-4" />;
if (type === 'extended screen') return <Monitor className="h-4 w-4" />;
if (type === 'keyboard') return <Keyboard className="h-4 w-4" />;
if (type === 'mouse') return <Mouse className="h-4 w-4" />;
if (type === 'wired mouse') return <Mouse className="h-4 w-4" />;
if (type === 'wireless mouse') return <MousePointer2 className="h-4 w-4" />;
if (type === 'laptop stand') return <Laptop className="h-4 w-4" />;
if (type === 'printer') return <Printer className="h-4 w-4" />;
if (type === 'scanner') return <Scan className="h-4 w-4" />;
if (type === 'tablet') return <Tablet className="h-4 w-4" />;
if (type === 'phone') return <Smartphone className="h-4 w-4" />;
// Fallback for partial matches
if (type.includes('laptop')) return <Laptop className="h-4 w-4" />;
if (type.includes('monitor') || type.includes('screen')) return <Monitor className="h-4 w-4" />;
if (type.includes('mouse')) return <Mouse className="h-4 w-4" />;
if (type.includes('keyboard')) return <Keyboard className="h-4 w-4" />;
if (type.includes('phone')) return <Smartphone className="h-4 w-4" />;
if (type.includes('tablet')) return <Tablet className="h-4 w-4" />;
if (type.includes('printer')) return <Printer className="h-4 w-4" />;
if (type.includes('scanner')) return <Scan className="h-4 w-4" />;
return <HardDrive className="h-4 w-4" />;
};
interface AssetCardProps {
asset: Asset | AssetWithDetails;
onView?: (asset: Asset) => void;
onEdit?: (asset: Asset) => void;
onDelete?: (asset: Asset) => void;
onAssign?: (asset: Asset) => void;
activeAssignment?: any;
showActions?: boolean;
compact?: boolean;
}
const AssetCard: React.FC<AssetCardProps> = ({
asset,
onView,
onEdit,
onDelete,
onAssign,
activeAssignment,
showActions = true,
compact = false
}) => {
const assetWithDetails = asset as AssetWithDetails;
const currentAssignment = activeAssignment ||
assetWithDetails.currentAssignment ||
(assetWithDetails.assignments && assetWithDetails.assignments.find(a => !a.returnedDate));
const isAvailable = !currentAssignment;
return (
<Card className="w-full hover:shadow-md transition-shadow p-0">
<CardHeader className={compact ? 'pb-3 pt-6 px-6' : 'pb-4 pt-8 px-8'}>
{/* Assignment Status Badge - Mobile First */}
<div className="mb-3">
{currentAssignment ? (
<Badge variant="secondary" className="flex items-center gap-1.5 w-fit text-xs md:text-xs py-1.5 px-2.5">
<User className="h-3.5 w-3.5 md:h-3 md:w-3" />
<span>Assigned to {currentAssignment.employeeName}</span>
</Badge>
) : (
<Badge variant="outline" className="flex items-center gap-1.5 w-fit text-green-600 border-green-600 text-xs md:text-xs py-1.5 px-2.5">
<span>Available</span>
</Badge>
)}
</div>
<div className="flex justify-between items-start gap-4">
<CardTitle className={`font-semibold flex items-center gap-2 ${compact ? 'text-base' : 'text-lg'}`}>
{getAssetTypeIcon(asset.assetType)}
<span>{asset.assetType}</span>
</CardTitle>
<Badge variant={getConditionBadgeVariant(asset.assetCondition as AssetCondition)} className="text-xs py-1 px-2">
{asset.assetCondition}
</Badge>
</div>
</CardHeader>
<CardContent className={compact ? 'pb-4 pt-0 px-6' : 'pb-6 pt-0 px-8'}>
<div className={compact ? 'space-y-3 md:space-y-4' : 'space-y-5'}>
<div className={compact ? 'text-sm md:text-sm' : 'text-base'}>
<span className="font-medium">Asset Code:</span>
<span className="ml-2 text-muted-foreground font-mono break-all">{asset.assetCode}</span>
</div>
<div className={compact ? 'text-sm md:text-sm' : 'text-base'}>
<span className="font-medium">Brand/Model:</span>
<span className="ml-2 text-muted-foreground break-words">{asset.brandModel}</span>
</div>
<div className={compact ? 'text-sm md:text-sm' : 'text-base'}>
<span className="font-medium">Serial Number:</span>
<span className="ml-2 text-muted-foreground font-mono break-all">{asset.serialNumber}</span>
</div>
{/* Assignment Status Details */}
{currentAssignment ? (
<div className={`flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-0 text-sm text-green-600 bg-green-50 dark:bg-green-950 rounded-md ${compact ? 'p-3' : 'p-4'}`}>
<div className="flex items-center">
<User className="h-4 w-4 mr-2 shrink-0" />
<span className="font-medium">Assigned</span>
</div>
{currentAssignment.assignedDate && (
<span className="text-xs text-muted-foreground sm:ml-2">
since {format(new Date(currentAssignment.assignedDate), 'PP')}
</span>
)}
</div>
) : (
<div className={`flex items-center text-sm text-muted-foreground bg-muted rounded-md ${compact ? 'p-3' : 'p-4'}`}>
<Calendar className="h-4 w-4 mr-2 shrink-0" />
<span>Available</span>
</div>
)}
{/* System Details Preview */}
{assetWithDetails.systemDetails && !compact && (
<div className="text-xs text-muted-foreground bg-muted/50 p-4 rounded-md">
<div className="font-medium mb-3">System Details:</div>
<div className="space-y-2">
<div className="break-words">{assetWithDetails.systemDetails.systemModel}</div>
<div className="break-words">{assetWithDetails.systemDetails.processor}</div>
<div>{assetWithDetails.systemDetails.installedRAM} RAM</div>
</div>
</div>
)}
</div>
</CardContent>
{showActions && (
<CardFooter className={compact ? 'pt-4 pb-6 px-6' : 'pt-5 pb-8 px-8'}>
<div className="flex flex-wrap justify-end w-full gap-2">
{onAssign && isAvailable && (
<Button
variant="default"
size="sm"
onClick={() => onAssign(asset)}
className="flex items-center gap-1 min-h-[44px] md:min-h-0 touch-manipulation"
aria-label={`Assign asset ${asset.assetCode} to employee`}
>
<UserPlus className="h-4 w-4 md:h-3 md:w-3" />
<span className="text-sm md:text-xs">Assign</span>
</Button>
)}
{onView && (
<Button
variant="outline"
size="sm"
onClick={() => onView(asset)}
className="flex items-center gap-1 min-h-[44px] md:min-h-0 touch-manipulation"
aria-label={`View asset ${asset.assetCode}`}
>
<Eye className="h-4 w-4 md:h-3 md:w-3" />
<span className="text-sm md:text-xs">View</span>
</Button>
)}
{onEdit && (
<Button
variant="outline"
size="sm"
onClick={() => onEdit(asset)}
className="flex items-center gap-1 min-h-[44px] md:min-h-0 touch-manipulation"
aria-label={`Edit asset ${asset.assetCode}`}
>
<Edit className="h-4 w-4 md:h-3 md:w-3" />
<span className="text-sm md:text-xs">Edit</span>
</Button>
)}
{onDelete && (
<Button
variant="destructive"
size="sm"
onClick={() => onDelete(asset)}
className="flex items-center gap-1 min-h-[44px] md:min-h-0 touch-manipulation"
aria-label={`Delete asset ${asset.assetCode}`}
>
<Trash2 className="h-4 w-4 md:h-3 md:w-3" />
<span className="text-sm md:text-xs">Delete</span>
</Button>
)}
</div>
</CardFooter>
)}
</Card>
);
};
export default AssetCard;