diff --git a/backend/dane.json b/backend/dane.json index d017372..bcfe233 100644 --- a/backend/dane.json +++ b/backend/dane.json @@ -7,7 +7,7 @@ "Zadanie domowe": 10, "Aktywność": 3, "Sprawdzian": 4, - "good_frekwencja": 4, + "good_frekwencja": 9, "aktywnosc": 2, "bad_frekwencja": 0, "sprawdzian": 1 @@ -46,5 +46,18 @@ "bad_frekwencja": 27 } } - ] + ], + "Jan-Kowalski": { + "bad_frekwencja": 2, + "good_frekwencja": 11, + "dobra_frekwencja": 10, + "aktywnosc": 1, + "sprawdzian": 3 + }, + "Anna-Nowak": { + "good_frekwencja": 4 + }, + "Piotr-Wiśniewski": { + "dobra_frekwencja": 3 + } } \ No newline at end of file diff --git a/backend/kryteria.json b/backend/kryteria.json index efc01ba..f67af9a 100644 --- a/backend/kryteria.json +++ b/backend/kryteria.json @@ -1,6 +1,19 @@ [ - { "nazwa": "good_frekwencja", "punkty": 10 }, - { "nazwa": "bad_frekwencja", "punkty": -10 }, - { "nazwa": "aktywnosc", "punkty": 5 }, - { "nazwa": "sprawdzian", "punkty": -200 } + { + "nazwa": "dobra_frekwencja", + "punkty": 111 + }, + { + "nazwa": "zla_frekwencja", + + "punkty": -10 + }, + { + "nazwa": "aktywnosc", + "punkty": 5 + }, + { + "nazwa": "sprawdzian", + "punkty": -20 + } ] \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index c6a88bd..c994477 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2,7 +2,6 @@ const express = require("express"); const fs = require("fs"); const path = require("path"); const cors = require("cors"); - const app = express(); const PORT = 4000; @@ -22,7 +21,8 @@ app.get("/uczniowie", (req, res) => { console.error("Błąd odczytu uczniów:", err); return res.status(500).send("Błąd odczytu uczniów."); } - res.json(JSON.parse(data)); + const uczniowieData = JSON.parse(data); + res.json({ uczniowie: uczniowieData.uczniowie }); }); }); @@ -42,16 +42,24 @@ app.get("/dane", (req, res) => { fs.readFile(DANE_FILE, "utf8", (err, data) => { if (err) { console.error("Błąd odczytu danych:", err); - return res.status(500).send("Błąd odczytu danych."); + // Jeśli plik nie istnieje, utwórz go z domyślnymi danymi + const defaultData = {}; + fs.writeFile(DANE_FILE, JSON.stringify(defaultData, null, 2), err => { + if (err) { + console.error("Błąd tworzenia pliku danych:", err); + return res.status(500).send("Błąd tworzenia pliku danych."); + } + res.json(defaultData); // Zwróć puste dane + }); + } else { + res.json(JSON.parse(data)); } - res.json(JSON.parse(data)); }); }); // Endpoint do zapisywania danych uczniów app.post("/dane", (req, res) => { const newData = req.body; - fs.writeFile(DANE_FILE, JSON.stringify(newData, null, 2), err => { if (err) { console.error("Błąd zapisywania danych:", err); @@ -61,6 +69,85 @@ app.post("/dane", (req, res) => { }); }); +// Endpoint do dodawania nowego kryterium +app.post("/kryteria", (req, res) => { + const { nazwa, punkty } = req.body; + if (!nazwa || !punkty) { + return res.status(400).send("Brak wymaganych danych."); + } + fs.readFile(KRYTERIA_FILE, "utf8", (err, data) => { + if (err) { + console.error("Błąd odczytu kryteriów:", err); + return res.status(500).send("Błąd odczytu kryteriów."); + } + const kryteria = JSON.parse(data); + // Sprawdź, czy kryterium już istnieje + if (kryteria.some(k => k.nazwa === nazwa)) { + return res.status(400).send("Kryterium już istnieje."); + } + kryteria.push({ nazwa, punkty }); + fs.writeFile(KRYTERIA_FILE, JSON.stringify(kryteria, null, 2), err => { + if (err) { + console.error("Błąd zapisywania kryteriów:", err); + return res.status(500).send("Błąd zapisywania kryteriów."); + } + res.send("Kryterium dodane pomyślnie."); + }); + }); +}); + +// Endpoint do edycji istniejącego kryterium +app.put("/kryteria/:nazwa", (req, res) => { + const { nazwa } = req.params; + const { nowaNazwa, punkty } = req.body; + if (!nowaNazwa || !punkty) { + return res.status(400).send("Brak wymaganych danych."); + } + fs.readFile(KRYTERIA_FILE, "utf8", (err, data) => { + if (err) { + console.error("Błąd odczytu kryteriów:", err); + return res.status(500).send("Błąd odczytu kryteriów."); + } + const kryteria = JSON.parse(data); + const index = kryteria.findIndex(k => k.nazwa === nazwa); + if (index === -1) { + return res.status(404).send("Kryterium nie istnieje."); + } + kryteria[index] = { nazwa: nowaNazwa, punkty }; + fs.writeFile(KRYTERIA_FILE, JSON.stringify(kryteria, null, 2), err => { + if (err) { + console.error("Błąd zapisywania kryteriów:", err); + return res.status(500).send("Błąd zapisywania kryteriów."); + } + res.send("Kryterium zaktualizowane pomyślnie."); + }); + }); +}); + +// Endpoint do usuwania kryterium +app.delete("/kryteria/:nazwa", (req, res) => { + const { nazwa } = req.params; + fs.readFile(KRYTERIA_FILE, "utf8", (err, data) => { + if (err) { + console.error("Błąd odczytu kryteriów:", err); + return res.status(500).send("Błąd odczytu kryteriów."); + } + const kryteria = JSON.parse(data); + const index = kryteria.findIndex(k => k.nazwa === nazwa); + if (index === -1) { + return res.status(404).send("Kryterium nie istnieje."); + } + kryteria.splice(index, 1); + fs.writeFile(KRYTERIA_FILE, JSON.stringify(kryteria, null, 2), err => { + if (err) { + console.error("Błąd zapisywania kryteriów:", err); + return res.status(500).send("Błąd zapisywania kryteriów."); + } + res.send("Kryterium usunięte pomyślnie."); + }); + }); +}); + // Uruchomienie serwera app.listen(PORT, () => { console.log(`Serwer API działa na http://localhost:${PORT}`); diff --git a/backend/uczniowie.json b/backend/uczniowie.json index 96740bd..07dca56 100644 --- a/backend/uczniowie.json +++ b/backend/uczniowie.json @@ -1,12 +1,8 @@ { - "klasy": { - "1A": [ - { "imie": "Jan", "nazwisko": "Kowalski" }, - { "imie": "Anna", "nazwisko": "Nowak" } - ], - "2B": [ - { "imie": "Piotr", "nazwisko": "Wiśniewski" }, - { "imie": "Maria", "nazwisko": "Kowalczyk" } - ] - } + "uczniowie": [ + { "imie": "Jan", "nazwisko": "Kowalski", "klasa": "4i" }, + { "imie": "Anna", "nazwisko": "Nowak", "klasa": "3a" }, + { "imie": "Piotr", "nazwisko": "Wiśniewski", "klasa": "2b" }, + { "imie": "Maria", "nazwisko": "Kowalczyk", "klasa": "1c" } + ] } \ No newline at end of file diff --git a/index.html b/index.html index 00dbc99..105263c 100644 --- a/index.html +++ b/index.html @@ -9,27 +9,48 @@

Zarządzanie Uczniami

-
+
- - - - -
- - + +
- \ No newline at end of file diff --git a/script.js b/script.js index 9e23380..d3d515d 100644 --- a/script.js +++ b/script.js @@ -1,13 +1,20 @@ -let uczniowie = {}; -let kryteria = []; -let dane = {}; - document.addEventListener("DOMContentLoaded", () => { - const classSelect = document.getElementById("class-select"); const studentsList = document.getElementById("students-list"); - const saveButton = document.getElementById("save-button"); - const statsTab = document.getElementById("stats-tab"); const statsContent = document.getElementById("stats-content"); + const criteriaContent = document.getElementById("criteria-content"); + const saveButton = document.getElementById("save-button"); + const studentsTab = document.getElementById("students-tab"); + const statsTab = document.getElementById("stats-tab"); + const criteriaTab = document.getElementById("criteria-tab"); + const addCriteriaForm = document.getElementById("add-criteria-form"); + const editCriteriaForm = document.getElementById("edit-criteria-form"); + const deleteCriteriaForm = document.getElementById("delete-criteria-form"); + const criteriaListDiv = document.getElementById("criteria-list"); + const editCriteriaNameSelect = document.getElementById("edit-criteria-name"); + const deleteCriteriaNameSelect = document.getElementById("delete-criteria-name"); + let uczniowie = []; + let kryteria = []; + let dane = {}; // Ładowanie danych z serwera Promise.all([ @@ -15,147 +22,79 @@ document.addEventListener("DOMContentLoaded", () => { fetch("http://localhost:4000/kryteria").then(response => response.json()), fetch("http://localhost:4000/dane").then(response => response.json()) ]) - .then(([uczniowieData, kryteriaData, daneData]) => { - uczniowie = uczniowieData.klasy; - kryteria = kryteriaData; - dane = daneData; + .then(([uczniowieData, kryteriaData, daneData]) => { + uczniowie = uczniowieData.uczniowie; + kryteria = kryteriaData; + dane = daneData; - populateClassSelect(); - renderStats(); // Renderuj statystyki przy ładowaniu - }) - .catch(error => console.error("Błąd ładowania danych:", error)); + console.log("Uczniowie:", uczniowie); + console.log("Kryteria:", kryteria); + console.log("Dane:", dane); - // Wypełnij listę klas - function populateClassSelect() { - Object.keys(uczniowie).forEach(klasa => { - const option = document.createElement("option"); - option.value = klasa; - option.textContent = klasa; - classSelect.appendChild(option); - }); - - // Wyświetl pierwszą klasę domyślnie - if (classSelect.options.length > 0) { - classSelect.value = classSelect.options[0].value; - renderStudents(classSelect.value); - } - } - - // Aktualizuj listę uczniów po wyborze klasy - classSelect.addEventListener("change", () => { - const selectedClass = classSelect.value; - renderStudents(selectedClass); - renderStats(); // Aktualizuj statystyki po zmianie klasy - }); + renderStudents(); + renderStats(); + renderCriteria(); + }) + .catch(error => console.error("Błąd ładowania danych:", error)); // Renderuj uczniów - function renderStudents(className) { + function renderStudents() { studentsList.innerHTML = ""; - const students = uczniowie[className]; - - students.forEach(student => { - const studentKey = `${className}-${student.imie}-${student.nazwisko}`; + uczniowie.forEach(student => { + const studentKey = `${student.imie}-${student.nazwisko}`; const details = document.createElement("details"); const summary = document.createElement("summary"); - - summary.textContent = `${student.imie} ${student.nazwisko}`; + summary.textContent = `${student.imie} ${student.nazwisko} (${student.klasa})`; details.appendChild(summary); - const criteriaList = document.createElement("div"); criteriaList.className = "criteria-list"; - kryteria.forEach(criteria => { const criteriaItem = document.createElement("div"); criteriaItem.className = "criteria-item"; - const label = document.createElement("span"); label.textContent = criteria.nazwa; - const pointsInfo = document.createElement("span"); pointsInfo.className = "points-info"; pointsInfo.textContent = `(${criteria.punkty} pkt)`; - const input = document.createElement("input"); input.type = "number"; input.min = "0"; - - // Wyszukaj dane ucznia w pliku `dane.json` - const savedStudent = dane[className]?.find(s => s.uczen.imie === student.imie && s.uczen.nazwisko === student.nazwisko); - input.value = savedStudent?.uczen[criteria.nazwa] || 0; - + const savedStudent = dane[studentKey]; + input.value = savedStudent?.[criteria.nazwa] || 0; input.addEventListener("input", () => { if (!savedStudent) { - dane[className] = dane[className] || []; - dane[className].push({ - uczen: { - imie: student.imie, - nazwisko: student.nazwisko - } - }); + dane[studentKey] = {}; } - - const currentStudent = dane[className].find(s => s.uczen.imie === student.imie && s.uczen.nazwisko === student.nazwisko); - currentStudent.uczen[criteria.nazwa] = parseInt(input.value) || 0; - - renderStats(); // Aktualizuj statystyki po edycji danych + dane[studentKey][criteria.nazwa] = parseInt(input.value) || 0; + renderStats(); }); - criteriaItem.appendChild(label); criteriaItem.appendChild(pointsInfo); criteriaItem.appendChild(input); criteriaList.appendChild(criteriaItem); }); - details.appendChild(criteriaList); studentsList.appendChild(details); }); } - // Zapisz dane na zewnętrznym serwerze - saveButton.addEventListener("click", () => { - fetch("http://localhost:4000/dane", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(dane) - }) - .then(response => { - if (response.ok) { - alert("Dane zostały zapisane!"); - } else { - alert("Wystąpił błąd podczas zapisywania danych."); - } - }) - .catch(error => console.error("Błąd zapisywania danych:", error)); - }); - // Renderuj statystyki function renderStats() { statsContent.innerHTML = ""; - - const selectedClass = classSelect.value; - const students = uczniowie[selectedClass]; - - if (!students) return; - - students.forEach(student => { - const savedStudent = dane[selectedClass]?.find(s => s.uczen.imie === student.imie && s.uczen.nazwisko === student.nazwisko); - + uczniowie.forEach(student => { + const studentKey = `${student.imie}-${student.nazwisko}`; + const savedStudent = dane[studentKey]; let totalPoints = 0; if (savedStudent) { kryteria.forEach(criteria => { - const count = savedStudent.uczen[criteria.nazwa] || 0; + const count = savedStudent[criteria.nazwa] || 0; totalPoints += count * criteria.punkty; }); } - const studentDiv = document.createElement("div"); studentDiv.className = "stat-item"; - const name = document.createElement("strong"); - name.textContent = `${student.imie} ${student.nazwisko}`; - + name.textContent = `${student.imie} ${student.nazwisko} (${student.klasa})`; const points = document.createElement("span"); points.className = "points"; points.textContent = `${totalPoints} pkt`; @@ -164,22 +103,166 @@ document.addEventListener("DOMContentLoaded", () => { } else if (totalPoints < 0) { points.classList.add("negative"); } - studentDiv.appendChild(name); studentDiv.appendChild(points); statsContent.appendChild(studentDiv); }); } - // Przełączanie między zakładkami - statsTab.addEventListener("click", () => { - studentsList.style.display = "none"; - statsContent.style.display = "block"; - }); + // Renderuj listę kryteriów + function renderCriteria() { + criteriaListDiv.innerHTML = ""; + editCriteriaNameSelect.innerHTML = ""; + deleteCriteriaNameSelect.innerHTML = ""; - const studentsTab = document.getElementById("students-tab"); + if (!Array.isArray(kryteria) || kryteria.length === 0) { + console.warn("Brak kryteriów do wyświetlenia."); + const noCriteriaMessage = document.createElement("p"); + noCriteriaMessage.textContent = "Brak dostępnych kryteriów."; + criteriaListDiv.appendChild(noCriteriaMessage); + return; + } + + kryteria.forEach(criteria => { + // Wyświetl kryterium na liście + const criteriaDiv = document.createElement("div"); + criteriaDiv.textContent = `${criteria.nazwa}: ${criteria.punkty} pkt`; + criteriaListDiv.appendChild(criteriaDiv); + + // Dodaj opcję do edycji + const editOption = document.createElement("option"); + editOption.value = criteria.nazwa; + editOption.textContent = criteria.nazwa; + editCriteriaNameSelect.appendChild(editOption); + + // Dodaj opcję do usunięcia + const deleteOption = document.createElement("option"); + deleteOption.value = criteria.nazwa; + deleteOption.textContent = criteria.nazwa; + deleteCriteriaNameSelect.appendChild(deleteOption); + }); + } + + // Przełączanie między zakładkami studentsTab.addEventListener("click", () => { studentsList.style.display = "block"; statsContent.style.display = "none"; + criteriaContent.style.display = "none"; + }); + + statsTab.addEventListener("click", () => { + studentsList.style.display = "none"; + statsContent.style.display = "block"; + criteriaContent.style.display = "none"; + }); + + criteriaTab.addEventListener("click", () => { + studentsList.style.display = "none"; + statsContent.style.display = "none"; + criteriaContent.style.display = "block"; // Pokaż zakładkę "Zarządzanie kryteriami" + renderCriteria(); // Odśwież listę kryteriów + }); + + // Dodawanie nowego kryterium + addCriteriaForm.addEventListener("submit", event => { + event.preventDefault(); + const nazwa = document.getElementById("new-criteria-name").value.trim(); + const punkty = parseInt(document.getElementById("new-criteria-points").value); + + if (!nazwa || isNaN(punkty)) { + alert("Wprowadź poprawne dane."); + return; + } + + fetch("http://localhost:4000/kryteria", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nazwa, punkty }) + }) + .then(response => { + if (!response.ok) throw new Error("Błąd dodawania kryterium."); + return response.text(); + }) + .then(() => { + alert("Kryterium dodane pomyślnie."); + return fetch("http://localhost:4000/kryteria").then(response => response.json()); + }) + .then(kryteriaData => { + kryteria = kryteriaData; + renderCriteria(); + }) + .catch(error => console.error(error)); + }); + + // Edycja istniejącego kryterium + editCriteriaForm.addEventListener("submit", event => { + event.preventDefault(); + const nazwa = editCriteriaNameSelect.value; + const nowaNazwa = document.getElementById("edit-criteria-new-name").value.trim(); + const punkty = parseInt(document.getElementById("edit-criteria-points").value); + + if (!nowaNazwa || isNaN(punkty)) { + alert("Wprowadź poprawne dane."); + return; + } + + fetch(`http://localhost:4000/kryteria/${encodeURIComponent(nazwa)}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nowaNazwa, punkty }) + }) + .then(response => { + if (!response.ok) throw new Error("Błąd edycji kryterium."); + return response.text(); + }) + .then(() => { + alert("Kryterium zaktualizowane pomyślnie."); + return fetch("http://localhost:4000/kryteria").then(response => response.json()); + }) + .then(kryteriaData => { + kryteria = kryteriaData; + renderCriteria(); + }) + .catch(error => console.error(error)); + }); + + // Usuwanie kryterium + deleteCriteriaForm.addEventListener("submit", event => { + event.preventDefault(); + const nazwa = deleteCriteriaNameSelect.value; + + fetch(`http://localhost:4000/kryteria/${encodeURIComponent(nazwa)}`, { + method: "DELETE" + }) + .then(response => { + if (!response.ok) throw new Error("Błąd usuwania kryterium."); + return response.text(); + }) + .then(() => { + alert("Kryterium usunięte pomyślnie."); + return fetch("http://localhost:4000/kryteria").then(response => response.json()); + }) + .then(kryteriaData => { + kryteria = kryteriaData; + renderCriteria(); + }) + .catch(error => console.error(error)); + }); + + // Zapisz dane na zewnętrznym serwerze + saveButton.addEventListener("click", () => { + fetch("http://localhost:4000/dane", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(dane) + }) + .then(response => { + if (response.ok) { + alert("Dane zostały zapisane!"); + } else { + alert("Wystąpił błąd podczas zapisywania danych."); + } + }) + .catch(error => console.error("Błąd zapisywania danych:", error)); }); }); \ No newline at end of file diff --git a/style.css b/style.css index ea5894f..e7d6952 100644 --- a/style.css +++ b/style.css @@ -25,7 +25,7 @@ label { font-weight: bold; } -select { +select, input { width: 100%; padding: 10px; margin-top: 5px; @@ -69,75 +69,24 @@ button:hover { background-color: #0056b3; } -/* Zakładka Uczniowie */ -details { +/* Lista kryteriów */ +#criteria-list div { + margin: 10px 0; + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + background-color: #f9f9f9; +} + +/* Formularze */ +form { + margin-top: 20px; +} + +form label { + margin-top: 10px; +} + +form input, form select { margin-bottom: 10px; - padding: 10px; - border: 1px solid #ddd; - border-radius: 4px; - background-color: #f9f9f9; -} - -summary { - font-weight: bold; - cursor: pointer; - outline: none; -} - -.criteria-list { - margin-left: 20px; -} - -.criteria-item { - display: flex; - align-items: center; - margin: 5px 0; -} - -.criteria-item span { - margin-right: 10px; - font-weight: bold; -} - -.criteria-item .points-info { - font-size: 12px; - color: #666; - margin-left: 10px; -} - -.criteria-item input { - width: 50px; - margin-left: 10px; - padding: 5px; - border: 1px solid #ccc; - border-radius: 4px; -} - -/* Zakładka Statystyka */ -.stat-item { - margin-bottom: 15px; - padding: 10px; - border: 1px solid #ddd; - border-radius: 8px; - background-color: #f9f9f9; - display: flex; - justify-content: space-between; - align-items: center; -} - -.stat-item strong { - font-size: 16px; -} - -.stat-item .points { - font-weight: bold; - font-size: 18px; -} - -.stat-item .points.positive { - color: green; -} - -.stat-item .points.negative { - color: red; } \ No newline at end of file