mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-19 21:18:16 +02:00
feat(export): export modified gammes to excel with specified columns
This commit is contained in:
1 parent
90205bf51b
commit
515ca55836
2 files changed
+115
-1
No files matched your search
@@ -0,0 +1,75 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import ExcelJS from "exceljs";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const ExportModifiedGammesSchema = z.object({
|
||||||
|
nomFournisseur: z.string(),
|
||||||
|
changes: z.array(z.object({
|
||||||
|
codein: z.string(),
|
||||||
|
gtin: z.string(),
|
||||||
|
gamme: z.string(),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
const body = await req.json();
|
||||||
|
const parsed = ExportModifiedGammesSchema.safeParse(body);
|
||||||
|
if (!parsed.success) {
|
||||||
|
return NextResponse.json({ error: "Invalid payload" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { nomFournisseur, changes } = parsed.data;
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
workbook.creator = "CollectFlow";
|
||||||
|
workbook.created = new Date();
|
||||||
|
|
||||||
|
const sheet = workbook.addWorksheet("Gammes Modifiées", {
|
||||||
|
views: [{ state: "frozen", ySplit: 1 }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Header style
|
||||||
|
const headerStyle: Partial<ExcelJS.Style> = {
|
||||||
|
font: { bold: true, color: { argb: "FFFFFFFF" } },
|
||||||
|
fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FF0F172A" } },
|
||||||
|
alignment: { horizontal: "center" },
|
||||||
|
border: { bottom: { style: "thin", color: { argb: "FF334155" } } },
|
||||||
|
};
|
||||||
|
|
||||||
|
sheet.columns = [
|
||||||
|
{ header: "CODE FOURNISSEUR", key: "codein", width: 22 },
|
||||||
|
{ header: "GENCOD", key: "gtin", width: 18 },
|
||||||
|
{ header: "GAMME", key: "gamme", width: 12 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Apply header style
|
||||||
|
sheet.getRow(1).eachCell((cell) => { Object.assign(cell, headerStyle); });
|
||||||
|
sheet.getRow(1).height = 22;
|
||||||
|
|
||||||
|
for (const row of changes) {
|
||||||
|
sheet.addRow({
|
||||||
|
codein: row.codein,
|
||||||
|
gtin: row.gtin || "—",
|
||||||
|
gamme: row.gamme || "—",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Centrer la colonne Gamme
|
||||||
|
const gammeCol = sheet.getColumn("gamme");
|
||||||
|
gammeCol.alignment = { horizontal: "center" };
|
||||||
|
|
||||||
|
// Format codein et gtin en texte pour eviter la notation scientifique excel
|
||||||
|
sheet.getColumn("codein").numFmt = "@";
|
||||||
|
sheet.getColumn("gtin").numFmt = "@";
|
||||||
|
|
||||||
|
// Generate buffer
|
||||||
|
const buffer = await workbook.xlsx.writeBuffer();
|
||||||
|
|
||||||
|
const filename = `Modifications_Gammes_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`;
|
||||||
|
|
||||||
|
return new NextResponse(buffer, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"Content-Disposition": `attachment; filename="${filename}"`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -49,8 +49,46 @@ export function FloatingSummaryBar() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex space-x-3">
|
<div className="flex space-x-3 items-center">
|
||||||
<button
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
if (!hasDrafts) {
|
||||||
|
alert("Aucune gamme n'a été modifiée. L'export Excel ne concerne que les modifications en cours.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare payload
|
||||||
|
const modifiedRows = useGridStore.getState().rows.filter(r => draftChanges[r.codein]);
|
||||||
|
const changes = modifiedRows.map(r => ({
|
||||||
|
codein: r.codein,
|
||||||
|
gtin: r.gtin,
|
||||||
|
gamme: draftChanges[r.codein] as string,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Extract supplier name from the first row (or a generic name)
|
||||||
|
const nomFournisseur = modifiedRows.length > 0 ? modifiedRows[0].nomFournisseur : "Fournisseur";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/export/modified-gammes", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ nomFournisseur, changes }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) throw new Error("Erreur lors de l'export");
|
||||||
|
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = `Modifications_Gammes_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
alert("Une erreur s'est produite lors de la génération du fichier Excel.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
className="px-6 py-3 rounded-xl text-sm font-bold transition-all active:scale-95 border hover:brightness-105"
|
className="px-6 py-3 rounded-xl text-sm font-bold transition-all active:scale-95 border hover:brightness-105"
|
||||||
style={{
|
style={{
|
||||||
background: "var(--action-secondary-bg)",
|
background: "var(--action-secondary-bg)",
|
||||||
@@ -60,6 +98,7 @@ export function FloatingSummaryBar() {
|
|||||||
>
|
>
|
||||||
Export Excel
|
Export Excel
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="px-8 py-3 rounded-xl text-sm font-black transition-all shadow-lg active:scale-95 text-white hover:brightness-110"
|
className="px-8 py-3 rounded-xl text-sm font-black transition-all shadow-lg active:scale-95 text-white hover:brightness-110"
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
Reference in new issue
Block a user