mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Improve display of commercial processing status for avoirs
Adjusted the display of the "Traité por commercial" status by wrapping the icon in a div to ensure consistent tooltip behavior across different browsers. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869/7eY9Hl8
This commit is contained in:
1 parent
c2edd36ccc
commit
acbc36fda1
2 files changed
+3
-582
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
+3
-582
@@ -989,7 +989,9 @@ export default function Avoirs() {
|
||||
<div className="flex items-center">
|
||||
{avoir.amount !== null && avoir.amount !== undefined ? `${Number(avoir.amount).toFixed(2)} €` : 'Non spécifié'}
|
||||
{avoir.commercialProcessed && (
|
||||
<UserCheck className="ml-2 h-4 w-4 text-purple-600" title="Traité par commercial" />
|
||||
<div title="Traité par commercial">
|
||||
<UserCheck className="ml-2 h-4 w-4 text-purple-600" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
@@ -1692,584 +1694,3 @@ export default function Avoirs() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Input
|
||||
placeholder="Rechercher par référence, fournisseur, commentaire..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Filtrage maintenant géré par le contexte global Layout */}
|
||||
</div>
|
||||
|
||||
{/* Avoirs List - Format Table */}
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
{isLoading ? (
|
||||
<div className="p-4">
|
||||
<div className="animate-pulse space-y-4">
|
||||
{Array.from({ length: 3 }).map((_, i) => (
|
||||
<div key={i} className="h-16 bg-gray-200 rounded"></div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : filteredAvoirs.length === 0 ? (
|
||||
<div className="p-12 text-center">
|
||||
<FileText className="mx-auto h-12 w-12 text-gray-400" />
|
||||
<h3 className="mt-4 text-sm font-medium text-gray-900">Aucun avoir</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
{searchTerm ? "Aucun avoir ne correspond à votre recherche." : "Commencez par créer votre premier avoir."}
|
||||
</p>
|
||||
{!searchTerm && (
|
||||
<Button className="mt-4" onClick={() => setIsCreateDialogOpen(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Créer un avoir
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<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">
|
||||
Statut
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Référence
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Magasin
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Montant
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Créé le
|
||||
</th>
|
||||
<th className="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Vérification
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{filteredAvoirs?.filter(avoir => avoir && avoir.id).map((avoir) => {
|
||||
const canEditDelete = ['admin', 'directeur'].includes((user as any)?.role);
|
||||
|
||||
return (
|
||||
<tr key={avoir?.id || Math.random()} className={`hover:bg-gray-50 ${avoir?.nocodbVerified ? 'bg-gray-100 opacity-75' : ''}`}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{avoir.supplier?.name || 'Fournisseur non défini'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<Select
|
||||
value={avoir?.status || ''}
|
||||
onValueChange={(newStatus) => handleStatusChange(avoir.id, newStatus)}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="En attente de demande">En attente de demande</SelectItem>
|
||||
<SelectItem value="Demandé">Demandé</SelectItem>
|
||||
<SelectItem value="Reçu">Reçu</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
#{avoir.invoiceReference || 'Sans référence'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="w-3 h-3 rounded-full mr-2"
|
||||
style={{ backgroundColor: avoir.group?.color || '#gray' }}
|
||||
/>
|
||||
{avoir.group?.name || 'Magasin non défini'}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<div className="flex items-center">
|
||||
{avoir.amount !== null && avoir.amount !== undefined ? `${Number(avoir.amount).toFixed(2)} €` : 'Non spécifié'}
|
||||
{avoir.commercialProcessed && (
|
||||
<div title="Avoir fait par commercial">
|
||||
<UserCheck className="h-4 w-4 text-blue-600 ml-2" />
|
||||
</div>
|
||||
)}
|
||||
{avoir.webhookSent && (
|
||||
<div title="Webhook envoyé">
|
||||
<Send className="h-4 w-4 text-green-600 ml-2" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<div>
|
||||
<div>
|
||||
{avoir?.createdAt
|
||||
? format(new Date(avoir.createdAt), "dd/MM/yyyy", { locale: fr })
|
||||
: 'Non défini'
|
||||
}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
Par {avoir?.creator?.firstName && avoir?.creator?.lastName
|
||||
? `${avoir.creator.firstName} ${avoir.creator.lastName}`
|
||||
: avoir?.creator?.username || 'Utilisateur inconnu'
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-center text-sm">
|
||||
{avoir.invoiceReference?.trim() ? (
|
||||
<div className="flex items-center justify-center">
|
||||
{verifyingAvoirs.has(avoir.id) ? (
|
||||
<Clock className="h-4 w-4 text-blue-500 animate-spin" />
|
||||
) : (avoirVerificationResults[avoir.id]?.exists === true || avoir.nocodbVerified) ? (
|
||||
<div title={`Vérifié: ${avoirVerificationResults[avoir.id]?.exists ? 'cache' : avoir.nocodbVerified ? 'validé' : 'inconnu'}`}>
|
||||
<CheckCircle className="h-4 w-4 text-green-500 cursor-help" />
|
||||
</div>
|
||||
) : avoirVerificationResults[avoir.id]?.exists === false ? (
|
||||
<XCircle className="h-4 w-4 text-red-500 cursor-help" />
|
||||
) : (
|
||||
<button
|
||||
onClick={() => handleVerifyAvoirInvoice(avoir)}
|
||||
className="h-4 w-4 text-gray-400 hover:text-blue-500 transition-colors"
|
||||
title="Cliquer pour vérifier la facture"
|
||||
>
|
||||
<Search className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-gray-400 text-xs">Sans réf.</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex items-center space-x-2">
|
||||
{/* 📋 Icône Upload pour avoirs "Reçu" */}
|
||||
{avoir?.status === 'Reçu' && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-blue-600 hover:text-blue-700 hover:bg-blue-50"
|
||||
title="Envoyer fichier avoir"
|
||||
onClick={() => handleOpenAvoirModal(avoir)}
|
||||
>
|
||||
<Upload className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Bouton Valider - apparaît si vérification réussie et pas encore validé */}
|
||||
{avoirVerificationResults[avoir.id]?.exists === true && !avoir.nocodbVerified && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-green-600 hover:text-green-700 hover:bg-green-50"
|
||||
title="Valider l'avoir"
|
||||
onClick={() => handleValidateAvoir(avoir.id)}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Bouton Dévalider - pour admins sur avoirs validés */}
|
||||
{avoir.nocodbVerified && canEditDelete && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-orange-600 hover:text-orange-700 hover:bg-orange-50"
|
||||
title="Dévalider l'avoir"
|
||||
onClick={() => handleDevalidateAvoir(avoir.id)}
|
||||
>
|
||||
<XCircle className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Bouton Commentaire - apparaît seulement s'il y a un commentaire */}
|
||||
{avoir.comment && avoir.comment.trim() !== '' && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-purple-600 hover:text-purple-700 hover:bg-purple-50"
|
||||
title="Voir le commentaire"
|
||||
onClick={() => handleViewComment(avoir.comment || '')}
|
||||
>
|
||||
<MessageSquare className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{canEditDelete && (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0"
|
||||
title="Modifier"
|
||||
onClick={() => handleEdit(avoir)}
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||
title="Supprimer"
|
||||
onClick={() => handleDelete(avoir)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{!canEditDelete && avoir?.status !== 'Reçu' && (
|
||||
<span className="text-gray-400 text-xs">Lecture seule</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Edit Modal */}
|
||||
<Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Modifier l'avoir</DialogTitle>
|
||||
<DialogDescription>
|
||||
Modifiez les informations de l'avoir.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...editForm}>
|
||||
<form onSubmit={editForm.handleSubmit(onEditSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={editForm.control}
|
||||
name="supplierId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Fournisseur *</FormLabel>
|
||||
<Select
|
||||
value={field.value.toString()}
|
||||
onValueChange={(value) => field.onChange(parseInt(value))}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Sélectionnez un fournisseur" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{suppliers.map((supplier) => (
|
||||
<SelectItem key={supplier.id} value={supplier.id.toString()}>
|
||||
{supplier.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={editForm.control}
|
||||
name="invoiceReference"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Référence facture</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Référence de la facture (optionnel)" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={editForm.control}
|
||||
name="amount"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Montant</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="Montant en euros (optionnel)"
|
||||
value={editAmountValue}
|
||||
onChange={(e) => {
|
||||
// ✅ Utiliser seulement l'état local, complètement séparé de React Hook Form
|
||||
setEditAmountValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={editForm.control}
|
||||
name="status"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Statut</FormLabel>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="En attente de demande">En attente de demande</SelectItem>
|
||||
<SelectItem value="Demandé">Demandé</SelectItem>
|
||||
<SelectItem value="Reçu">Reçu</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={editForm.control}
|
||||
name="comment"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Commentaire</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Commentaire ou description (optionnel)"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={editForm.control}
|
||||
name="commercialProcessed"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel>
|
||||
Avoir fait par commercial
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end space-x-2 pt-4">
|
||||
<Button type="button" variant="outline" onClick={() => setIsEditDialogOpen(false)}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={editAvoirMutation.isPending}
|
||||
>
|
||||
{editAvoirMutation.isPending ? "Modification..." : "Modifier"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Delete Modal */}
|
||||
<AlertDialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Confirmer la suppression</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Êtes-vous sûr de vouloir supprimer cet avoir ? Cette action ne peut pas être annulée.
|
||||
{selectedAvoir && (
|
||||
<div className="mt-4 p-3 bg-gray-50 rounded-lg">
|
||||
<div className="text-sm">
|
||||
<div><strong>Fournisseur:</strong> {selectedAvoir?.supplier?.name || 'Fournisseur non défini'}</div>
|
||||
<div><strong>Référence:</strong> {selectedAvoir?.invoiceReference || 'Sans référence'}</div>
|
||||
<div><strong>Montant:</strong> {selectedAvoir?.amount !== null && selectedAvoir?.amount !== undefined ? `${Number(selectedAvoir.amount).toFixed(2)} €` : 'Non spécifié'}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Annuler</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmDelete}
|
||||
disabled={deleteAvoirMutation.isPending}
|
||||
className="bg-red-600 hover:bg-red-700"
|
||||
>
|
||||
{deleteAvoirMutation.isPending ? "Suppression..." : "Supprimer"}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
{/* 🌐 Modal d'envoi d'avoir (comme rapprochement) */}
|
||||
<Dialog open={showAvoirModal} onOpenChange={setShowAvoirModal}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Envoyer Fichier Avoir</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
{selectedAvoirForUpload && (
|
||||
<div className="bg-gray-50 p-3 rounded-md">
|
||||
<div className="text-sm">
|
||||
<div className="font-medium">{selectedAvoirForUpload.supplier?.name || 'Fournisseur non défini'}</div>
|
||||
<div className="text-gray-600">
|
||||
Référence: {selectedAvoirForUpload.invoiceReference || 'Non renseigné'}
|
||||
</div>
|
||||
<div className="text-gray-600">
|
||||
Magasin: {selectedAvoirForUpload?.group?.name || 'Magasin non défini'}
|
||||
</div>
|
||||
<div className="text-gray-600">
|
||||
Montant: {selectedAvoirForUpload?.amount !== null && selectedAvoirForUpload?.amount !== undefined ? `${Number(selectedAvoirForUpload.amount).toFixed(2)} €` : 'Non spécifié'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="avoir-pdf-file">Fichier PDF</Label>
|
||||
<input
|
||||
id="avoir-pdf-file"
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={handleFileChange}
|
||||
className="w-full p-2 border border-gray-300 rounded-md"
|
||||
/>
|
||||
{selectedFile && (
|
||||
<div className="text-sm text-green-600">
|
||||
Fichier sélectionné: {selectedFile.name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selectedAvoirForUpload && (
|
||||
<div className="text-xs text-gray-500">
|
||||
Envoi via webhook configuré pour {selectedAvoirForUpload.group?.name || 'magasin'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCloseAvoirModal}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSendAvoir}
|
||||
disabled={!selectedFile || isUploading}
|
||||
>
|
||||
{isUploading ? "Envoi..." : "Envoyer"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* ⏱️ Modal d'attente pour le traitement du webhook */}
|
||||
<Dialog open={showWaitingModal} onOpenChange={() => {}}>
|
||||
<DialogContent className="sm:max-w-[400px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center space-x-2">
|
||||
<Clock className="w-5 h-5 text-blue-600 animate-spin" />
|
||||
<span>Traitement en cours</span>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-6 py-6">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 mx-auto mb-4 relative">
|
||||
<div className="w-16 h-16 border-4 border-gray-200 border-t-blue-600 rounded-full animate-spin"></div>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-xs font-bold text-blue-600">{processingSeconds}s</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-medium text-gray-900">
|
||||
Traitement de votre avoir en cours...
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Le workflow peut prendre jusqu'à 1 minute.
|
||||
<br />
|
||||
Veuillez patienter.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 bg-gray-100 rounded-full h-2 w-full">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded-full transition-all duration-1000 ease-out"
|
||||
style={{ width: `${Math.min((processingSeconds / 60) * 100, 100)}%` }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
{processingSeconds < 60 ? `${60 - processingSeconds}s restantes (max)` : 'Finalisation...'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedAvoirForUpload && (
|
||||
<div className="bg-blue-50 p-3 rounded-md">
|
||||
<div className="text-sm">
|
||||
<div className="font-medium text-blue-900">
|
||||
{selectedAvoirForUpload.supplier?.name || 'Fournisseur non défini'}
|
||||
</div>
|
||||
<div className="text-blue-700">
|
||||
Référence: {selectedAvoirForUpload.invoiceReference || 'Non renseigné'}
|
||||
</div>
|
||||
<div className="text-blue-700">
|
||||
Fichier: {selectedFile?.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Modal de commentaire */}
|
||||
<Dialog open={showCommentModal} onOpenChange={setShowCommentModal}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Commentaire</DialogTitle>
|
||||
<DialogDescription>
|
||||
Détails du commentaire de cet avoir
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="py-4">
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<p className="text-sm text-gray-700 whitespace-pre-wrap">
|
||||
{selectedComment}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button variant="outline" onClick={() => setShowCommentModal(false)}>
|
||||
Fermer
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user