72 lines
2.9 KiB
TypeScript
72 lines
2.9 KiB
TypeScript
import React from 'react';
|
|
import { LiveCount } from '../../types';
|
|
import { formatNumber } from '../../utils/formatters.ts';
|
|
|
|
const statusConfig: {
|
|
[key in LiveCount['status']]: { text: string; bg: string; pulse: boolean; icon: string };
|
|
} = {
|
|
online: { text: 'Menghitung', bg: 'bg-green-500', pulse: true, icon: 'fa-video' },
|
|
offline: { text: 'Offline', bg: 'bg-gray-500', pulse: false, icon: 'fa-video-slash' },
|
|
processing: { text: 'Memproses', bg: 'bg-blue-500', pulse: true, icon: 'fa-cogs' },
|
|
};
|
|
|
|
const CameraCountCard: React.FC<{ liveCount: LiveCount }> = ({ liveCount }) => {
|
|
const status = statusConfig[liveCount.status];
|
|
|
|
return (
|
|
<div className="bg-white rounded-xl shadow-sm transition-all duration-300 hover:shadow-lg flex flex-col border border-gray-200">
|
|
<div className="relative aspect-video bg-black rounded-t-xl flex items-center justify-center overflow-hidden group">
|
|
<img
|
|
src={liveCount.imageUrl}
|
|
alt={liveCount.cameraName}
|
|
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
/>
|
|
<div className="absolute inset-0 bg-black/30"></div>
|
|
|
|
<div className="absolute top-2 left-2 flex items-center space-x-2">
|
|
<span
|
|
className={`flex items-center text-xs font-semibold text-white px-2 py-0.5 rounded-full ${status.bg}`}
|
|
>
|
|
{status.pulse && (
|
|
<span className="relative flex h-2 w-2 mr-1.5">
|
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-white/75 opacity-75"></span>
|
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-white"></span>
|
|
</span>
|
|
)}
|
|
{status.text}
|
|
</span>
|
|
</div>
|
|
|
|
{liveCount.status === 'online' && (
|
|
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
|
<p
|
|
className="text-white text-4xl font-bold drop-shadow-lg"
|
|
style={{ textShadow: '0 2px 4px rgba(0,0,0,0.5)' }}
|
|
>
|
|
{formatNumber(liveCount.count)}
|
|
</p>
|
|
<p className="text-white/80 text-sm font-semibold drop-shadow-md">ekor terdeteksi</p>
|
|
</div>
|
|
)}
|
|
|
|
{liveCount.status !== 'online' && (
|
|
<div className="absolute inset-0 flex flex-col items-center justify-center bg-black/50">
|
|
<i className={`fa-solid ${status.icon} text-4xl text-white/70`}></i>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="p-3">
|
|
<h3 className="font-bold text-gray-800 truncate" title={liveCount.cameraName}>
|
|
{liveCount.cameraName}
|
|
</h3>
|
|
<p className="text-xs text-gray-500 truncate" title={liveCount.locationName}>
|
|
<i className="fa-solid fa-map-marker-alt mr-1.5"></i>
|
|
{liveCount.locationName}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CameraCountCard;
|