Checkpoint: Transformation des cellules Service, Type d'achat et Ventilation en menus déroulants éditables directement dans la liste des factures. Les modifications sont sauvegardées automatiquement avec notification de succès.
This commit is contained in:
@@ -37,6 +37,8 @@ export default function Invoices() {
|
|||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [invoiceToDelete, setInvoiceToDelete] = useState<number | null>(null);
|
const [invoiceToDelete, setInvoiceToDelete] = useState<number | null>(null);
|
||||||
const { data: invoices, isLoading } = trpc.invoices.list.useQuery();
|
const { data: invoices, isLoading } = trpc.invoices.list.useQuery();
|
||||||
|
const { data: departments } = trpc.departments.getByUser.useQuery();
|
||||||
|
const { data: allocations } = trpc.accountingAllocations.getByUser.useQuery();
|
||||||
const utils = trpc.useUtils();
|
const utils = trpc.useUtils();
|
||||||
|
|
||||||
const exportExcelMutation = trpc.sftp.exportToExcel.useMutation({
|
const exportExcelMutation = trpc.sftp.exportToExcel.useMutation({
|
||||||
@@ -101,6 +103,16 @@ export default function Invoices() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updateFieldMutation = trpc.invoices.update.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Facture mise à jour");
|
||||||
|
utils.invoices.list.invalidate();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.message || "Erreur lors de la mise à jour");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const filteredInvoices = invoices?.filter((inv) => {
|
const filteredInvoices = invoices?.filter((inv) => {
|
||||||
// Filter by search query
|
// Filter by search query
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
@@ -365,9 +377,56 @@ export default function Invoices() {
|
|||||||
? `${parseFloat(invoice.totalAmount as string).toFixed(2)} €`
|
? `${parseFloat(invoice.totalAmount as string).toFixed(2)} €`
|
||||||
: "-"}
|
: "-"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-sm">{invoice.serviceConcerne || "-"}</TableCell>
|
<TableCell className="text-sm">
|
||||||
<TableCell className="text-sm">{invoice.typeAchat || "-"}</TableCell>
|
<select
|
||||||
<TableCell className="text-sm">{invoice.ventilationComptable || "-"}</TableCell>
|
value={invoice.serviceConcerne || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateFieldMutation.mutate({
|
||||||
|
id: invoice.id,
|
||||||
|
data: { serviceConcerne: e.target.value || undefined },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="w-full px-2 py-1 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<option value="">-</option>
|
||||||
|
{departments?.map((dept) => (
|
||||||
|
<option key={dept.id} value={dept.name}>{dept.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-sm">
|
||||||
|
<select
|
||||||
|
value={invoice.typeAchat || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateFieldMutation.mutate({
|
||||||
|
id: invoice.id,
|
||||||
|
data: { typeAchat: e.target.value as "CAPEX" | "OPEX" | undefined },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="w-full px-2 py-1 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<option value="">-</option>
|
||||||
|
<option value="CAPEX">CAPEX</option>
|
||||||
|
<option value="OPEX">OPEX</option>
|
||||||
|
</select>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-sm">
|
||||||
|
<select
|
||||||
|
value={invoice.ventilationComptable || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateFieldMutation.mutate({
|
||||||
|
id: invoice.id,
|
||||||
|
data: { ventilationComptable: e.target.value || undefined },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="w-full px-2 py-1 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<option value="">-</option>
|
||||||
|
{allocations?.map((alloc) => (
|
||||||
|
<option key={alloc.id} value={alloc.name}>{alloc.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</TableCell>
|
||||||
<TableCell>{getQualityBadge(invoice.qualityScore)}</TableCell>
|
<TableCell>{getQualityBadge(invoice.qualityScore)}</TableCell>
|
||||||
<TableCell>{getExportStatusBadge(invoice.exportStatus || "not_exported")}</TableCell>
|
<TableCell>{getExportStatusBadge(invoice.exportStatus || "not_exported")}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
|||||||
8
todo.md
8
todo.md
@@ -222,3 +222,11 @@
|
|||||||
- [x] Ajouter un bouton "Supprimer" avec confirmation (AlertDialog)
|
- [x] Ajouter un bouton "Supprimer" avec confirmation (AlertDialog)
|
||||||
- [x] Utiliser la route tRPC existante pour supprimer une facture
|
- [x] Utiliser la route tRPC existante pour supprimer une facture
|
||||||
- [x] Tester la suppression avec confirmation
|
- [x] Tester la suppression avec confirmation
|
||||||
|
|
||||||
|
## Édition directe des champs métier dans la liste
|
||||||
|
- [x] Charger les listes de services et ventilations dans la page Invoices
|
||||||
|
- [x] Transformer la cellule Service en menu déroulant éditable
|
||||||
|
- [x] Transformer la cellule Type d'achat en menu déroulant éditable
|
||||||
|
- [x] Transformer la cellule Ventilation en menu déroulant éditable
|
||||||
|
- [x] Sauvegarder automatiquement les modifications (mutation updateFieldMutation)
|
||||||
|
- [x] Afficher une notification de succès après sauvegarde
|
||||||
|
|||||||
Reference in New Issue
Block a user