Compare commits

...

6 Commits
main ... build

Author SHA1 Message Date
rzodkiew
9399b55714 swer2 2025-02-19 12:18:08 +01:00
rzodkiew
85b9141087 swe 2025-02-14 14:45:18 +01:00
rzodkiew
7595d1bd8f na spokojnie jak obiad 3 2025-02-14 14:31:21 +01:00
rzodkiew
c5388b5690 spokojnie jak obiad 2 2025-02-14 12:49:24 +01:00
rzodkiew
5a15a8d5ac na spokojnie jak obiad 2025-02-14 12:17:22 +01:00
rzodkiew
1c65005b0a swer 2025-02-14 11:33:27 +01:00
8 changed files with 176 additions and 92 deletions

View File

@ -50,14 +50,23 @@
"Jan-Kowalski": { "Jan-Kowalski": {
"bad_frekwencja": 2, "bad_frekwencja": 2,
"good_frekwencja": 11, "good_frekwencja": 11,
"dobra_frekwencja": 10, "dobra_frekwencja": 3,
"aktywnosc": 1, "aktywnosc": 0,
"sprawdzian": 3 "sprawdzian": 3,
"siema": 15,
"zla_frekwencja": 5,
"konkursy": 0
}, },
"Anna-Nowak": { "Anna-Nowak": {
"good_frekwencja": 4 "good_frekwencja": 4,
"dobra_frekwencja": 0,
"aktywnosc": 10
}, },
"Piotr-Wiśniewski": { "Piotr-Wiśniewski": {
"dobra_frekwencja": 3 "dobra_frekwencja": 0,
"aktywnosc": 25
},
"Maria-Kowalczyk": {
"dobra_frekwencja": 1
} }
} }

View File

@ -5,7 +5,6 @@
}, },
{ {
"nazwa": "zla_frekwencja", "nazwa": "zla_frekwencja",
"punkty": -10 "punkty": -10
}, },
{ {
@ -13,7 +12,15 @@
"punkty": 5 "punkty": 5
}, },
{ {
"nazwa": "sprawdzian", "nazwa": "siema",
"punkty": -20 "punkty": 2
},
{
"nazwa": "konkursy",
"punkty": 15
},
{
"nazwa": "obiad",
"punkty": 25
} }
] ]

9
backend/oceny.json Normal file
View File

@ -0,0 +1,9 @@
{
"progi": [
{ "min": 90, "ocena": 5 },
{ "min": 75, "ocena": 4 },
{ "min": 60, "ocena": 3 },
{ "min": 50, "ocena": 2 },
{ "min": 0, "ocena": 1 }
]
}

View File

