Hwandji's picture
feat: initial HuggingFace Space deployment
4343907
raw
history blame
8.33 kB
<template>
<div
class="saap-agent-card"
:class="[
`status-${agent.status}`,
{ 'loading': loading, 'interactive': !loading }
]"
>
<!-- Agent Header -->
<div class="agent-header">
<div class="agent-avatar" :style="{ backgroundColor: agent.color || '#6B7280' }">
<span class="avatar-text">{{ agent.name.charAt(0).toUpperCase() }}</span>
</div>
<div class="agent-info">
<h3 class="agent-name">{{ agent.name }}</h3>
<p class="agent-type">{{ agent.type }}</p>
</div>
<div class="status-indicator">
<div class="status-dot" :class="`status-${agent.status}`"></div>
<span class="status-text">{{ formatStatus(agent.status) }}</span>
</div>
</div>
<!-- Agent Description -->
<div class="agent-description">
<p class="description-text">{{ agent.description || 'No description available' }}</p>
</div>
<!-- Agent Metrics -->
<div v-if="agent.metrics" class="agent-metrics">
<div class="metric">
<span class="metric-label">Messages</span>
<span class="metric-value">{{ agent.metrics.messages_processed || 0 }}</span>
</div>
<div class="metric">
<span class="metric-label">Avg. Response</span>
<span class="metric-value">
{{ agent.metrics.average_response_time ? `${agent.metrics.average_response_time.toFixed(1)}s` : 'N/A' }}
</span>
</div>
</div>
<!-- Agent Actions -->
<div class="agent-actions">
<button
v-if="agent.status === 'inactive'"
@click="handleStartAgent"
:disabled="loading"
class="action-button start-button"
>
<PlayIcon class="action-icon" />
<span>Start</span>
</button>
<button
v-if="agent.status === 'active'"
@click="handleStopAgent"
:disabled="loading"
class="action-button stop-button"
>
<StopIcon class="action-icon" />
<span>Stop</span>
</button>
<button
v-if="agent.status === 'starting'"
disabled
class="action-button starting-button"
>
<div class="saap-loading-spinner-sm"></div>
<span>Starting...</span>
</button>
<button
@click="handleChatAgent"
:disabled="loading || agent.status !== 'active'"
class="action-button chat-button"
>
<ChatBubbleLeftIcon class="action-icon" />
<span>Chat</span>
</button>
<button
@click="handleAgentClick"
:disabled="loading"
class="action-button details-button"
>
<InformationCircleIcon class="action-icon" />
<span>Details</span>
</button>
</div>
<!-- Loading Overlay -->
<div v-if="loading" class="loading-overlay">
<div class="saap-loading-spinner"></div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import {
PlayIcon,
StopIcon,
ChatBubbleLeftIcon,
InformationCircleIcon
} from '@heroicons/vue/24/outline'
const props = defineProps({
agent: {
type: Object,
required: true
},
loading: {
type: Boolean,
default: false
}
})
const emit = defineEmits([
'agent-click',
'start-agent',
'stop-agent',
'chat-agent'
])
const formatStatus = (status) => {
const statusMap = {
'active': 'Active',
'inactive': 'Inactive',
'starting': 'Starting',
'error': 'Error'
}
return statusMap[status] || status
}
const handleAgentClick = () => {
emit('agent-click', props.agent)
}
const handleStartAgent = () => {
emit('start-agent', props.agent.id)
}
const handleStopAgent = () => {
emit('stop-agent', props.agent.id)
}
const handleChatAgent = () => {
emit('chat-agent', props.agent.id)
}
</script>
<style scoped>
.saap-agent-card {
@apply bg-white rounded-xl border border-saap-gray-200;
@apply p-4 relative overflow-hidden;
@apply transition-all duration-200;
@apply max-w-sm mx-auto;
@apply flex flex-col;
height: 380px;
}
.saap-agent-card.interactive:hover {
@apply shadow-md border-saap-primary-300;
transform: translateY(-2px);
}
.saap-agent-card.loading {
@apply opacity-75;
}
/* Agent Header */
.agent-header {
@apply flex items-start space-x-3 mb-4;
flex-shrink: 0;
}
.agent-avatar {
@apply w-12 h-12 rounded-lg flex items-center justify-center;
@apply flex-shrink-0;
}
.avatar-text {
@apply text-white font-semibold text-lg;
}
.agent-info {
@apply flex-1 min-w-0;
}
.agent-name {
@apply text-lg font-semibold text-saap-gray-900 truncate;
}
.agent-type {
@apply text-sm text-saap-gray-600 capitalize;
}
.status-indicator {
@apply flex items-center space-x-1 flex-shrink-0;
}
.status-dot {
@apply w-2 h-2 rounded-full;
}
.status-dot.status-active {
@apply bg-saap-secondary-500;
}
.status-dot.status-inactive {
@apply bg-saap-gray-400;
}
.status-dot.status-starting {
@apply bg-saap-accent-500;
}
.status-dot.status-error {
@apply bg-red-500;
}
.status-text {
@apply text-xs font-medium text-saap-gray-600;
}
/* Agent Description */
.agent-description {
@apply mb-4;
flex-shrink: 0;
}
.description-text {
@apply text-sm text-saap-gray-600 leading-relaxed;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
min-height: 2.5rem;
}
/* Agent Metrics */
.agent-metrics {
@apply flex items-center justify-between;
@apply p-3 bg-saap-gray-50 rounded-lg mb-4;
flex: 1 1 auto;
align-content: center;
}
.metric {
@apply flex flex-col items-center text-center;
}
.metric-label {
@apply text-xs text-saap-gray-500 font-medium;
}
.metric-value {
@apply text-sm font-semibold text-saap-gray-900;
}
/* Agent Actions */
.agent-actions {
@apply flex flex-wrap gap-2;
flex-shrink: 0;
margin-top: auto;
}
.action-button {
@apply inline-flex items-center space-x-1.5 px-3 py-2;
@apply text-xs font-medium rounded-lg;
@apply transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-offset-2;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
}
.action-icon {
@apply w-4 h-4 flex-shrink-0;
}
.start-button {
@apply bg-saap-secondary-50 text-saap-secondary-600 border border-saap-secondary-200;
@apply hover:bg-saap-secondary-100 hover:border-saap-secondary-300 focus:ring-saap-secondary-500;
}
.start-button .action-icon {
@apply text-saap-secondary-600;
}
.stop-button {
@apply bg-red-50 text-red-600 border border-red-200;
@apply hover:bg-red-100 hover:border-red-300 focus:ring-red-500;
}
.stop-button .action-icon {
@apply text-red-600;
}
.starting-button {
@apply bg-saap-accent-50 text-saap-accent-600 border border-saap-accent-200;
}
.chat-button {
@apply bg-saap-primary-50 text-saap-primary-600 border border-saap-primary-200;
@apply hover:bg-saap-primary-100 hover:border-saap-primary-300 focus:ring-saap-primary-500;
}
.chat-button .action-icon {
@apply text-saap-primary-600;
}
.details-button {
@apply bg-saap-gray-50 text-saap-gray-600 border border-saap-gray-200;
@apply hover:bg-saap-gray-100 hover:border-saap-gray-300 focus:ring-saap-gray-500;
}
.details-button .action-icon {
@apply text-saap-gray-600;
}
/* Loading Overlay */
.loading-overlay {
@apply absolute inset-0 bg-white bg-opacity-75;
@apply flex items-center justify-center z-10;
}
/* Status-based card styling */
.saap-agent-card.status-active {
@apply border-saap-secondary-200;
}
.saap-agent-card.status-error {
@apply border-red-200;
}
/* Responsive Design */
@media (max-width: 640px) {
.saap-agent-card {
@apply max-w-none;
}
.agent-header {
@apply flex-col space-x-0 space-y-3 text-center;
}
.agent-avatar {
@apply mx-auto;
}
.status-indicator {
@apply justify-center;
}
.agent-metrics {
@apply flex-col space-y-2;
}
.metric {
@apply flex-row items-center justify-between w-full;
}
.agent-actions {
@apply grid grid-cols-2 gap-2;
}
.action-button {
@apply justify-center w-full;
}
}
@media (max-width: 480px) {
.agent-actions {
@apply grid-cols-1;
}
}
/* Line clamp utility */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>