Czego się nauczycie?
- Manipulacji DOM w JavaScript
- Obsługi zdarzeń (click, submit, change)
- Synchronizacji stanu między frontendem a backendem
- Wizualizacji statusów i priorytetów
- Praca zespołowa - podział zadań, współpraca, integracja kodu
Stworzycie Rejestr zadań (ToDo) - aplikację webową do zarządzania listą zadań do wykonania. To klasyczny projekt, który każdy programista powinien wykonać, rozszerzony o priorytety, terminy wykonania i statusy. Aplikacja pozwala dodawać zadania, oznaczać je jako wykonane i organizować swoją pracę.
Czego się nauczycie?
W prawdziwej firmie...
Aplikacje ToDo to podstawa systemów zarządzania projektami. Trello, Asana, Todoist, Microsoft To Do - wszystkie bazują na tych samych koncepcjach. Umiejętność tworzenia interaktywnych list to fundament wielu aplikacji biznesowych.
Umiejętności rynkowe
Nauczycie się synchronizować stan aplikacji między przeglądarką (JavaScript) a serwerem (PHP). To kluczowa koncepcja w każdej nowoczesnej aplikacji webowej - od prostych formularzy po SPA.
Formularz dodawania zadania Użytkownik wpisuje treść zadania, wybiera priorytet (niski/średni/wysoki) i opcjonalnie ustawia termin wykonania. Po kliknięciu “Dodaj” zadanie pojawia się na liście.
Lista zadań z interakcją Każde zadanie można oznaczyć jako wykonane (checkbox lub kliknięcie). Wykonane zadania są przekreślone lub przeniesione na dół listy. JavaScript zapewnia płynną interakcję.
Zapis do pliku JSON Stan wszystkich zadań jest zapisywany do pliku JSON. Po odświeżeniu strony zadania są wczytywane z powrotem - nic nie ginie!
Filtry i statystyki (B+) W wyższych wariantach można filtrować zadania (wszystkie/aktywne/wykonane) i widzieć statystyki (ile zadań do wykonania).
Główny widok ToDo
Zadanie na liście
Filtry (B+)
Edycja/Usuwanie (C)
Przykładowa struktura pliku JSON:
{ "tasks": [ { "id": 1, "text": "Dokończyć projekt PHP", "priority": "high", "status": "open", "due_date": "2026-02-20", "created_at": "2026-02-13 10:30:00", "completed_at": null }, { "id": 2, "text": "Przeczytać dokumentację JavaScript", "priority": "medium", "status": "done", "due_date": null, "created_at": "2026-02-12 14:00:00", "completed_at": "2026-02-13 09:15:00" }, { "id": 3, "text": "Kupić kawę", "priority": "low", "status": "open", "due_date": "2026-02-14", "created_at": "2026-02-13 11:00:00", "completed_at": null } ]}Wymagane funkcjonalności:
Struktura plików:
Wszystko z wariantu A, plus:
htmlspecialchars() na wszystkich danych wyjściowychStruktura plików:
Wszystko z wariantu B, plus:
Zmiana statusu zadania:
<?phpfunction toggleTaskStatus(int $taskId): bool { $data = readJsonFile('data/tasks.json');
foreach ($data['tasks'] as &$task) { if ($task['id'] === $taskId) { if ($task['status'] === 'open') { $task['status'] = 'done'; $task['completed_at'] = date('Y-m-d H:i:s'); } else { $task['status'] = 'open'; $task['completed_at'] = null; } return writeJsonFile('data/tasks.json', $data); } }
return false;}
// Obsługa POSTif ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['task_id'])) { $taskId = (int) $_POST['task_id']; toggleTaskStatus($taskId); header('Location: index.php'); exit;}JavaScript - obsługa checkbox bez przeładowania:
document.querySelectorAll('.task-checkbox').forEach(checkbox => { checkbox.addEventListener('change', async function() { const taskId = this.dataset.taskId; const taskItem = this.closest('.task-item');
try { const formData = new FormData(); formData.append('task_id', taskId);
const response = await fetch('toggle.php', { method: 'POST', body: formData });
if (response.ok) { // Wizualna zmiana taskItem.classList.toggle('task-done'); updateCounter(); } } catch (error) { console.error('Błąd:', error); // Przywróć poprzedni stan checkbox this.checked = !this.checked; } });});
function updateCounter() { const total = document.querySelectorAll('.task-item').length; const done = document.querySelectorAll('.task-item.task-done').length; document.getElementById('counter').textContent = `${done} z ${total} wykonanych`;}Filtrowanie zadań w PHP:
<?phpfunction filterTasks(array $tasks, string $filter): array { switch ($filter) { case 'active': return array_filter($tasks, fn($t) => $t['status'] === 'open'); case 'done': return array_filter($tasks, fn($t) => $t['status'] === 'done'); default: return $tasks; }}
function sortTasks(array $tasks, string $sortBy): array { usort($tasks, function($a, $b) use ($sortBy) { switch ($sortBy) { case 'priority': $order = ['high' => 0, 'medium' => 1, 'low' => 2]; return $order[$a['priority']] - $order[$b['priority']]; case 'due_date': $aDate = $a['due_date'] ?? '9999-12-31'; $bDate = $b['due_date'] ?? '9999-12-31'; return strcmp($aDate, $bDate); default: // created_at return strcmp($b['created_at'], $a['created_at']); } }); return $tasks;}Sprawdzanie przeterminowania:
<?phpfunction isOverdue(array $task): bool { if ($task['status'] === 'done') { return false; } if (empty($task['due_date'])) { return false; } return $task['due_date'] < date('Y-m-d');}
function getDaysUntilDue(array $task): ?int { if (empty($task['due_date'])) { return null; } $due = new DateTime($task['due_date']); $today = new DateTime('today'); $diff = $today->diff($due); return $diff->invert ? -$diff->days : $diff->days;}CSS dla zadań:
.task-item { display: flex; align-items: center; padding: 12px 16px; border-bottom: 1px solid #e5e7eb; transition: background-color 0.2s;}
.task-item:hover { background-color: #f9fafb;}
.task-item.task-done { opacity: 0.6;}
.task-item.task-done .task-text { text-decoration: line-through; color: #9ca3af;}
.task-checkbox { width: 20px; height: 20px; margin-right: 12px; cursor: pointer;}
.task-text { flex: 1; font-size: 1rem;}
.task-priority { padding: 2px 8px; border-radius: 12px; font-size: 0.75em; font-weight: 600; margin-left: 8px;}
.priority-high { background: #fee2e2; color: #dc2626; }.priority-medium { background: #fef3c7; color: #d97706; }.priority-low { background: #e0f2fe; color: #0369a1; }
.task-due { font-size: 0.85em; color: #6b7280; margin-left: 12px;}
.task-due.overdue { color: #dc2626; font-weight: 600;}
.task-counter { text-align: center; padding: 12px; background-color: #f3f4f6; color: #374151; font-weight: 500;}To prawdziwy projekt zespołowy!
Aplikacja ToDo to klasyk, który każdy programista powinien mieć w portfolio! Nauczycie się synchronizować frontend z backendem - umiejętność używana w KAŻDEJ nowoczesnej aplikacji.
Pracujcie iteracyjnie - lepiej mieć działający wariant A niż niedziałający C!