Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9399b55714 | ||
![]() |
85b9141087 | ||
![]() |
7595d1bd8f | ||
![]() |
c5388b5690 | ||
![]() |
5a15a8d5ac | ||
![]() |
1c65005b0a |
@ -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
|
||||
}
|
||||
}
|
@ -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
9
backend/oceny.json
Normal 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 }
|
||||
]
|
||||
}
|
@ -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.");
|
||||
}
|
||||
|
@ -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" }
|
||||
|
@ -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
111
script.js
@ -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.");
|
||||
}
|
||||
|
91
style.css
91
style.css
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user