115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
|
|
import React from 'react';
|
|||
|
|
import styled, { css } from 'styled-components';
|
|||
|
|
import { DeviceUser } from "../../../store/useDeviceStore";
|
|||
|
|
|
|||
|
|
interface DeviceCardProps {
|
|||
|
|
device: DeviceUser;
|
|||
|
|
status: 'online' | 'offline';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const CardWrapper = styled.div<{ status: 'online' | 'offline' }>`
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|||
|
|
overflow: hidden;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
cursor: pointer;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
|
|||
|
|
&:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
|||
|
|
}
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const CardHeader = styled.div<{ status: 'online' | 'offline' }>`
|
|||
|
|
background: ${({ status, theme }) =>
|
|||
|
|
status === 'online' ? theme.successColor : theme.errorColor};
|
|||
|
|
color: white;
|
|||
|
|
padding: 16px;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
|
|||
|
|
const CardContent = styled.div<{ isExpanded: boolean }>`
|
|||
|
|
max-height: 0;
|
|||
|
|
opacity: 0;
|
|||
|
|
padding: 0 16px;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 14px;
|
|||
|
|
display: grid;
|
|||
|
|
gap: 12px;
|
|||
|
|
|
|||
|
|
${({ isExpanded }) => isExpanded && css`
|
|||
|
|
max-height: 500px;
|
|||
|
|
opacity: 1;
|
|||
|
|
padding: 16px;
|
|||
|
|
`}
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const InfoRow = styled.div`
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const Label = styled.span`
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #666;
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const Value = styled.span`
|
|||
|
|
color: #333;
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
|
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
|||
|
|
const toggleExpanded = () => setIsExpanded(!isExpanded);
|
|||
|
|
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<CardWrapper status={status} onClick={toggleExpanded}>
|
|||
|
|
<CardHeader status={status}>
|
|||
|
|
<p>
|
|||
|
|
MAC: {device.mac}
|
|||
|
|
</p>
|
|||
|
|
</CardHeader>
|
|||
|
|
|
|||
|
|
<CardContent isExpanded={isExpanded}>
|
|||
|
|
<InfoRow>
|
|||
|
|
<Label>Имя:</Label>
|
|||
|
|
<Value>{device.hostname || 'Неизвестно'}</Value>
|
|||
|
|
</InfoRow>
|
|||
|
|
|
|||
|
|
<InfoRow>
|
|||
|
|
<Label>Производитель:</Label>
|
|||
|
|
<Value>{device.vendor || 'Неизвестно'}</Value>
|
|||
|
|
</InfoRow>
|
|||
|
|
|
|||
|
|
<InfoRow>
|
|||
|
|
<Label>IP адрес:</Label>
|
|||
|
|
<Value>{device.ip}</Value>
|
|||
|
|
</InfoRow>
|
|||
|
|
|
|||
|
|
{device.open_ports?.length > 0 && (
|
|||
|
|
<InfoRow>
|
|||
|
|
<Label>Открытые порты:</Label>
|
|||
|
|
<Value>{device.open_ports.join(', ')}</Value>
|
|||
|
|
</InfoRow>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<InfoRow>
|
|||
|
|
<Label>Последняя активность:</Label>
|
|||
|
|
<Value>{device.last_seen}</Value>
|
|||
|
|
</InfoRow>
|
|||
|
|
</CardContent>
|
|||
|
|
</CardWrapper>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default DeviceCard;
|