Checkpoint: Ajout de la colonne 'Export SP' dans le tableau historique de Ventilation FreePro : icône horloge grise = non exporté, icône verte = exporté (date au survol), icône rouge = erreur (message au survol). Le statut est sauvegardé en base (sharepointUploadStatus, sharepointUploadPath, sharepointUploadError, sharepointExportedAt) lors de chaque export via 'Envoyer vers SharePoint'.
This commit is contained in:
@@ -66,6 +66,10 @@ interface ImportRecord {
|
||||
nbLignes: number;
|
||||
totalTtc: string | null;
|
||||
createdAt: Date;
|
||||
sharepointUploadStatus: 'success' | 'error' | null;
|
||||
sharepointUploadPath: string | null;
|
||||
sharepointUploadError: string | null;
|
||||
sharepointExportedAt: Date | null;
|
||||
}
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
@@ -847,6 +851,7 @@ function VentilationFreeProContent() {
|
||||
<TableHead className="text-right">Lignes</TableHead>
|
||||
<TableHead className="text-right">Total TTC</TableHead>
|
||||
<TableHead>Importé le</TableHead>
|
||||
<TableHead className="text-center">Export SP</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -876,6 +881,29 @@ function VentilationFreeProContent() {
|
||||
<TableCell className="text-xs text-muted-foreground">
|
||||
{new Date(imp.createdAt).toLocaleDateString("fr-FR")}
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
{imp.sharepointUploadStatus === 'success' ? (
|
||||
<span
|
||||
title={`Exporté le ${imp.sharepointExportedAt ? new Date(imp.sharepointExportedAt).toLocaleString('fr-FR') : ''}${imp.sharepointUploadPath ? '\n' + imp.sharepointUploadPath : ''}`}
|
||||
className="inline-flex items-center gap-1 text-green-600 cursor-default"
|
||||
>
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
<span className="text-xs hidden sm:inline">Exporté</span>
|
||||
</span>
|
||||
) : imp.sharepointUploadStatus === 'error' ? (
|
||||
<span
|
||||
title={`Erreur : ${imp.sharepointUploadError || 'Erreur inconnue'}`}
|
||||
className="inline-flex items-center gap-1 text-red-500 cursor-default"
|
||||
>
|
||||
<XCircle className="h-4 w-4" />
|
||||
<span className="text-xs hidden sm:inline">Erreur</span>
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1 text-muted-foreground/40 cursor-default" title="Non exporté vers SharePoint">
|
||||
<Clock className="h-4 w-4" />
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex items-center justify-end gap-1" onClick={(e) => e.stopPropagation()}>
|
||||
<Button
|
||||
|
||||
4
drizzle/0030_vengeful_warbird.sql
Normal file
4
drizzle/0030_vengeful_warbird.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE `freeproImports` ADD `sharepointUploadStatus` enum('success','error');--> statement-breakpoint
|
||||
ALTER TABLE `freeproImports` ADD `sharepointUploadPath` text;--> statement-breakpoint
|
||||
ALTER TABLE `freeproImports` ADD `sharepointUploadError` text;--> statement-breakpoint
|
||||
ALTER TABLE `freeproImports` ADD `sharepointExportedAt` timestamp;
|
||||
2140
drizzle/meta/0030_snapshot.json
Normal file
2140
drizzle/meta/0030_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -211,6 +211,13 @@
|
||||
"when": 1780924653802,
|
||||
"tag": "0029_unknown_spot",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 30,
|
||||
"version": "5",
|
||||
"when": 1780930222253,
|
||||
"tag": "0030_vengeful_warbird",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -442,6 +442,14 @@ export const freeproImports = mysqlTable("freeproImports", {
|
||||
nbLignes: int("nbLignes").default(0).notNull(),
|
||||
/** Total général TTC calculé */
|
||||
totalTtc: varchar("totalTtc", { length: 30 }),
|
||||
/** Statut de l'export SharePoint : null = jamais exporté, 'success' = exporté, 'error' = erreur */
|
||||
sharepointUploadStatus: mysqlEnum("sharepointUploadStatus", ["success", "error"]),
|
||||
/** URL/chemin du fichier dans SharePoint après export réussi */
|
||||
sharepointUploadPath: text("sharepointUploadPath"),
|
||||
/** Message d'erreur si l'export SharePoint a échoué */
|
||||
sharepointUploadError: text("sharepointUploadError"),
|
||||
/** Date de l'export SharePoint */
|
||||
sharepointExportedAt: timestamp("sharepointExportedAt"),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
|
||||
20
server/db.ts
20
server/db.ts
@@ -1008,6 +1008,26 @@ export async function getFreeproImportWithLines(
|
||||
return { import: imports[0], lines };
|
||||
}
|
||||
|
||||
/** Met à jour le statut d'export SharePoint d'un import FreePro */
|
||||
export async function updateFreeproSharepointStatus(
|
||||
importId: number,
|
||||
status: 'success' | 'error',
|
||||
path?: string | null,
|
||||
error?: string | null
|
||||
): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) return;
|
||||
await db
|
||||
.update(freeproImports)
|
||||
.set({
|
||||
sharepointUploadStatus: status,
|
||||
sharepointUploadPath: path ?? null,
|
||||
sharepointUploadError: error ?? null,
|
||||
sharepointExportedAt: new Date(),
|
||||
})
|
||||
.where(eq(freeproImports.id, importId));
|
||||
}
|
||||
|
||||
/** Supprime un import FreePro et ses lignes */
|
||||
export async function deleteFreeproImport(importId: number): Promise<void> {
|
||||
const db = await getDb();
|
||||
|
||||
@@ -2436,9 +2436,16 @@ export const appRouter = router({
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
// Sauvegarder le statut d'erreur en base
|
||||
const { updateFreeproSharepointStatus } = await import('./db');
|
||||
await updateFreeproSharepointStatus(input.id, 'error', null, result.error || 'Erreur inconnue');
|
||||
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: result.error || 'Erreur upload SharePoint' });
|
||||
}
|
||||
|
||||
// Sauvegarder le statut de succès en base
|
||||
const { updateFreeproSharepointStatus } = await import('./db');
|
||||
await updateFreeproSharepointStatus(input.id, 'success', result.webUrl || null, null);
|
||||
|
||||
return { success: true, webUrl: result.webUrl, fileName };
|
||||
}),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user