Czego się nauczysz?
- Pracy z dniami tygodnia i godzinami
- Walidacji formatu czasu (HH:MM)
- Grupowania danych po kategoriach (dniach)
- Tworzenia widoku kalendarza/harmonogramu
- Sortowania chronologicznego
Stworzysz Prosty planer tygodniowy - aplikację do planowania zajęć i wydarzeń na poszczególne dni tygodnia. System pozwala dodawać wpisy z godziną i opisem, wyświetla je w czytelnym widoku tygodniowym i umożliwia filtrowanie po dniach.
Czego się nauczysz?
W prawdziwej pracy...
Aplikacje do planowania czasu są jednymi z najpopularniejszych - od Google Calendar, przez Outlook, po dedykowane planery produktywności. Umiejętność projektowania systemów z widokiem czasowym, grupowaniem po dniach i sortowaniem jest fundamentem dla każdego programisty aplikacji kalendarzowych i organizacyjnych.
Formularz dodawania wpisu Użytkownik wybiera dzień tygodnia, podaje godzinę i wpisuje opis wydarzenia.
Walidacja danych System sprawdza poprawność wprowadzonych danych - czy dzień jest poprawny, czy godzina ma format HH:MM.
Widok tygodniowy Wpisy są wyświetlane pogrupowane według dni tygodnia, posortowane chronologicznie.
Filtrowanie Użytkownik może wybrać konkretny dzień i zobaczyć tylko jego wpisy.
Przykładowa struktura pliku JSON:
{ "entries": [ { "id": 1, "day": "monday", "time": "08:00", "title": "Matematyka", "description": "Sala 203, przygotować zeszyt", "color": "#3498db", "created_at": "2026-02-10 18:00:00" }, { "id": 2, "day": "monday", "time": "10:30", "title": "Fizyka", "description": "Laboratorium, doświadczenie z elektrycznością", "color": "#e74c3c", "created_at": "2026-02-10 18:05:00" }, { "id": 3, "day": "wednesday", "time": "14:00", "title": "Trening koszykówki", "description": "Hala sportowa", "color": "#27ae60", "created_at": "2026-02-10 18:10:00" }, { "id": 4, "day": "friday", "time": "16:00", "title": "Korepetycje angielski", "description": "Online, link w mailu", "color": "#9b59b6", "created_at": "2026-02-10 18:15:00" } ]}Wymagane funkcje:
Przykładowy scenariusz:
Ocena: 3.0Użytkownik wybiera “Poniedziałek”, wpisuje godzinę “08:00” i opis “Matematyka”. Po zapisie widzi wpis w sekcji “Poniedziałek” na liście tygodniowej.
Wszystko z wariantu A, plus:
htmlspecialchars() przy wyświetlaniuPrzykładowy scenariusz:
Ocena: 4.0-5.0Użytkownik widzi siatkę tygodniową z kolumnami dla każdego dnia. Wpisy są posortowane po godzinie. Może kliknąć “Środa” i zobaczyć tylko wpisy że środy w większym widoku.
Wszystko z wariantu B, plus:
Przykładowy scenariusz:
Ocena: 5.0-6.0Użytkownik dodaje wpis na poniedziałek 10:00 i wybiera kolor zielony. System ostrzega jeśli już ma wpis o tej godzinie. Może edytować wpis klikając na niego i zmieniać godzinę lub opis.
Walidacja danych wejściowych:
$day = $_POST['day'] ?? '';$time = $_POST['time'] ?? '';$title = trim($_POST['title'] ?? '');
$validDays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];if (!in_array($day, $validDays)) { $errors[] = "Wybierz poprawny dzień tygodnia";}
// Walidacja formatu godziny HH:MMif (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $time)) { $errors[] = "Nieprawidłowy format godziny (użyj HH:MM)";}
if (empty($title)) { $errors[] = "Tytuł jest wymagany";}
if (strlen($title) > 100) { $errors[] = "Tytuł może mieć maksymalnie 100 znaków";}Dni tygodnia:
function getDays(): array { return [ 'monday' => ['label' => 'Poniedziałek', 'short' => 'Pon', 'order' => 1], 'tuesday' => ['label' => 'Wtorek', 'short' => 'Wt', 'order' => 2], 'wednesday' => ['label' => 'Środa', 'short' => 'Śr', 'order' => 3], 'thursday' => ['label' => 'Czwartek', 'short' => 'Czw', 'order' => 4], 'friday' => ['label' => 'Piątek', 'short' => 'Pt', 'order' => 5], 'saturday' => ['label' => 'Sobota', 'short' => 'Sob', 'order' => 6], 'sunday' => ['label' => 'Niedziela', 'short' => 'Nd', 'order' => 7], ];}
function getDayLabel(string $day): string { $days = getDays(); return $days[$day]['label'] ?? $day;}Grupowanie wpisów po dniach:
function groupByDay(array $entries): array { $grouped = []; $days = getDays();
// Inicjalizuj wszystkie dni (nawet puste) foreach (array_keys($days) as $day) { $grouped[$day] = []; }
foreach ($entries as $entry) { $day = $entry['day']; $grouped[$day][] = $entry; }
// Sortuj wpisy w każdym dniu po godzinie foreach ($grouped as $day => &$dayEntries) { usort($dayEntries, fn($a, $b) => strcmp($a['time'], $b['time'])); }
return $grouped;}Filtrowanie po dniu:
function filterByDay(array $entries, string $day): array { if (empty($day)) { return $entries; }
return array_filter($entries, fn($e) => $e['day'] === $day);}Sortowanie po godzinie:
function sortByTime(array $entries): array { usort($entries, fn($a, $b) => strcmp($a['time'], $b['time'])); return $entries;}Dodawanie wpisu:
function addEntry(array &$entries, array $data): int { $newId = empty($entries) ? 1 : max(array_column($entries, 'id')) + 1;
$newEntry = [ 'id' => $newId, 'day' => $data['day'], 'time' => $data['time'], 'title' => $data['title'], 'description' => $data['description'] ?? '', 'color' => $data['color'] ?? '#3498db', 'user_id' => $data['user_id'] ?? 1, 'created_at' => date('Y-m-d H:i:s'), ];
$entries[] = $newEntry; return $newId;}Wykrywanie konfliktów (wariant C):
function hasConflict(array $entries, string $day, string $time, ?int $excludeId = null): bool { foreach ($entries as $entry) { if ($entry['day'] === $day && $entry['time'] === $time) { if ($excludeId === null || $entry['id'] !== $excludeId) { return true; } } } return false;}
// Sprawdź czy wpis nakłada się z innym (zakładając 1h na wpis)function hasTimeOverlap(array $entries, string $day, string $time, int $durationMinutes = 60, ?int $excludeId = null): array { $conflicts = []; $newStart = strtotime("2000-01-01 $time"); $newEnd = $newStart + ($durationMinutes * 60);
foreach ($entries as $entry) { if ($entry['day'] !== $day) continue; if ($excludeId !== null && $entry['id'] === $excludeId) continue;
$existingStart = strtotime("2000-01-01 {$entry['time']}"); $existingEnd = $existingStart + ($durationMinutes * 60);
if ($newStart < $existingEnd && $newEnd > $existingStart) { $conflicts[] = $entry; } }
return $conflicts;}Dostępne kolory:
function getColors(): array { return [ '#3498db' => 'Niebieski', '#e74c3c' => 'Czerwony', '#27ae60' => 'Zielony', '#9b59b6' => 'Fioletowy', '#f39c12' => 'Pomarańczowy', '#1abc9c' => 'Turkusowy', '#34495e' => 'Granatowy', '#95a5a6' => 'Szary', ];}Generowanie widoku siatki tygodniowej (HTML):
function renderWeekGrid(array $groupedEntries): string { $days = getDays(); $html = '<div class="week-grid">';
foreach ($days as $dayKey => $dayInfo) { $html .= '<div class="day-column">'; $html .= '<h3>' . htmlspecialchars($dayInfo['label']) . '</h3>';
$entries = $groupedEntries[$dayKey] ?? []; if (empty($entries)) { $html .= '<p class="empty">Brak wpisów</p>'; } else { foreach ($entries as $entry) { $color = htmlspecialchars($entry['color'] ?? '#3498db'); $html .= '<div class="entry" style="border-left: 4px solid ' . $color . '">'; $html .= '<span class="time">' . htmlspecialchars($entry['time']) . '</span>'; $html .= '<span class="title">' . htmlspecialchars($entry['title']) . '</span>'; $html .= '</div>'; } }
$html .= '</div>'; }
$html .= '</div>'; return $html;}Wykorzystaj lekcje!
Cotygodniowe spotkania podczas lekcji to idealny moment, by:
Pracuj iteracyjnie - lepiej mieć działający wariant A niż niedokończony C!