Spaces:
Sleeping
Sleeping
| <template> | |
| <div class="agent-card" :class="[ | |
| `agent-card--${agent.status}`, | |
| { 'agent-card--featured': featured } | |
| ]" :style="cardStyles"> | |
| <!-- Agent Header --> | |
| <div class="agent-card__header"> | |
| <div class="agent-card__avatar"> | |
| <img | |
| :src="agent.appearance.avatar" | |
| :alt="agent.appearance.displayName" | |
| class="agent-card__avatar-image" | |
| /> | |
| <div class="agent-card__status-indicator" :class="`status--${agent.status}`"> | |
| <div class="status__dot"></div> | |
| </div> | |
| </div> | |
| <div class="agent-card__info"> | |
| <h3 class="agent-card__name">{{ agent.appearance.displayName }}</h3> | |
| <p class="agent-card__subtitle">{{ agent.appearance.subtitle }}</p> | |
| <div class="agent-card__meta"> | |
| <span class="agent-card__type">{{ formatAgentType(agent.type) }}</span> | |
| <span class="agent-card__id">{{ agent.id }}</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Agent Capabilities --> | |
| <div class="agent-card__capabilities"> | |
| <div class="capabilities__label">Capabilities</div> | |
| <div class="capabilities__tags"> | |
| <span | |
| v-for="capability in agent.capabilities.slice(0, 3)" | |
| :key="capability" | |
| class="capability-tag" | |
| > | |
| {{ formatCapability(capability) }} | |
| </span> | |
| <span v-if="agent.capabilities.length > 3" class="capability-tag capability-tag--more"> | |
| +{{ agent.capabilities.length - 3 }} | |
| </span> | |
| </div> | |
| </div> | |
| <!-- Agent Stats --> | |
| <div class="agent-card__stats"> | |
| <div class="stat-item"> | |
| <div class="stat-item__value">{{ stats.messagesProcessed || 0 }}</div> | |
| <div class="stat-item__label">Messages</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-item__value">{{ formatUptime(stats.uptime) }}</div> | |
| <div class="stat-item__label">Uptime</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-item__value">{{ stats.responseTime || 0 }}ms</div> | |
| <div class="stat-item__label">Response</div> | |
| </div> | |
| </div> | |
| <!-- Agent Actions --> | |
| <div class="agent-card__actions"> | |
| <button | |
| class="btn btn--secondary btn--sm" | |
| @click="$emit('view-details', agent)" | |
| > | |
| <Icon name="eye" size="16" /> | |
| Details | |
| </button> | |
| <button | |
| class="btn btn--primary btn--sm" | |
| @click="$emit('send-message', agent)" | |
| :disabled="agent.status !== 'active'" | |
| > | |
| <Icon name="message-circle" size="16" /> | |
| Message | |
| </button> | |
| <div class="agent-card__menu"> | |
| <button class="btn btn--ghost btn--sm" @click="toggleMenu"> | |
| <Icon name="more-horizontal" size="16" /> | |
| </button> | |
| <div v-if="menuOpen" class="dropdown-menu" @click.stop> | |
| <button @click="$emit('configure', agent)" class="dropdown-item"> | |
| <Icon name="settings" size="16" /> | |
| Configure | |
| </button> | |
| <button @click="$emit('restart', agent)" class="dropdown-item"> | |
| <Icon name="refresh-cw" size="16" /> | |
| Restart | |
| </button> | |
| <hr class="dropdown-divider" /> | |
| <button @click="$emit('stop', agent)" class="dropdown-item dropdown-item--danger"> | |
| <Icon name="stop-circle" size="16" /> | |
| Stop Agent | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Real-time Activity Indicator --> | |
| <div v-if="agent.status === 'active'" class="agent-card__activity"> | |
| <div class="activity-pulse"></div> | |
| <span class="activity-text">Processing...</span> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| import { ref, computed } from 'vue' | |
| import Icon from './Icon.vue' | |
| export default { | |
| name: 'AgentCard', | |
| components: { Icon }, | |
| props: { | |
| agent: { | |
| type: Object, | |
| required: true, | |
| validator: (agent) => { | |
| return agent.id && agent.name && agent.type && agent.status | |
| } | |
| }, | |
| stats: { | |
| type: Object, | |
| default: () => ({ | |
| messagesProcessed: 0, | |
| uptime: 0, | |
| responseTime: 0 | |
| }) | |
| }, | |
| featured: { | |
| type: Boolean, | |
| default: false | |
| } | |
| }, | |
| emits: [ | |
| 'view-details', | |
| 'send-message', | |
| 'configure', | |
| 'restart', | |
| 'stop' | |
| ], | |
| setup(props) { | |
| const menuOpen = ref(false) | |
| const cardStyles = computed(() => ({ | |
| '--agent-color': props.agent.appearance?.color || '#6B7280', | |
| '--agent-color-light': props.agent.appearance?.color + '20' || '#6B728020', | |
| })) | |
| const formatAgentType = (type) => { | |
| return type.charAt(0).toUpperCase() + type.slice(1).replace('_', ' ') | |
| } | |
| const formatCapability = (capability) => { | |
| return capability.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) | |
| } | |
| const formatUptime = (seconds) => { | |
| if (!seconds) return '0m' | |
| const hours = Math.floor(seconds / 3600) | |
| const minutes = Math.floor((seconds % 3600) / 60) | |
| if (hours > 0) return `${hours}h ${minutes}m` | |
| return `${minutes}m` | |
| } | |
| const toggleMenu = () => { | |
| menuOpen.value = !menuOpen.value | |
| } | |
| return { | |
| menuOpen, | |
| cardStyles, | |
| formatAgentType, | |
| formatCapability, | |
| formatUptime, | |
| toggleMenu | |
| } | |
| } | |
| } | |
| </script> | |
| <style scoped> | |
| /* Agent Card Base Styles */ | |
| .agent-card { | |
| --agent-color: #6B7280; | |
| --agent-color-light: #6B728020; | |
| background: #ffffff; | |
| border: 1px solid #E5E7EB; | |
| border-radius: 12px; | |
| padding: 20px; | |
| transition: all 0.2s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .agent-card:hover { | |
| border-color: var(--agent-color); | |
| box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); | |
| transform: translateY(-2px); | |
| } | |
| .agent-card--featured { | |
| border-color: var(--agent-color); | |
| background: linear-gradient(135deg, #ffffff 0%, var(--agent-color-light) 100%); | |
| } | |
| .agent-card--active { | |
| border-left: 4px solid var(--agent-color); | |
| } | |
| .agent-card--inactive { | |
| opacity: 0.6; | |
| border-left: 4px solid #9CA3AF; | |
| } | |
| .agent-card--error { | |
| border-left: 4px solid #EF4444; | |
| } | |
| /* Header Section */ | |
| .agent-card__header { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 12px; | |
| margin-bottom: 16px; | |
| } | |
| .agent-card__avatar { | |
| position: relative; | |
| flex-shrink: 0; | |
| } | |
| .agent-card__avatar-image { | |
| width: 48px; | |
| height: 48px; | |
| border-radius: 50%; | |
| border: 3px solid var(--agent-color); | |
| object-fit: cover; | |
| } | |
| .agent-card__status-indicator { | |
| position: absolute; | |
| bottom: 0; | |
| right: 0; | |
| width: 16px; | |
| height: 16px; | |
| border-radius: 50%; | |
| border: 2px solid white; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .status__dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| animation: pulse 2s infinite; | |
| } | |
| .status--active .status__dot { | |
| background: #10B981; | |
| } | |
| .status--inactive .status__dot { | |
| background: #6B7280; | |
| } | |
| .status--error .status__dot { | |
| background: #EF4444; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .agent-card__info { | |
| flex: 1; | |
| min-width: 0; | |
| } | |
| .agent-card__name { | |
| font-size: 16px; | |
| font-weight: 600; | |
| color: #111827; | |
| margin: 0 0 4px 0; | |
| line-height: 1.3; | |
| } | |
| .agent-card__subtitle { | |
| font-size: 13px; | |
| color: #6B7280; | |
| margin: 0 0 8px 0; | |
| line-height: 1.4; | |
| } | |
| .agent-card__meta { | |
| display: flex; | |
| gap: 8px; | |
| align-items: center; | |
| } | |
| .agent-card__type { | |
| font-size: 11px; | |
| font-weight: 500; | |
| color: var(--agent-color); | |
| background: var(--agent-color-light); | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| } | |
| .agent-card__id { | |
| font-size: 10px; | |
| color: #9CA3AF; | |
| font-family: 'Fira Code', monospace; | |
| } | |
| /* Capabilities Section */ | |
| .agent-card__capabilities { | |
| margin-bottom: 16px; | |
| } | |
| .capabilities__label { | |
| font-size: 11px; | |
| font-weight: 500; | |
| color: #6B7280; | |
| margin-bottom: 6px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .capabilities__tags { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 4px; | |
| } | |
| .capability-tag { | |
| font-size: 10px; | |
| color: #4B5563; | |
| background: #F3F4F6; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| border: 1px solid #E5E7EB; | |
| } | |
| .capability-tag--more { | |
| color: #6B7280; | |
| font-weight: 500; | |
| } | |
| /* Stats Section */ | |
| .agent-card__stats { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr 1fr; | |
| gap: 12px; | |
| margin-bottom: 16px; | |
| padding: 12px; | |
| background: #F9FAFB; | |
| border-radius: 8px; | |
| } | |
| .stat-item { | |
| text-align: center; | |
| } | |
| .stat-item__value { | |
| font-size: 14px; | |
| font-weight: 600; | |
| color: #111827; | |
| line-height: 1.2; | |
| } | |
| .stat-item__label { | |
| font-size: 10px; | |
| color: #6B7280; | |
| margin-top: 2px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| /* Actions Section */ | |
| .agent-card__actions { | |
| display: flex; | |
| gap: 8px; | |
| align-items: center; | |
| position: relative; | |
| } | |
| .agent-card__menu { | |
| position: relative; | |
| margin-left: auto; | |
| } | |
| .dropdown-menu { | |
| position: absolute; | |
| top: calc(100% + 4px); | |
| right: 0; | |
| background: white; | |
| border: 1px solid #E5E7EB; | |
| border-radius: 8px; | |
| padding: 4px 0; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | |
| z-index: 10; | |
| min-width: 140px; | |
| } | |
| .dropdown-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| width: 100%; | |
| padding: 8px 12px; | |
| font-size: 13px; | |
| color: #374151; | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| text-align: left; | |
| } | |
| .dropdown-item:hover { | |
| background: #F3F4F6; | |
| } | |
| .dropdown-item--danger { | |
| color: #EF4444; | |
| } | |
| .dropdown-item--danger:hover { | |
| background: #FEF2F2; | |
| } | |
| .dropdown-divider { | |
| margin: 4px 0; | |
| border: none; | |
| border-top: 1px solid #E5E7EB; | |
| } | |
| /* Activity Indicator */ | |
| .agent-card__activity { | |
| position: absolute; | |
| bottom: 8px; | |
| right: 12px; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .activity-pulse { | |
| width: 8px; | |
| height: 8px; | |
| background: var(--agent-color); | |
| border-radius: 50%; | |
| animation: pulse 1.5s ease-in-out infinite; | |
| } | |
| .activity-text { | |
| font-size: 10px; | |
| color: var(--agent-color); | |
| font-weight: 500; | |
| } | |
| /* Button Styles */ | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| font-size: 13px; | |
| font-weight: 500; | |
| padding: 8px 12px; | |
| border-radius: 6px; | |
| border: 1px solid transparent; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| background: none; | |
| } | |
| .btn--sm { | |
| font-size: 12px; | |
| padding: 6px 10px; | |
| } | |
| .btn--primary { | |
| background: var(--agent-color); | |
| color: white; | |
| border-color: var(--agent-color); | |
| } | |
| .btn--primary:hover:not(:disabled) { | |
| opacity: 0.9; | |
| transform: translateY(-1px); | |
| } | |
| .btn--primary:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .btn--secondary { | |
| background: #F9FAFB; | |
| color: #374151; | |
| border-color: #E5E7EB; | |
| } | |
| .btn--secondary:hover { | |
| background: #F3F4F6; | |
| border-color: #D1D5DB; | |
| } | |
| .btn--ghost { | |
| color: #6B7280; | |
| } | |
| .btn--ghost:hover { | |
| color: #374151; | |
| background: #F3F4F6; | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 640px) { | |
| .agent-card { | |
| padding: 16px; | |
| } | |
| .agent-card__stats { | |
| grid-template-columns: 1fr 1fr; | |
| gap: 8px; | |
| } | |
| .agent-card__actions { | |
| flex-wrap: wrap; | |
| } | |
| } | |
| </style> |