@ -9,6 +9,7 @@ const PORT = 4000;
const UCZNIOWIE_FILE = path.join(__dirname, "uczniowie.json"); const UCZNIOWIE_FILE = path.join(__dirname, "uczniowie.json");
const KRYTERIA_FILE = path.join(__dirname, "kryteria.json"); const KRYTERIA_FILE = path.join(__dirname, "kryteria.json");
const DANE_FILE = path.join(__dirname, "dane.json"); const DANE_FILE = path.join(__dirname, "dane.json");
const OCENY_FILE = path.join(__dirname, "oceny.json"); // Nowy plik
// Middleware do obsługi JSON i CORS // Middleware do obsługi JSON i CORS
app.use(express.json()); app.use(express.json());
@ -42,14 +43,13 @@ app.get("/dane", (req, res) => {
fs.readFile(DANE_FILE, "utf8", (err, data) => { fs.readFile(DANE_FILE, "utf8", (err, data) => {
if (err) { if (err) {
console.error("Błąd odczytu danych:", err); console.error("Błąd odczytu danych:", err);
// Jeśli plik nie istnieje, utwórz go z domyślnymi danymi
const defaultData = {}; const defaultData = {};
fs.writeFile(DANE_FILE, JSON.stringify(defaultData, null, 2), err => { fs.writeFile(DANE_FILE, JSON.stringify(defaultData, null, 2), err => {
if (err) { if (err) {
console.error("Błąd tworzenia pliku danych:", err); console.error("Błąd tworzenia pliku danych:", err);
return res.status(500).send("Błąd tworzenia pliku danych."); return res.status(500).send("Błąd tworzenia pliku danych.");
} }
res.json(defaultData); // Zwróć puste dane res.json(defaultData);
}); });
} else { } else {
res.json(JSON.parse(data)); res.json(JSON.parse(data));
@ -69,6 +69,17 @@ app.post("/dane", (req, res) => {
}); });
}); });
// Endpoint do pobierania progów ocen
app.get("/oceny", (req, res) => {
fs.readFile(OCENY_FILE, "utf8", (err, data) => {
if (err) {
console.error("Błąd odczytu progów ocen:", err);
return res.status(500).send("Błąd odczytu progów ocen.");
}
res.json(JSON.parse(data));
});
});
// Endpoint do dodawania nowego kryterium // Endpoint do dodawania nowego kryterium
app.post("/kryteria", (req, res) => { app.post("/kryteria", (req, res) => {
const { nazwa, punkty } = req.body; const { nazwa, punkty } = req.body;
@ -81,7 +92,6 @@ app.post("/kryteria", (req, res) => {
return res.status(500).send("Błąd odczytu kryteriów."); return res.status(500).send("Błąd odczytu kryteriów.");
} }
const kryteria = JSON.parse(data); const kryteria = JSON.parse(data);
// Sprawdź, czy kryterium już istnieje
if (kryteria.some(k => k.nazwa === nazwa)) { if (kryteria.some(k => k.nazwa === nazwa)) {
return res.status(400).send("Kryterium już istnieje."); return res.status(400).send("Kryterium już istnieje.");
} }

View File

@ -1,6 +1,6 @@
{ {
"uczniowie": [ "uczniowie": [
{ "imie": "Jan", "nazwisko": "Kowalski", "klasa": "4i" }, { "imie": "Jan", "nazwisko": "Kowalski", "klasa": "2i" },
{ "imie": "Anna", "nazwisko": "Nowak", "klasa": "3a" }, { "imie": "Anna", "nazwisko": "Nowak", "klasa": "3a" },
{ "imie": "Piotr", "nazwisko": "Wiśniewski", "klasa": "2b" }, { "imie": "Piotr", "nazwisko": "Wiśniewski", "klasa": "2b" },
{ "imie": "Maria", "nazwisko": "Kowalczyk", "klasa": "1c" } { "imie": "Maria", "nazwisko": "Kowalczyk", "klasa": "1c" }

View File

@ -9,16 +9,15 @@
<body> <body>
<div class="container"> <div class="container">
<h1>Zarządzanie Uczniami</h1> <h1>Zarządzanie Uczniami</h1>
<!-- Przycisk "Zapisz dane" -->
<button id="save-button">Zapisz dane</button>
<!-- Zakładki --> <!-- Zakładki -->
<div class="tabs"> <div class="tabs">
<button id="students-tab">Uczniowie</button> <button id="students-tab">Uczniowie</button>
<button id="stats-tab">Statystyka</button>
<button id="criteria-tab">Zarządzanie kryteriami</button> <button id="criteria-tab">Zarządzanie kryteriami</button>
</div> </div>
<!-- Lista uczniów --> <!-- Lista uczniów -->
<div id="students-list"></div> <div id="students-list"></div>
<!-- Statystyki -->
<div id="stats-content" style="display: none;"></div>
<!-- Zarządzanie kryteriami --> <!-- Zarządzanie kryteriami -->
<div id="criteria-content" style="display: none;"> <div id="criteria-content" style="display: none;">
<h2>Lista kryteriów</h2> <h2>Lista kryteriów</h2>
@ -48,8 +47,6 @@
<button type="submit">Usuń</button> <button type="submit">Usuń</button>
</form> </form>
</div> </div>
<!-- Przycisk do zapisywania danych -->
<button id="save-button">Zapisz dane</button>
</div> </div>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>

111
script.js
View File

@ -1,10 +1,8 @@
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const studentsList = document.getElementById("students-list"); const studentsList = document.getElementById("students-list");
const statsContent = document.getElementById("stats-content");
const criteriaContent = document.getElementById("criteria-content"); const criteriaContent = document.getElementById("criteria-content");
const saveButton = document.getElementById("save-button"); const saveButton = document.getElementById("save-button");
const studentsTab = document.getElementById("students-tab"); const studentsTab = document.getElementById("students-tab");
const statsTab = document.getElementById("stats-tab");
const criteriaTab = document.getElementById("criteria-tab"); const criteriaTab = document.getElementById("criteria-tab");
const addCriteriaForm = document.getElementById("add-criteria-form"); const addCriteriaForm = document.getElementById("add-criteria-form");
const editCriteriaForm = document.getElementById("edit-criteria-form"); const editCriteriaForm = document.getElementById("edit-criteria-form");
@ -15,24 +13,25 @@ document.addEventListener("DOMContentLoaded", () => {
let uczniowie = []; let uczniowie = [];
let kryteria = []; let kryteria = [];
let dane = {}; let dane = {};
let progiOcen = [];
// Ładowanie danych z serwera // Ładowanie danych z serwera
Promise.all([ Promise.all([
fetch("http://localhost:4000/uczniowie").then(response => response.json()), fetch("http://localhost:4000/uczniowie").then(response => response.json()),
fetch("http://localhost:4000/kryteria").then(response => response.json()), fetch("http://localhost:4000/kryteria").then(response => response.json()),
fetch("http://localhost:4000/dane").then(response => response.json()) fetch("http://localhost:4000/dane").then(response => response.json()),
fetch("http://localhost:4000/oceny").then(response => response.json())
]) ])
.then(([uczniowieData, kryteriaData, daneData]) => { .then(([uczniowieData, kryteriaData, daneData, ocenyData]) => {
uczniowie = uczniowieData.uczniowie; uczniowie = uczniowieData.uczniowie;
kryteria = kryteriaData; kryteria = kryteriaData;
dane = daneData; dane = daneData;
progiOcen = ocenyData.progi;
console.log("Uczniowie:", uczniowie); console.log("Uczniowie:", uczniowie);
console.log("Kryteria:", kryteria); console.log("Kryteria:", kryteria);
console.log("Dane:", dane); console.log("Dane:", dane);
console.log("Progi ocen:", progiOcen);
renderStudents(); renderStudents();
renderStats();
renderCriteria(); renderCriteria();
}) })
.catch(error => console.error("Błąd ładowania danych:", error)); .catch(error => console.error("Błąd ładowania danych:", error));
@ -43,11 +42,16 @@ document.addEventListener("DOMContentLoaded", () => {
uczniowie.forEach(student => { uczniowie.forEach(student => {
const studentKey = `${student.imie}-${student.nazwisko}`; const studentKey = `${student.imie}-${student.nazwisko}`;
const details = document.createElement("details"); const details = document.createElement("details");
details.style.position = "relative"; // Pozycjonowanie względne dla stats-section
const summary = document.createElement("summary"); const summary = document.createElement("summary");
summary.textContent = `${student.imie} ${student.nazwisko} (${student.klasa})`; summary.textContent = `${student.imie} ${student.nazwisko} (${student.klasa})`;
details.appendChild(summary); details.appendChild(summary);
const criteriaList = document.createElement("div"); const criteriaList = document.createElement("div");
criteriaList.className = "criteria-list"; criteriaList.className = "criteria-list";
let totalPoints = 0;
kryteria.forEach(criteria => { kryteria.forEach(criteria => {
const criteriaItem = document.createElement("div"); const criteriaItem = document.createElement("div");
criteriaItem.className = "criteria-item"; criteriaItem.className = "criteria-item";
@ -56,56 +60,57 @@ document.addEventListener("DOMContentLoaded", () => {
const pointsInfo = document.createElement("span"); const pointsInfo = document.createElement("span");
pointsInfo.className = "points-info"; pointsInfo.className = "points-info";
pointsInfo.textContent = `(${criteria.punkty} pkt)`; pointsInfo.textContent = `(${criteria.punkty} pkt)`;
const inputWrapper = document.createElement("div");
inputWrapper.className = "input-wrapper"; // Nowy wrapper dla inputu
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "number"; input.type = "number";
input.min = "0"; input.min = "0";
const savedStudent = dane[studentKey]; input.value = dane[studentKey]?.[criteria.nazwa] || 0;
input.value = savedStudent?.[criteria.nazwa] || 0;
// Zapisz zmiany lokalnie w pamięci (bez natychmiastowego zapisu)
input.addEventListener("input", () => { input.addEventListener("input", () => {
if (!savedStudent) { if (!dane[studentKey]) {
dane[studentKey] = {}; dane[studentKey] = {};
} }
dane[studentKey][criteria.nazwa] = parseInt(input.value) || 0; dane[studentKey][criteria.nazwa] = parseInt(input.value) || 0;
renderStats();
}); });
inputWrapper.appendChild(input); // Dodaj input do wrappera
criteriaItem.appendChild(label); criteriaItem.appendChild(label);
criteriaItem.appendChild(pointsInfo); criteriaItem.appendChild(pointsInfo);
criteriaItem.appendChild(input); criteriaItem.appendChild(inputWrapper); // Dodaj wrapper z inputem
criteriaList.appendChild(criteriaItem); criteriaList.appendChild(criteriaItem);
});
details.appendChild(criteriaList);
studentsList.appendChild(details);
});
}
// Renderuj statystyki // Oblicz sumę punktów
function renderStats() { const count = dane[studentKey]?.[criteria.nazwa] || 0;
statsContent.innerHTML = "";
uczniowie.forEach(student => {
const studentKey = `${student.imie}-${student.nazwisko}`;
const savedStudent = dane[studentKey];
let totalPoints = 0;
if (savedStudent) {
kryteria.forEach(criteria => {
const count = savedStudent[criteria.nazwa] || 0;
totalPoints += count * criteria.punkty; totalPoints += count * criteria.punkty;
}); });
}
const studentDiv = document.createElement("div"); // Wybierz ocenę na podstawie punktów
studentDiv.className = "stat-item"; const ocena = progiOcen.find(prog => totalPoints >= prog.min)?.ocena || 1;
const name = document.createElement("strong");
name.textContent = `${student.imie} ${student.nazwisko} (${student.klasa})`; // Dodaj sekcję z punktami i oceną w prawym górnym rogu
const points = document.createElement("span"); const statsSection = document.createElement("div");
points.className = "points"; statsSection.className = "stats-section";
points.textContent = `${totalPoints} pkt`;
if (totalPoints > 0) { const statsBox = document.createElement("div");
points.classList.add("positive"); statsBox.className = "stats-box";
} else if (totalPoints < 0) {
points.classList.add("negative"); const totalPointsElement = document.createElement("div");
} totalPointsElement.className = "total-points";
studentDiv.appendChild(name); totalPointsElement.textContent = `Punkty: ${totalPoints}`;
studentDiv.appendChild(points); statsBox.appendChild(totalPointsElement);
statsContent.appendChild(studentDiv);
const gradeElement = document.createElement("div");
gradeElement.className = "grade";
gradeElement.textContent = `Ocena: ${ocena}`;
statsBox.appendChild(gradeElement);
statsSection.appendChild(statsBox);
details.appendChild(statsSection); // Dodaj stats-section do details
details.appendChild(criteriaList);
studentsList.appendChild(details);
}); });
} }
@ -114,7 +119,6 @@ document.addEventListener("DOMContentLoaded", () => {
criteriaListDiv.innerHTML = ""; criteriaListDiv.innerHTML = "";
editCriteriaNameSelect.innerHTML = ""; editCriteriaNameSelect.innerHTML = "";
deleteCriteriaNameSelect.innerHTML = ""; deleteCriteriaNameSelect.innerHTML = "";
if (!Array.isArray(kryteria) || kryteria.length === 0) { if (!Array.isArray(kryteria) || kryteria.length === 0) {
console.warn("Brak kryteriów do wyświetlenia."); console.warn("Brak kryteriów do wyświetlenia.");
const noCriteriaMessage = document.createElement("p"); const noCriteriaMessage = document.createElement("p");
@ -122,7 +126,6 @@ document.addEventListener("DOMContentLoaded", () => {
criteriaListDiv.appendChild(noCriteriaMessage); criteriaListDiv.appendChild(noCriteriaMessage);
return; return;
} }
kryteria.forEach(criteria => { kryteria.forEach(criteria => {
// Wyświetl kryterium na liście // Wyświetl kryterium na liście
const criteriaDiv = document.createElement("div"); const criteriaDiv = document.createElement("div");
@ -146,21 +149,13 @@ document.addEventListener("DOMContentLoaded", () => {
// Przełączanie między zakładkami // Przełączanie między zakładkami
studentsTab.addEventListener("click", () => { studentsTab.addEventListener("click", () => {
studentsList.style.display = "block"; 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"; criteriaContent.style.display = "none";
}); });
criteriaTab.addEventListener("click", () => { criteriaTab.addEventListener("click", () => {
studentsList.style.display = "none"; studentsList.style.display = "none";
statsContent.style.display = "none"; criteriaContent.style.display = "block";
criteriaContent.style.display = "block"; // Pokaż zakładkę "Zarządzanie kryteriami" renderCriteria();
renderCriteria(); // Odśwież listę kryteriów
}); });
// Dodawanie nowego kryterium // Dodawanie nowego kryterium
@ -168,12 +163,10 @@ document.addEventListener("DOMContentLoaded", () => {
event.preventDefault(); event.preventDefault();
const nazwa = document.getElementById("new-criteria-name").value.trim(); const nazwa = document.getElementById("new-criteria-name").value.trim();
const punkty = parseInt(document.getElementById("new-criteria-points").value); const punkty = parseInt(document.getElementById("new-criteria-points").value);
if (!nazwa || isNaN(punkty)) { if (!nazwa || isNaN(punkty)) {
alert("Wprowadź poprawne dane."); alert("Wprowadź poprawne dane.");
return; return;
} }
fetch("http://localhost:4000/kryteria", { fetch("http://localhost:4000/kryteria", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
@ -200,12 +193,10 @@ document.addEventListener("DOMContentLoaded", () => {
const nazwa = editCriteriaNameSelect.value; const nazwa = editCriteriaNameSelect.value;
const nowaNazwa = document.getElementById("edit-criteria-new-name").value.trim(); const nowaNazwa = document.getElementById("edit-criteria-new-name").value.trim();
const punkty = parseInt(document.getElementById("edit-criteria-points").value); const punkty = parseInt(document.getElementById("edit-criteria-points").value);
if (!nowaNazwa || isNaN(punkty)) { if (!nowaNazwa || isNaN(punkty)) {
alert("Wprowadź poprawne dane."); alert("Wprowadź poprawne dane.");
return; return;
} }
fetch(`http://localhost:4000/kryteria/${encodeURIComponent(nazwa)}`, { fetch(`http://localhost:4000/kryteria/${encodeURIComponent(nazwa)}`, {
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
@ -230,7 +221,6 @@ document.addEventListener("DOMContentLoaded", () => {
deleteCriteriaForm.addEventListener("submit", event => { deleteCriteriaForm.addEventListener("submit", event => {
event.preventDefault(); event.preventDefault();
const nazwa = deleteCriteriaNameSelect.value; const nazwa = deleteCriteriaNameSelect.value;
fetch(`http://localhost:4000/kryteria/${encodeURIComponent(nazwa)}`, { fetch(`http://localhost:4000/kryteria/${encodeURIComponent(nazwa)}`, {
method: "DELETE" method: "DELETE"
}) })
@ -259,6 +249,7 @@ document.addEventListener("DOMContentLoaded", () => {
.then(response => { .then(response => {
if (response.ok) { if (response.ok) {
alert("Dane zostały zapisane!"); alert("Dane zostały zapisane!");
renderStudents(); // Odśwież listę uczniów
} else { } else {
alert("Wystąpił błąd podczas zapisywania danych."); alert("Wystąpił błąd podczas zapisywania danych.");
} }

View File

@ -12,6 +12,7 @@ body {
background: #fff; background: #fff;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
position: relative; /* Dodane dla pozycjonowania stats-section */
} }
h1 { h1 {
@ -25,7 +26,8 @@ label {
font-weight: bold; font-weight: bold;
} }
select, input { select,
input {
width: 100%; width: 100%;
padding: 10px; padding: 10px;
margin-top: 5px; margin-top: 5px;
@ -69,7 +71,79 @@ button:hover {
background-color: #0056b3; background-color: #0056b3;
} }
/* Lista kryteriów */ /* Lista uczniów */
#students-list details {
margin-bottom: 15px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f9f9f9;
position: relative; /* Dodane dla pozycjonowania stats-section */
}
#students-list summary {
font-weight: bold;
cursor: pointer;
}
.criteria-list {
margin-top: 10px;
}
.criteria-item {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.criteria-item span {
margin-right: 10px;
}
.points-info {
color: #555;
font-size: 0.9em;
}
input[type="number"] {
width: 60px;
padding: 5px;
margin-left: 10px;
}
/* Sekcja statystyk w prawym górnym rogu */
.stats-section {
position: absolute;
top: 10px;
right: 10px;
background-color: #e6f7ff; /* Lekko turkusowe tło */
border: 1px solid #b3e5fc; /* Cienka obramówka */
border-radius: 5px;
padding: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Delikatny cień */
z-index: 10;
}
.stats-box {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 5px;
}
.total-points {
font-size: 14px;
font-weight: bold;
color: #007bff;
}
.grade {
font-size: 16px;
font-weight: bold;
color: #28a745;
}
/* Zarządzanie kryteriami */
#criteria-list div { #criteria-list div {
margin: 10px 0; margin: 10px 0;
padding: 10px; padding: 10px;
@ -77,16 +151,3 @@ button:hover {
border-radius: 4px; border-radius: 4px;
background-color: #f9f9f9; background-color: #f9f9f9;
} }
/* Formularze */
form {
margin-top: 20px;
}
form label {
margin-top: 10px;
}
form input, form select {
margin-bottom: 10px;
}