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": {
"bad_frekwencja": 2,
"good_frekwencja": 11,
"dobra_frekwencja": 10,
"aktywnosc": 1,
"sprawdzian": 3
"dobra_frekwencja": 3,
"aktywnosc": 0,
"sprawdzian": 3,
"siema": 15,
"zla_frekwencja": 5,
"konkursy": 0
},
"Anna-Nowak": {
"good_frekwencja": 4
"good_frekwencja": 4,
"dobra_frekwencja": 0,
"aktywnosc": 10
},
"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",
"punkty": -10
},
{
@ -13,7 +12,15 @@
"punkty": 5
},
{
"nazwa": "sprawdzian",
"punkty": -20
"nazwa": "siema",
"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 KRYTERIA_FILE = path.join(__dirname, "kryteria.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
app.use(express.json());
@ -42,14 +43,13 @@ app.get("/dane", (req, res) => {
fs.readFile(DANE_FILE, "utf8", (err, data) => {
if (err) {
console.error("Błąd odczytu danych:", err);
// 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
res.json(defaultData);
});
} else {
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
app.post("/kryteria", (req, res) => {
const { nazwa, punkty } = req.body;
@ -81,7 +92,6 @@ app.post("/kryteria", (req, res) => {
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.");
}

View File

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

View File

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

111
script.js
View File

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

View File

@ -12,6 +12,7 @@ body {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
position: relative; /* Dodane dla pozycjonowania stats-section */
}
h1 {
@ -25,7 +26,8 @@ label {
font-weight: bold;
}
select, input {
select,
input {
width: 100%;
padding: 10px;
margin-top: 5px;
@ -69,24 +71,83 @@ button:hover {
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 {
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;
}