124 lines
4.2 KiB
Svelte
124 lines
4.2 KiB
Svelte
<script lang="ts">
|
|
export let ticket: import('$lib/stores/tickets').Ticket;
|
|
|
|
// Status mapping
|
|
const statusConfig = {
|
|
NEW: { label: 'Nuevo', class: 'badge-new' },
|
|
IN_PROGRESS: { label: 'En Progreso', class: 'badge-in-progress' },
|
|
WAITING_FOR_CLIENT: { label: 'Esperando Cliente', class: 'badge-waiting' },
|
|
RESOLVED: { label: 'Resuelto', class: 'badge-resolved' },
|
|
CLOSED: { label: 'Cerrado', class: 'badge-closed' },
|
|
REOPENED: { label: 'Reabierto', class: 'badge-reopened' }
|
|
};
|
|
|
|
// Priority mapping
|
|
const priorityConfig = {
|
|
LOW: { label: 'Baja', class: 'badge-priority-low' },
|
|
MEDIUM: { label: 'Media', class: 'badge-priority-medium' },
|
|
HIGH: { label: 'Alta', class: 'badge-priority-high' },
|
|
URGENT: { label: 'Urgente', class: 'badge-priority-urgent' }
|
|
};
|
|
|
|
// Format date
|
|
function formatDate(dateString: string): string {
|
|
return new Date(dateString).toLocaleDateString('es-ES', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
}
|
|
|
|
// Format relative time
|
|
function formatRelativeTime(dateString: string): string {
|
|
const date = new Date(dateString);
|
|
const now = new Date();
|
|
const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / (1000 * 60));
|
|
|
|
if (diffInMinutes < 1) return 'hace un momento';
|
|
if (diffInMinutes < 60) return `hace ${diffInMinutes}m`;
|
|
|
|
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
if (diffInHours < 24) return `hace ${diffInHours}h`;
|
|
|
|
const diffInDays = Math.floor(diffInHours / 24);
|
|
if (diffInDays < 7) return `hace ${diffInDays}d`;
|
|
|
|
return formatDate(dateString);
|
|
}
|
|
</script>
|
|
|
|
<div class="card hover:shadow-md transition-shadow">
|
|
<div class="card-content">
|
|
<div class="flex justify-between items-start mb-3">
|
|
<h3 class="text-lg font-medium text-gray-900 line-clamp-2">
|
|
<a href="/tickets/{ticket.id}" class="hover:text-primary-600">
|
|
{ticket.title}
|
|
</a>
|
|
</h3>
|
|
<div class="flex items-center space-x-2 ml-4">
|
|
<span class={`${statusConfig[ticket.status].class}`}>
|
|
{statusConfig[ticket.status].label}
|
|
</span>
|
|
<span class={`${priorityConfig[ticket.priority].class}`}>
|
|
{priorityConfig[ticket.priority].label}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-gray-600 text-sm line-clamp-3 mb-4">
|
|
{ticket.description}
|
|
</p>
|
|
|
|
<div class="flex justify-between items-center text-xs text-gray-500">
|
|
<div class="flex items-center space-x-4">
|
|
<span>#{ticket.id.substring(0, 8)}</span>
|
|
{#if ticket.category_name}
|
|
<span class="bg-gray-100 text-gray-600 px-2 py-1 rounded">
|
|
{ticket.category_name}
|
|
</span>
|
|
{/if}
|
|
{#if ticket.assigned_to_name}
|
|
<span class="flex items-center space-x-1">
|
|
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
</svg>
|
|
<span>{ticket.assigned_to_name}</span>
|
|
</span>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-3">
|
|
{#if ticket.due_date}
|
|
<span class="flex items-center space-x-1 {new Date(ticket.due_date) < new Date() ? 'text-red-600' : ''}">
|
|
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<span>Vence {formatRelativeTime(ticket.due_date)}</span>
|
|
</span>
|
|
{/if}
|
|
|
|
<span title={formatDate(ticket.updated_at)}>
|
|
Actualizado {formatRelativeTime(ticket.updated_at)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.line-clamp-3 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 3;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
</style> |