diff --git a/fresh.gen.ts b/fresh.gen.ts index 73d132d..1cfe548 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -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; diff --git a/routes/(apps)/mobility/(_islands)/SaveStudents.tsx b/routes/(apps)/mobility/(_islands)/SaveStudents.tsx new file mode 100644 index 0000000..01d1cc3 --- /dev/null +++ b/routes/(apps)/mobility/(_islands)/SaveStudents.tsx @@ -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([]); + const statusMessage = useSignal(""); + + 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 = {}; + 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 ( +
+

Loaded Promotions

+ +

{statusMessage.value}

+
    + {promotions.value.map((promotion) => ( +
  • + {promotion.name} +
      + {promotion.students.map((student, index) => ( +
    • + {student.firstName} {student.lastName} - {student.email} +
    • + ))} +
    +
  • + ))} +
+
+ ); +} diff --git a/routes/(apps)/mobility/(_islands)/UploadStudents.tsx b/routes/(apps)/mobility/(_islands)/UploadStudents.tsx new file mode 100644 index 0000000..c1e717d --- /dev/null +++ b/routes/(apps)/mobility/(_islands)/UploadStudents.tsx @@ -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([]); + const tempPromotions = useSignal([]); + const statusMessage = useSignal(""); + + 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 ( +
+

Upload Promotions

+ + +

{statusMessage.value}

+ +
+

Available Promotions

+
    + {promotions.value.map((promotion) => ( +
  • + {promotion.name} +
      + {promotion.students.map((student, index) => ( +
    • + {student.firstName} {student.lastName} - {student.email} +
    • + ))} +
    +
  • + ))} +
+
+
+ ); +} diff --git a/routes/(apps)/mobility/(_props)/props.ts b/routes/(apps)/mobility/(_props)/props.ts index a5fe572..b324ead 100644 --- a/routes/(apps)/mobility/(_props)/props.ts +++ b/routes/(apps)/mobility/(_props)/props.ts @@ -10,7 +10,7 @@ const properties: AppProperties = { mobility: "Mobility management", students: "Students management", }, - adminOnly: [], + adminOnly: ["students"], }; export default properties; diff --git a/routes/(apps)/mobility/partials/(admin)/students.tsx b/routes/(apps)/mobility/partials/(admin)/students.tsx deleted file mode 100644 index a73c859..0000000 --- a/routes/(apps)/mobility/partials/(admin)/students.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Partial } from "$fresh/runtime.ts"; -import { RouteConfig } from "$fresh/server.ts"; - -type ModulesProps = Record; - -export const config: RouteConfig = { - skipAppWrapper: true, - skipInheritedLayouts: true, -}; - -export default function Modules(_props: ModulesProps) { - return ( - - mobility - - ); -} diff --git a/routes/(apps)/mobility/partials/students.tsx b/routes/(apps)/mobility/partials/students.tsx new file mode 100644 index 0000000..9f40c20 --- /dev/null +++ b/routes/(apps)/mobility/partials/students.tsx @@ -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 ( + +

Manage Promotions

+ +
+ +
+ ); +}