Update the SAV ticket design to match the tasks module layout

Refactors the client/src/pages/SavTickets.tsx file to implement a card-based layout for SAV tickets, aligning its visual design with the existing tasks module.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 818c4995-85cc-4d53-8067-b67dcee52c2d
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
michaelschal committed 2025-09-11 11:42:31 +00:00
1 parent d48e0425e8
commit 7c29e8b0e1
1 file changed
+101 -118
+101 -118
View File
@@ -826,143 +826,126 @@ export default function SavTickets() {
<p className="text-gray-500">Aucun ticket trouvé</p> <p className="text-gray-500">Aucun ticket trouvé</p>
</div> </div>
) : ( ) : (
<div> <div className="p-6">
<table className="w-full"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<thead className="bg-gray-50 border-b"> {filteredTickets.map((ticket) => {
<tr> const StatusIcon = statusConfig[ticket.status as keyof typeof statusConfig]?.icon || Clock;
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> const priorityColor = priorityConfig[ticket.priority as keyof typeof priorityConfig]?.color || "bg-gray-100 text-gray-800";
Ticket const statusColor = statusConfig[ticket.status as keyof typeof statusConfig]?.color || "bg-gray-100 text-gray-800";
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> return (
Client <Card key={ticket.id} className={`hover:shadow-md transition-shadow cursor-pointer ${ticket.status === 'ferme' ? 'opacity-60' : ''}`}>
</th> <CardContent className="p-4">
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> {/* Header avec numéro de ticket et statut */}
Produit <div className="flex items-start justify-between mb-3">
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Problème
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Statut
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Priorité
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Fournisseur
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Date
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredTickets.map((ticket) => {
const StatusIcon = statusConfig[ticket.status as keyof typeof statusConfig]?.icon || Clock;
return (
<tr key={ticket.id} className={`hover:bg-gray-50 ${ticket.status === 'ferme' ? 'bg-gray-100 opacity-60' : ''}`}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center"> <div className="flex items-center">
<Wrench className="h-4 w-4 text-gray-400 mr-2" /> <Wrench className="h-4 w-4 text-gray-400 mr-2" />
<span className="text-sm font-medium text-gray-900 mr-2"> <h5 className="font-medium text-gray-900 text-sm">{ticket.ticketNumber}</h5>
{ticket.ticketNumber}
</span>
{hasRecentComments(ticket) && ( {hasRecentComments(ticket) && (
<Badge className="bg-orange-100 text-orange-800 text-xs"> <Badge className="bg-orange-100 text-orange-800 text-xs ml-2">
<MessageCircle className="h-3 w-3 mr-1" /> <MessageCircle className="h-3 w-3 mr-1" />
Nouveau Nouveau
</Badge> </Badge>
)} )}
</div> </div>
</td>
<td className="px-6 py-4">
<div>
<div className="text-sm font-medium text-gray-900">
{ticket.clientName || 'N/A'}
</div>
{ticket.clientPhone && (
<div className="text-sm text-gray-500">
{ticket.clientPhone}
</div>
)}
</div>
</td>
<td className="px-6 py-4">
<div>
{ticket.productDesignation && (
<div className="text-sm font-medium text-gray-900">
{ticket.productDesignation}
</div>
)}
{ticket.productGencode && (
<div className="text-sm text-gray-500">
{ticket.productGencode}
</div>
)}
</div>
</td>
<td className="px-6 py-4">
<div className="text-sm text-gray-900 max-w-xs truncate">
{ticket.problemDescription}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center"> <div className="flex items-center">
<StatusIcon className="h-4 w-4 mr-2" /> <StatusIcon className="h-3 w-3 mr-1" />
<Badge className={statusConfig[ticket.status as keyof typeof statusConfig]?.color}> <Badge className={`${statusColor} text-xs`}>
{statusConfig[ticket.status as keyof typeof statusConfig]?.label} {statusConfig[ticket.status as keyof typeof statusConfig]?.label}
</Badge> </Badge>
</div> </div>
</td> </div>
<td className="px-6 py-4 whitespace-nowrap">
<Badge className={priorityConfig[ticket.priority as keyof typeof priorityConfig]?.color}> {/* Client */}
<div className="mb-2">
<div className="text-sm font-medium text-gray-900">
{ticket.clientName || 'N/A'}
</div>
{ticket.clientPhone && (
<div className="text-xs text-gray-500">
📞 {ticket.clientPhone}
</div>
)}
</div>
{/* Produit */}
<div className="mb-2">
{ticket.productDesignation && (
<div className="text-xs text-gray-600 font-medium">
{ticket.productDesignation}
</div>
)}
{ticket.productGencode && (
<div className="text-xs text-gray-500">
📦 {ticket.productGencode}
</div>
)}
</div>
{/* Problème */}
{ticket.problemDescription && (
<div className="mb-3">
<p className="text-xs text-gray-600 line-clamp-2">
{ticket.problemDescription}
</p>
</div>
)}
{/* Priorité et fournisseur */}
<div className="flex items-center justify-between mb-3">
<Badge className={`${priorityColor} text-xs`}>
{priorityConfig[ticket.priority as keyof typeof priorityConfig]?.label} {priorityConfig[ticket.priority as keyof typeof priorityConfig]?.label}
</Badge> </Badge>
</td>
<td className="px-6 py-4 whitespace-nowrap"> <div className="flex items-center text-xs text-gray-500">
<div className="flex items-center"> <Building className="h-3 w-3 mr-1" />
<Building className="h-4 w-4 text-gray-400 mr-2" /> {ticket.supplier?.name}
<span className="text-sm text-gray-900">
{ticket.supplier?.name}
</span>
</div> </div>
</td> </div>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="flex items-center"> {/* Date */}
<Calendar className="h-4 w-4 mr-1" /> <div className="text-xs text-gray-500 mb-3 flex items-center">
{safeFormat(ticket.createdAt, 'dd/MM/yyyy')} <Calendar className="h-3 w-3 mr-1" />
</div> {safeFormat(ticket.createdAt, 'dd/MM/yyyy')}
</td> </div>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div className="flex justify-end space-x-2"> {/* Actions */}
<div className="flex items-center gap-1">
<Button
variant="outline"
size="sm"
onClick={() => handleViewTicket(ticket)}
title="Suivi du ticket"
className="text-xs h-7"
>
<ClipboardList className="h-3 w-3" />
</Button>
{canModify && (
<Button <Button
variant="ghost" variant="outline"
size="sm" size="sm"
onClick={() => handleViewTicket(ticket)} onClick={() => handleEditTicket(ticket)}
title="Suivi du ticket" className="text-xs h-7"
> >
<ClipboardList className="h-4 w-4" /> <Edit className="h-3 w-3" />
</Button> </Button>
{canModify && ( )}
<Button variant="ghost" size="sm" onClick={() => handleEditTicket(ticket)}> {canDelete && (
<Edit className="h-4 w-4" /> <Button
</Button> variant="outline"
)} size="sm"
{canDelete && ( className="text-red-600 hover:text-red-700 text-xs h-7"
<Button variant="ghost" size="sm" className="text-red-600 hover:text-red-900" onClick={() => handleDeleteTicket(ticket)}> onClick={() => handleDeleteTicket(ticket)}
<Trash2 className="h-4 w-4" /> >
</Button> <Trash2 className="h-3 w-3" />
)} </Button>
</div> )}
</td> </div>
</tr> </CardContent>
); </Card>
})} );
</tbody> })}
</table> </div>
</div> </div>
)} )}
</CardContent> </CardContent>