Temporary student manager
This commit is contained in:
+9
-3
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user