Refactor: Use existing API for support

This commit is contained in:
gpt-engineer-app[bot]
2025-09-25 17:34:57 +00:00
parent 5771ff3a8d
commit a8baef01f2
3 changed files with 845 additions and 23 deletions

View File

@@ -1,35 +1,392 @@
import React from 'react';
import { Phone, MessageSquare, HeadphonesIcon } from 'lucide-react';
import React, { useState } from 'react';
import {
Phone,
MessageSquare,
HeadphonesIcon,
Ticket,
BookOpen,
BarChart3,
Plus,
Clock,
CheckCircle,
AlertCircle,
Users,
Star
} from 'lucide-react';
import { useSupport } from '@/hooks/useSupport';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
interface SupportTabProps {
isAdmin: boolean;
isSuperAdmin: boolean;
}
const SupportTab: React.FC<SupportTabProps> = ({ }) => {
const SupportTab: React.FC<SupportTabProps> = ({ isAdmin, isSuperAdmin }) => {
const {
tickets,
metrics,
knowledgeBase,
chatSessions,
loading,
error,
createTicket,
updateTicketStatus,
clearError
} = useSupport();
const [showCreateTicket, setShowCreateTicket] = useState(false);
const [newTicket, setNewTicket] = useState({
subject: '',
description: '',
priority: 'medium' as const,
category: ''
});
const handleCreateTicket = async () => {
if (!newTicket.subject || !newTicket.description) return;
try {
await createTicket({
...newTicket,
status: 'open',
userId: 'current-user' // This should come from auth context
});
setNewTicket({
subject: '',
description: '',
priority: 'medium',
category: ''
});
setShowCreateTicket(false);
} catch (error) {
console.error('Error creating ticket:', error);
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'urgent': return 'destructive';
case 'high': return 'destructive';
case 'medium': return 'default';
case 'low': return 'secondary';
default: return 'default';
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'open': return 'destructive';
case 'in_progress': return 'default';
case 'resolved': return 'default';
case 'closed': return 'secondary';
default: return 'default';
}
};
return (
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900">Centro de Soporte y Tickets</h2>
<div className="bg-white rounded-lg shadow p-8 text-center">
<HeadphonesIcon className="w-16 h-16 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Centro de Soporte y Tickets
</h3>
<p className="text-gray-600 mb-4">
Esta sección está en desarrollo y se implementará según las especificaciones del informe.
</p>
<div className="text-sm text-gray-500">
Funcionalidades pendientes:
<ul className="mt-2 space-y-1">
<li> Sistema de tickets de soporte</li>
<li> Chat en vivo</li>
<li> Base de conocimientos</li>
<li> Métricas de soporte</li>
<li> Escalación automática</li>
</ul>
</div>
<div className="flex justify-between items-center">
<h2 className="text-2xl font-bold text-foreground">Centro de Soporte y Tickets</h2>
<Button onClick={() => setShowCreateTicket(true)} className="flex items-center gap-2">
<Plus className="h-4 w-4" />
Nuevo Ticket
</Button>
</div>
{error && (
<Card className="border-destructive bg-destructive/10">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<p className="text-destructive">{error}</p>
<Button variant="ghost" size="sm" onClick={clearError}>
Cerrar
</Button>
</div>
</CardContent>
</Card>
)}
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
<TabsTrigger value="overview">Resumen</TabsTrigger>
<TabsTrigger value="tickets">Tickets</TabsTrigger>
<TabsTrigger value="chat">Chat en Vivo</TabsTrigger>
<TabsTrigger value="knowledge">Base de Conocimientos</TabsTrigger>
<TabsTrigger value="metrics">Métricas</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Tickets</CardTitle>
<Ticket className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{metrics?.totalTickets || 0}</div>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Tickets Abiertos</CardTitle>
<AlertCircle className="h-4 w-4 text-destructive" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-destructive">{metrics?.openTickets || 0}</div>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Tiempo Promedio</CardTitle>
<Clock className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{metrics?.averageResponseTime || 0}h</div>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Satisfacción</CardTitle>
<Star className="h-4 w-4 text-yellow-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{metrics?.customerSatisfaction || 0}/5</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="tickets" className="space-y-4">
<div className="space-y-4">
{loading ? (
<Card>
<CardContent className="p-8 text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Cargando tickets...</p>
</CardContent>
</Card>
) : tickets.length === 0 ? (
<Card>
<CardContent className="p-8 text-center">
<Ticket className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
<p className="text-muted-foreground">No hay tickets disponibles</p>
</CardContent>
</Card>
) : (
tickets.map((ticket) => (
<Card key={ticket.id}>
<CardHeader>
<div className="flex justify-between items-start">
<div>
<CardTitle className="text-lg">{ticket.subject}</CardTitle>
<CardDescription className="mt-1">
Ticket #{ticket.id} {new Date(ticket.createdAt).toLocaleDateString()}
</CardDescription>
</div>
<div className="flex gap-2">
<Badge variant={getPriorityColor(ticket.priority)}>
{ticket.priority}
</Badge>
<Badge variant={getStatusColor(ticket.status)}>
{ticket.status}
</Badge>
</div>
</div>
</CardHeader>
<CardContent>
<p className="text-muted-foreground mb-4">{ticket.description}</p>
<div className="flex justify-between items-center">
<span className="text-sm text-muted-foreground">
Categoría: {ticket.category}
</span>
{(isAdmin || isSuperAdmin) && (
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
onClick={() => updateTicketStatus(ticket.id, 'in_progress')}
>
En Progreso
</Button>
<Button
size="sm"
onClick={() => updateTicketStatus(ticket.id, 'resolved')}
>
Resolver
</Button>
</div>
)}
</div>
</CardContent>
</Card>
))
)}
</div>
</TabsContent>
<TabsContent value="chat" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<MessageSquare className="h-5 w-5" />
Chat en Vivo
</CardTitle>
<CardDescription>
Sistema de chat en tiempo real para soporte inmediato
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-center py-8">
<Users className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
<p className="text-muted-foreground">
{chatSessions.length === 0
? "No hay sesiones de chat activas"
: `${chatSessions.length} sesiones activas`
}
</p>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="knowledge" className="space-y-4">
<div className="space-y-4">
{knowledgeBase.length === 0 ? (
<Card>
<CardContent className="p-8 text-center">
<BookOpen className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
<p className="text-muted-foreground">No hay artículos disponibles</p>
</CardContent>
</Card>
) : (
knowledgeBase.map((article) => (
<Card key={article.id}>
<CardHeader>
<CardTitle>{article.title}</CardTitle>
<CardDescription>
Categoría: {article.category} {article.views} vistas {article.helpful} útiles
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">{article.content}</p>
<div className="flex flex-wrap gap-1 mt-4">
{article.tags.map((tag) => (
<Badge key={tag} variant="secondary" className="text-xs">
{tag}
</Badge>
))}
</div>
</CardContent>
</Card>
))
)}
</div>
</TabsContent>
<TabsContent value="metrics" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<BarChart3 className="h-5 w-5" />
Métricas de Soporte
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h4 className="font-semibold mb-2">Resumen de Tickets</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span>Total:</span>
<span className="font-medium">{metrics?.totalTickets || 0}</span>
</div>
<div className="flex justify-between">
<span>Abiertos:</span>
<span className="font-medium text-destructive">{metrics?.openTickets || 0}</span>
</div>
<div className="flex justify-between">
<span>Resueltos:</span>
<span className="font-medium text-green-600">{metrics?.resolvedTickets || 0}</span>
</div>
</div>
</div>
<div>
<h4 className="font-semibold mb-2">Rendimiento</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span>Tiempo Promedio:</span>
<span className="font-medium">{metrics?.averageResponseTime || 0} horas</span>
</div>
<div className="flex justify-between">
<span>Satisfacción:</span>
<span className="font-medium">{metrics?.customerSatisfaction || 0}/5 </span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
{showCreateTicket && (
<Card className="mt-6">
<CardHeader>
<CardTitle>Crear Nuevo Ticket</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Input
placeholder="Asunto del ticket"
value={newTicket.subject}
onChange={(e) => setNewTicket(prev => ({ ...prev, subject: e.target.value }))}
/>
<Textarea
placeholder="Descripción del problema"
value={newTicket.description}
onChange={(e) => setNewTicket(prev => ({ ...prev, description: e.target.value }))}
/>
<div className="grid grid-cols-2 gap-4">
<Select
value={newTicket.priority}
onValueChange={(value: any) => setNewTicket(prev => ({ ...prev, priority: value }))}
>
<SelectTrigger>
<SelectValue placeholder="Prioridad" />
</SelectTrigger>
<SelectContent>
<SelectItem value="low">Baja</SelectItem>
<SelectItem value="medium">Media</SelectItem>
<SelectItem value="high">Alta</SelectItem>
<SelectItem value="urgent">Urgente</SelectItem>
</SelectContent>
</Select>
<Input
placeholder="Categoría"
value={newTicket.category}
onChange={(e) => setNewTicket(prev => ({ ...prev, category: e.target.value }))}
/>
</div>
<div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => setShowCreateTicket(false)}>
Cancelar
</Button>
<Button onClick={handleCreateTicket}>
Crear Ticket
</Button>
</div>
</CardContent>
</Card>
)}
</div>
);
};