Przejdź do głównej zawartości

Prosty planer tygodniowy

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?

  • Pracy z dniami tygodnia i godzinami
  • Walidacji formatu czasu (HH:MM)
  • Grupowania danych po kategoriach (dniach)
  • Tworzenia widoku kalendarza/harmonogramu
  • Sortowania chronologicznego

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.

  1. Formularz dodawania wpisu Użytkownik wybiera dzień tygodnia, podaje godzinę i wpisuje opis wydarzenia.

  2. Walidacja danych System sprawdza poprawność wprowadzonych danych - czy dzień jest poprawny, czy godzina ma format HH:MM.

  3. Widok tygodniowy Wpisy są wyświetlane pogrupowane według dni tygodnia, posortowane chronologicznie.

  4. 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"
}
]
}
  • Folderplaner-tygodniowy/
    • index.php (widok tygodniowy)
    • dodaj.php (formularz dodawania)
    • dzień.php (widok pojedynczego dnia)
    • edytuj.php (edycja wpisu - wariant C)
    • usun.php (usuwanie wpisu - wariant C)
    • Folderincludes/
      • config.php
      • functions.php
      • auth.php (wariant C)
    • Folderdata/
      • entries.json
      • users.json (wariant C)
    • Foldercss/
      • style.css
      • calendar.css
    • Folderjs/
      • validation.js

Wymagane funkcje:

  • Formularz dodawania: dzień (select), godzina (input time), opis
  • Walidacja PHP (dzień z listy, godzina w formacie HH:MM)
  • Min. 1 walidacja JavaScript
  • Zapis wpisów do pliku JSON
  • Lista wpisów pogrupowana po dniach
  • Komunikaty błędów i sukcesu

Przykładowy scenariusz:

Użytkownik wybiera “Poniedziałek”, wpisuje godzinę “08:00” i opis “Matematyka”. Po zapisie widzi wpis w sekcji “Poniedziałek” na liście tygodniowej.

Ocena: 3.0

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:MM
if (!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:

  • Pokazać postępy - nawet małe kroki się liczą
  • Wyjaśnić wątpliwości - pytaj, nie zgaduj
  • Skonsultować rozwiązania - feedback pomoże Ci się rozwijać

Pracuj iteracyjnie - lepiej mieć działający wariant A niż niedokończony C!