mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-21 22:18:17 +02:00
Add edit and delete functionality for task management entries
Update the Avoirs page to display entries in a table format, including edit and delete icons for administrative users. 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/5h3gCuD
This commit is contained in:
1 parent
b3eb0644e0
commit
3ae1dc31fe
1 file changed
+126
-106
+126
-106
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock } from "lucide-react";
|
import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2 } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
@@ -418,112 +418,132 @@ export default function Avoirs() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Avoirs List */}
|
{/* Avoirs List - Format Table */}
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||||
{isLoading ? (
|
<div className="overflow-x-auto">
|
||||||
Array.from({ length: 6 }).map((_, i) => (
|
{isLoading ? (
|
||||||
<Card key={i} className="animate-pulse">
|
<div className="p-4">
|
||||||
<CardHeader>
|
<div className="animate-pulse space-y-4">
|
||||||
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
<div className="h-3 bg-gray-200 rounded w-1/2"></div>
|
<div key={i} className="h-16 bg-gray-200 rounded"></div>
|
||||||
</CardHeader>
|
))}
|
||||||
<CardContent>
|
</div>
|
||||||
<div className="space-y-2">
|
</div>
|
||||||
<div className="h-3 bg-gray-200 rounded"></div>
|
) : filteredAvoirs.length === 0 ? (
|
||||||
<div className="h-3 bg-gray-200 rounded w-4/5"></div>
|
<div className="p-12 text-center">
|
||||||
</div>
|
<FileText className="mx-auto h-12 w-12 text-gray-400" />
|
||||||
</CardContent>
|
<h3 className="mt-4 text-sm font-medium text-gray-900">Aucun avoir</h3>
|
||||||
</Card>
|
<p className="mt-1 text-sm text-gray-500">
|
||||||
))
|
{searchTerm ? "Aucun avoir ne correspond à votre recherche." : "Commencez par créer votre premier avoir."}
|
||||||
) : filteredAvoirs.length === 0 ? (
|
</p>
|
||||||
<div className="col-span-full text-center py-12">
|
{canCreate && !searchTerm && (
|
||||||
<FileText className="mx-auto h-12 w-12 text-gray-400" />
|
<Button className="mt-4" onClick={() => setIsCreateDialogOpen(true)}>
|
||||||
<h3 className="mt-2 text-sm font-medium text-gray-900">Aucun avoir</h3>
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
<p className="mt-1 text-sm text-gray-500">
|
Créer un avoir
|
||||||
Commencez par créer votre premier avoir.
|
</Button>
|
||||||
</p>
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filteredAvoirs.map((avoir) => (
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<Card key={avoir.id} className="hover:shadow-md transition-shadow">
|
<thead className="bg-gray-50">
|
||||||
<CardHeader>
|
<tr>
|
||||||
<div className="flex items-center justify-between">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
<CardTitle className="text-lg flex items-center">
|
Statut
|
||||||
{getStatusIcon(avoir.status)}
|
</th>
|
||||||
<span className="ml-2">#{avoir.invoiceReference || 'Sans référence'}</span>
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
</CardTitle>
|
Référence
|
||||||
<Badge variant={getStatusVariant(avoir.status)}>
|
</th>
|
||||||
{avoir.status}
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
</Badge>
|
Fournisseur
|
||||||
</div>
|
</th>
|
||||||
<CardDescription>
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
<div className="flex items-center">
|
Magasin
|
||||||
<div
|
</th>
|
||||||
className="w-3 h-3 rounded-full mr-2"
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
style={{ backgroundColor: avoir.group.color }}
|
Montant
|
||||||
/>
|
</th>
|
||||||
{avoir.group.name}
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
</div>
|
Créé le
|
||||||
</CardDescription>
|
</th>
|
||||||
</CardHeader>
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
<CardContent>
|
Actions
|
||||||
<div className="space-y-2">
|
</th>
|
||||||
<div className="flex justify-between items-center">
|
</tr>
|
||||||
<span className="text-sm text-muted-foreground">Fournisseur:</span>
|
</thead>
|
||||||
<span className="font-medium">{avoir.supplier.name}</span>
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
</div>
|
{filteredAvoirs.map((avoir) => {
|
||||||
|
const canEditDelete = ['admin', 'directeur'].includes((user as any)?.role);
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
return (
|
||||||
<span className="text-sm text-muted-foreground">Montant:</span>
|
<tr key={avoir.id} className="hover:bg-gray-50">
|
||||||
<span className="font-bold text-lg">{avoir.amount ? avoir.amount.toFixed(2) + ' €' : 'Non spécifié'}</span>
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
</div>
|
<div className="flex items-center">
|
||||||
|
{getStatusIcon(avoir.status)}
|
||||||
{avoir.comment && (
|
<Badge variant={getStatusVariant(avoir.status)} className="ml-2">
|
||||||
<div className="pt-2 border-t">
|
{avoir.status}
|
||||||
<p className="text-sm text-muted-foreground italic">
|
</Badge>
|
||||||
"{avoir.comment}"
|
</div>
|
||||||
</p>
|
</td>
|
||||||
</div>
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
)}
|
#{avoir.invoiceReference || 'Sans référence'}
|
||||||
|
</td>
|
||||||
<div className="pt-2 border-t">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||||
<div className="flex justify-between text-xs text-muted-foreground">
|
{avoir.supplier.name}
|
||||||
<span>
|
</td>
|
||||||
Par {avoir.creator.firstName && avoir.creator.lastName
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||||
? `${avoir.creator.firstName} ${avoir.creator.lastName}`
|
<div className="flex items-center">
|
||||||
: avoir.creator.username
|
<div
|
||||||
}
|
className="w-3 h-3 rounded-full mr-2"
|
||||||
</span>
|
style={{ backgroundColor: avoir.group.color }}
|
||||||
<span>
|
/>
|
||||||
{format(new Date(avoir.createdAt), "dd/MM/yyyy", { locale: fr })}
|
{avoir.group.name}
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</div>
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
|
{avoir.amount ? `${avoir.amount.toFixed(2)} €` : 'Non spécifié'}
|
||||||
{avoir.commercialProcessed && (
|
</td>
|
||||||
<div className="flex items-center text-xs text-green-600">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
<CheckCircle className="h-3 w-3 mr-1" />
|
<div>
|
||||||
Traité par commercial
|
<div>{format(new Date(avoir.createdAt), "dd/MM/yyyy", { locale: fr })}</div>
|
||||||
</div>
|
<div className="text-xs text-gray-400">
|
||||||
)}
|
Par {avoir.creator.firstName && avoir.creator.lastName
|
||||||
|
? `${avoir.creator.firstName} ${avoir.creator.lastName}`
|
||||||
<div className="flex items-center space-x-2">
|
: avoir.creator.username
|
||||||
{avoir.webhookSent && (
|
}
|
||||||
<Badge variant="secondary" className="text-xs">
|
</div>
|
||||||
Webhook envoyé
|
</div>
|
||||||
</Badge>
|
</td>
|
||||||
)}
|
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
{avoir.nocodbVerified && (
|
{canEditDelete ? (
|
||||||
<Badge variant="default" className="text-xs">
|
<div className="flex items-center space-x-2">
|
||||||
Vérifié NocoDB
|
<Button
|
||||||
</Badge>
|
variant="outline"
|
||||||
)}
|
size="sm"
|
||||||
</div>
|
className="h-8 w-8 p-0"
|
||||||
</div>
|
title="Modifier"
|
||||||
</CardContent>
|
>
|
||||||
</Card>
|
<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"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-400 text-xs">Lecture seule</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user