Temporary student manager

This commit is contained in:
Clayzxr
2025-01-17 10:40:49 +01:00
parent 7c91f6c69b
commit a24f8b35c2
6 changed files with 235 additions and 21 deletions
+9 -3
View File
@@ -5,9 +5,9 @@
import * as $_apps_layout from "./routes/(apps)/_layout.tsx";
import * as $_apps_mobility_index from "./routes/(apps)/mobility/index.tsx";
import * as $_apps_mobility_partials_admin_mobility from "./routes/(apps)/mobility/partials/(admin)/mobility.tsx";
import * as $_apps_mobility_partials_admin_students from "./routes/(apps)/mobility/partials/(admin)/students.tsx";
import * as $_apps_mobility_partials_index from "./routes/(apps)/mobility/partials/index.tsx";
import * as $_apps_mobility_partials_overview from "./routes/(apps)/mobility/partials/overview.tsx";
import * as $_apps_mobility_partials_students from "./routes/(apps)/mobility/partials/students.tsx";
import * as $_apps_notes_index from "./routes/(apps)/notes/index.tsx";
import * as $_apps_notes_partials_admin_courses from "./routes/(apps)/notes/partials/(admin)/courses.tsx";
import * as $_apps_notes_partials_admin_students from "./routes/(apps)/notes/partials/(admin)/students.tsx";
@@ -23,6 +23,8 @@ import * as $login from "./routes/login.tsx";
import * as $logout from "./routes/logout.tsx";
import * as $_islands_AppNavigator from "./routes/(_islands)/AppNavigator.tsx";
import * as $_islands_Navbar from "./routes/(_islands)/Navbar.tsx";
import * as $_apps_mobility_islands_SaveStudents from "./routes/(apps)/mobility/(_islands)/SaveStudents.tsx";
import * as $_apps_mobility_islands_UploadStudents from "./routes/(apps)/mobility/(_islands)/UploadStudents.tsx";
import type { Manifest } from "$fresh/server.ts";
const manifest = {
@@ -31,12 +33,12 @@ const manifest = {
"./routes/(apps)/mobility/index.tsx": $_apps_mobility_index,
"./routes/(apps)/mobility/partials/(admin)/mobility.tsx":
$_apps_mobility_partials_admin_mobility,
"./routes/(apps)/mobility/partials/(admin)/students.tsx":
$_apps_mobility_partials_admin_students,
"./routes/(apps)/mobility/partials/index.tsx":
$_apps_mobility_partials_index,
"./routes/(apps)/mobility/partials/overview.tsx":
$_apps_mobility_partials_overview,
"./routes/(apps)/mobility/partials/students.tsx":
$_apps_mobility_partials_students,
"./routes/(apps)/notes/index.tsx": $_apps_notes_index,
"./routes/(apps)/notes/partials/(admin)/courses.tsx":
$_apps_notes_partials_admin_courses,
@@ -56,6 +58,10 @@ const manifest = {
islands: {
"./routes/(_islands)/AppNavigator.tsx": $_islands_AppNavigator,
"./routes/(_islands)/Navbar.tsx": $_islands_Navbar,
"./routes/(apps)/mobility/(_islands)/SaveStudents.tsx":
$_apps_mobility_islands_SaveStudents,
"./routes/(apps)/mobility/(_islands)/UploadStudents.tsx":
$_apps_mobility_islands_UploadStudents,
},
baseUrl: import.meta.url,
} satisfies Manifest;
@@ -0,0 +1,82 @@
import { useSignal } from "@preact/signals";
import Papa from "https://cdn.skypack.dev/papaparse";
type Student = {
firstName: string;
lastName: string;
email: string;
};
type Promotion = {
name: string;
students: Student[];
};
export default function SaveStudents() {
const promotions = useSignal<Promotion[]>([]);
const statusMessage = useSignal<string>("");
const loadCSV = async () => {
try {
const response = await fetch("/api/students"); // Assurez-vous que l'API est appelée correctement
if (!response.ok) {
throw new Error("Failed to load CSV file");
}
const csvText = await response.text(); // Lire le contenu en texte
const parsedData = Papa.parse(csvText, {
header: true,
skipEmptyLines: true,
});
const groupedPromotions: Record<string, Student[]> = {};
parsedData.data.forEach((row: any) => {
const { promotion, firstName, lastName, email } = row;
if (!groupedPromotions[promotion]) {
groupedPromotions[promotion] = [];
}
groupedPromotions[promotion].push({ firstName, lastName, email });
});
const loadedPromotions = Object.entries(groupedPromotions).map(
([name, students]) => ({
name,
students,
})
);
promotions.value = loadedPromotions;
statusMessage.value = "Data loaded successfully!";
} catch (error) {
console.error("Error loading CSV file:", error);
statusMessage.value = "Failed to load data. Please try again.";
}
};
// Charger les données CSV dès le chargement du composant
loadCSV();
return (
<div>
<h2>Loaded Promotions</h2>
<button onClick={loadCSV}>Actualiser</button>
<p>{statusMessage.value}</p>
<ul>
{promotions.value.map((promotion) => (
<li key={promotion.name}>
<strong>{promotion.name}</strong>
<ul>
{promotion.students.map((student, index) => (
<li key={index}>
{student.firstName} {student.lastName} - {student.email}
</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
@@ -0,0 +1,123 @@
// @deno-types="https://cdn.sheetjs.com/xlsx-0.20.3/package/types/index.d.ts"
import * as XLSX from "https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs";
import { useSignal } from "@preact/signals";
import Papa from "https://cdn.skypack.dev/papaparse"; // Bibliothèque pour manipuler CSVs
type Student = {
firstName: string;
lastName: string;
email: string;
};
type Promotion = {
name: string;
students: Student[];
};
export default function UploadStudents() {
const promotions = useSignal<Promotion[]>([]);
const tempPromotions = useSignal<Promotion[]>([]);
const statusMessage = useSignal<string>("");
const handleFileUpload = async (event: Event) => {
const input = event.target as HTMLInputElement;
if (!input.files || input.files.length === 0) {
statusMessage.value = "No file selected";
return;
}
const file = input.files[0];
const reader = new FileReader();
reader.onload = async (e) => {
try {
const arrayBuffer = e.target?.result as ArrayBuffer;
// Lire le classeur Excel
const workbook = XLSX.read(arrayBuffer, { type: "array" });
const newPromotions: Promotion[] = [];
workbook.SheetNames.forEach((sheetName) => {
const worksheet = workbook.Sheets[sheetName];
const students: Student[] = XLSX.utils.sheet_to_json(worksheet, {
header: ["firstName", "lastName", "email"],
range: 1, // Skip header row
});
newPromotions.push({ name: sheetName, students });
});
tempPromotions.value = newPromotions; // Charger temporairement les promotions
statusMessage.value = "File loaded. Please confirm to save.";
} catch (error) {
statusMessage.value = "Error processing file";
console.error(error);
}
};
reader.readAsArrayBuffer(file);
};
const confirmFileUpload = () => {
if (tempPromotions.value.length > 0) {
promotions.value = tempPromotions.value; // Mettre à jour les promotions
tempPromotions.value = []; // Réinitialiser le tampon temporaire
statusMessage.value = "Promotions updated successfully!";
savePromotionsToCSV(promotions.value); // Enregistrer dans un fichier CSV
} else {
statusMessage.value = "No data to confirm.";
}
};
const savePromotionsToCSV = (data: Promotion[]) => {
const csvData = data.map((promotion) => {
return promotion.students.map((student) => ({
promotion: promotion.name,
firstName: student.firstName,
lastName: student.lastName,
email: student.email,
}));
});
const flatData = csvData.flat();
const csv = Papa.unparse(flatData);
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
const url = URL.createObjectURL(blob);
// Télécharger le fichier
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", "students.csv");
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
<div>
<h1>Upload Promotions</h1>
<input type="file" accept=".xlsx, .xls" onChange={handleFileUpload} />
<button onClick={confirmFileUpload}>Confirm Upload</button>
<p>{statusMessage.value}</p>
<div>
<h2>Available Promotions</h2>
<ul>
{promotions.value.map((promotion) => (
<li key={promotion.name}>
<strong>{promotion.name}</strong>
<ul>
{promotion.students.map((student, index) => (
<li key={index}>
{student.firstName} {student.lastName} - {student.email}
</li>
))}
</ul>
</li>
))}
</ul>
</div>
</div>
);
}
+1 -1
View File
@@ -10,7 +10,7 @@ const properties: AppProperties = {
mobility: "Mobility management",
students: "Students management",
},
adminOnly: [],
adminOnly: ["students"],
};
export default properties;
@@ -1,17 +0,0 @@
import { Partial } from "$fresh/runtime.ts";
import { RouteConfig } from "$fresh/server.ts";
type ModulesProps = Record<string | number | symbol, never>;
export const config: RouteConfig = {
skipAppWrapper: true,
skipInheritedLayouts: true,
};
export default function Modules(_props: ModulesProps) {
return (
<Partial name="body">
<a href="mobility" f-partial={"notes/partials"}>mobility</a>
</Partial>
);
}
@@ -0,0 +1,20 @@
import { Partial } from "$fresh/runtime.ts";
import { RouteConfig } from "$fresh/server.ts";
import UploadStudents from "../(_islands)/UploadStudents.tsx";
import SaveStudents from "../(_islands)/SaveStudents.tsx";
export const config: RouteConfig = {
skipAppWrapper: true,
skipInheritedLayouts: true,
};
export default function Students() {
return (
<Partial name="body">
<h1>Manage Promotions</h1>
<UploadStudents />
<hr />
<SaveStudents />
</Partial>
);
}