feat : fix a lot of stuff
Check Deno code / Check Deno code (pull_request) Failing after 8s
Tests / Unit tests (pull_request) Successful in 13s
Tests / Integration tests (pull_request) Failing after 1m0s

This commit is contained in:
2026-04-30 13:49:47 +02:00
parent 04be659d6b
commit df3957741d
50 changed files with 2664 additions and 437 deletions
@@ -2,13 +2,17 @@
import * as XLSX from "https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs";
import { useRef } from "preact/hooks";
import { useSignal } from "@preact/signals";
import ImportResultPopup, {
type ImportDetail,
type ImportResult,
} from "$root/defaults/ImportResultPopup.tsx";
export default function UploadStudents() {
const file = useSignal<File | null>(null);
const dragging = useSignal(false);
const uploading = useSignal(false);
const error = useSignal<string | null>(null);
const success = useSignal<string | null>(null);
const importResult = useSignal<ImportResult | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
function pickFile(f: File) {
@@ -18,7 +22,7 @@ export default function UploadStudents() {
}
file.value = f;
error.value = null;
success.value = null;
importResult.value = null;
}
function onDragOver(e: DragEvent) {
@@ -46,36 +50,58 @@ export default function UploadStudents() {
if (!file.value) return;
uploading.value = true;
error.value = null;
success.value = null;
importResult.value = null;
try {
const arrayBuffer = await file.value.arrayBuffer();
const workbook = XLSX.read(arrayBuffer, { type: "array" });
let imported = 0;
let failed = 0;
let added = 0;
let errors = 0;
const details: ImportDetail[] = [];
for (const sheetName of workbook.SheetNames) {
const sheet = workbook.Sheets[sheetName];
const rows = XLSX.utils.sheet_to_json<{
numEtud: number;
nom: string;
prenom: string;
}>(sheet, { header: ["numEtud", "nom", "prenom"], range: 1 });
numEtud: number;
idPromo: string;
}>(sheet, {
header: ["nom", "prenom", "numEtud", "idPromo"],
range: 2,
});
for (const row of rows) {
const res = await fetch("/students/api/students", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ ...row, idPromo: sheetName }),
body: JSON.stringify(row),
});
if (res.ok) imported++;
else failed++;
if (res.ok) {
added++;
details.push({
type: "change",
message:
`${row.numEtud} : ${row.nom} ${row.prenom} -> ${row.idPromo}`,
});
} else {
errors++;
const body = await res.json().catch(() => ({}));
details.push({
type: "error",
message: `${row.numEtud} : ${body.error ?? "Erreur creation"}`,
});
}
}
}
success.value = `Import terminé — ${imported} ajouté${
imported !== 1 ? "s" : ""
}${failed > 0 ? `, ${failed} erreur${failed !== 1 ? "s" : ""}` : ""}`;
importResult.value = {
added,
modified: 0,
ignored: 0,
errors,
details,
};
} catch {
error.value = "Erreur lors de la lecture du fichier.";
} finally {
@@ -84,10 +110,7 @@ export default function UploadStudents() {
}
function downloadTemplate() {
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.aoa_to_sheet([["numEtud", "nom", "prenom"]]);
XLSX.utils.book_append_sheet(wb, ws, "4A22");
XLSX.writeFile(wb, "modele_etudiants.xlsx");
globalThis.open("/templates/modele_etudiants.xlsx", "_blank");
}
return (
@@ -117,10 +140,12 @@ export default function UploadStudents() {
</div>
{error.value && <p class="state-error">{error.value}</p>}
{success.value && (
<p style="font-size:0.82rem; color: light-dark(var(--light-accent-color), var(--dark-accent-color)); margin-bottom: 0.75rem">
{success.value}
</p>
{importResult.value && (
<ImportResultPopup
result={importResult.value}
onClose={() => (importResult.value = null)}
/>
)}
<div class="upload-actions">
@@ -142,9 +167,8 @@ export default function UploadStudents() {
</div>
<p class="upload-format">
Format : <strong>promo</strong> (nom de la feuille) |{" "}
<strong>numEtud</strong> | <strong>nom</strong> |{" "}
<strong>prénom</strong>
Format : <strong>Nom</strong> | <strong>Prenom</strong> |{" "}
<strong>Numero-etudiant</strong> | <strong>Promotion</strong>
</p>
</div>
);