Przejdź do głównej zawartości

Rejestr zadań (ToDo) - PHP + JS

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?

  • 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

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.

  1. 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.

  2. 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ę.

  3. 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!

  4. 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

  • Formularz dodawania zadania
  • Lista wszystkich zadań
  • Checkbox do oznaczania
  • Licznik zadań

Zadanie na liście

  • Treść zadania
  • Priorytet (kolorowy chip)
  • Termin (jeśli ustawiony)
  • Checkbox status

Filtry (B+)

  • Wszystkie / Aktywne / Wykonane
  • Sortowanie po priorytecie
  • Sortowanie po terminie

Edycja/Usuwanie (C)

  • Edycja treści zadania
  • Przycisk usuń
  • Potwierdzenie usunięcia

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:

  • Formularz zadania: treść, priorytet, opcjonalny termin
  • Walidacja PHP wszystkich pól
  • Minimum 1 walidacja JavaScript (np. niepusta treść)
  • Zapis zadań do pliku JSON
  • Generowanie unikalnego ID
  • Lista zadań z odczytem z JSON
  • Oznaczanie zadania jako wykonane (zmiana statusu)
  • Wizualne odróżnienie wykonanych zadań (przekreślenie)
  • Wyświetlanie komunikatów o błędach

Struktura plików:

  • Folderprojekt/
    • index.php (główny widok)
    • add.php (obsługa POST dodawania)
    • toggle.php (obsługa zmiany statusu)
    • README.md
    • Folderdata/
      • tasks.json
    • Foldercss/
      • style.css
    • Folderjs/
      • app.js
Ocena: 3.0
  1. Użytkownik otwiera aplikację ToDo
  2. Wpisuje “Dokończyć projekt PHP”
  3. Wybiera priorytet “Wysoki”
  4. Ustawia termin na piątek
  5. Klika “Dodaj zadanie”
  6. Zadanie pojawia się na liście z czerwonym oznaczeniem priorytetu
  7. Po wykonaniu użytkownik klika checkbox
  8. Zadanie zostaje przekreślone i przesuwa się na dół
  1. Użytkownik ma na liście 10 zadań
  2. Chcę zobaczyć tylko te do wykonania
  3. Klika filtr “Aktywne”
  4. Lista pokazuje tylko 6 niewykonanych zadań
  5. Widzi licznik “6 zadań do wykonania”
  6. Sortuje po priorytecie - najważniejsze na górze
  1. Użytkownik zauważa literówkę w zadaniu
  2. Klika przycisk edycji przy zadaniu
  3. Pole tekstowe staje się edytowalne
  4. Poprawia treść i klika “Zapisz”
  5. Zmiany są zapisane do JSON
  6. Strona nie przeładowuje się (JS)

Zmiana statusu zadania:

toggle.php
<?php
function 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 POST
if ($_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:

<?php
function 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:

<?php
function 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!