| typescript | |
| import React from 'react' | |
| import { CheckCircle, AlertTriangle, Info } from 'lucide-react' | |
| interface ActivityItem { | |
| id: string | |
| type: 'success' | 'warning' | 'info' | |
| title: string | |
| description: string | |
| timestamp: string | |
| asset?: string | |
| } | |
| const ActivityTimeline: React.FC = () => { | |
| const activities: ActivityItem[] = [ | |
| { | |
| id: '1', | |
| type: 'success', | |
| title: 'Database server restored', | |
| description: 'Backup restoration completed successfully', | |
| timestamp: '2 hours ago', | |
| asset: 'db-prod-01' | |
| }, | |
| { | |
| id: '2', | |
| type: 'warning', | |
| title: 'High memory usage detected', | |
| description: 'Web server memory usage above 90% threshold', | |
| timestamp: '4 hours ago', | |
| asset: 'web-staging-02' | |
| }, | |
| { | |
| id: '3', | |
| type: 'info', | |
| title: 'New asset added', | |
| description: 'Added new load balancer to inventory', | |
| timestamp: '6 hours ago' | |
| }, | |
| { | |
| id: '4', | |
| type: 'success', | |
| title: 'Network switch configured', | |
| description: 'VLAN configuration updated successfully', | |
| timestamp: '1 day ago', | |
| asset: 'switch-dc-03' | |
| } | |
| ] | |
| const getActivityIcon = (type: string) => { | |
| switch (type) { | |
| case 'success': | |
| return <CheckCircle className="h-4 w-4 text-accent-500" /> | |
| case 'warning': | |
| return <AlertTriangle className="h-4 w-4 text-alert-500" /> | |
| case 'info': | |
| return <Info className="h-4 w-4 text-primary-500" /> | |
| default: | |
| return <Info className="h-4 w-4 text-primary-500" /> | |
| } | |
| } | |
| const getActivityColor = (type: string) => { | |
| switch (type) { | |
| case 'success': | |
| return 'border-accent-200' | |
| case 'warning': | |
| return 'border-alert-200' | |
| case 'info': | |
| return 'border-primary-200' | |
| default: | |
| return 'border-gray-200' | |
| } | |
| } | |
| return ( | |
| <div className="flow-root"> | |
| <ul className="-mb-8"> | |
| {activities.map((activity, activityIdx) => ( | |
| <li key={activity.id}> | |
| <div className="relative pb-8"> | |
| {activityIdx !== activities.length - 1 && ( | |
| <span | |
| className="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200" | |
| aria-hidden="true" | |
| /> | |
| )} | |
| <div className="relative flex space-x-3"> | |
| <div> | |
| <span className={` | |
| h-8 w-8 rounded-full flex items-center justify-center ring-8 ring-white | |
| ${getActivityColor(activity.type)} | |
| `}> | |
| {getActivityIcon(activity.type)} | |
| </span> | |
| </div> | |
| <div className="min-w-0 flex-1 pt-1.5 flex justify-between space-x-4"> | |
| <div> | |
| <p className="text-sm text-gray-900">{activity.title}</p> | |
| <p className="text-sm text-gray-500">{activity.description}</p> | |
| {activity.asset && ( | |
| <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800"> | |
| {activity.asset} | |
| </span> | |
| )} | |
| </div> | |
| <div className="text-right text-sm whitespace-nowrap text-gray-500"> | |
| {activity.timestamp} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| ) | |
| } | |
| export default ActivityTimeline | |
| </html> |