Przejdź do głównej zawartości

Rejestr projektów uczniowskich

Stworzysz Rejestr projektów uczniowskich - katalog prezentujący projekty uczniów z opisami, użytymi technologiami i linkami do repozytoriów. System pozwala dodawać nowe projekty, przeglądać istniejące i filtrować po technologiach.

Czego się nauczysz?

  • Walidacji URL-i (linków do GitHub/GitLab)
  • Pracy z tablicami (lista technologii)
  • Implementacji filtrowania i wyszukiwania
  • Tworzenia katalogów i portfoliów
  • Wyświetlania kart projektów

W prawdziwej pracy...

Katalogi i portfolia są podstawą prezentacji prac - od repozytoriów open source, przez portfolio freelancerów, po wewnętrzne katalogi projektów firmowych. Umiejętność projektowania systemów katalogowych z tagami, filtrowaniem i walidacją URL jest fundamentem dla każdego programisty aplikacji webowych.

  1. Formularz dodawania projektu Użytkownik podaje tytuł, opis, listę technologii i link do repozytorium.

  2. Walidacja danych System sprawdza poprawność wprowadzonych danych - czy tytuł nie jest pusty, czy URL jest prawidłowy.

  3. Katalog projektów Wszystkie projekty są wyświetlane jako karty z miniaturami informacji.

  4. Szczegóły projektu Użytkownik może zobaczyć pełny opis projektu i przejść do repozytorium.

Przykładowa struktura pliku JSON:

{
"projects": [
{
"id": 1,
"title": "System rezerwacji sal",
"description": "Aplikacja webowa do rezerwowania sal konferencyjnych z kalendarzem i powiadomieniami email.",
"technologies": ["PHP", "MySQL", "JavaScript", "Bootstrap"],
"url": "https://github.com/jankowalski/room-booking",
"author": "Jan Kowalski",
"class": "4TI",
"status": "completed",
"created_at": "2026-02-10 14:30:00"
},
{
"id": 2,
"title": "Aplikacja pogodowa",
"description": "Mobilna aplikacja do sprawdzania pogody z wykorzystaniem API OpenWeatherMap.",
"technologies": ["React Native", "JavaScript", "REST API"],
"url": "https://github.com/annanowak/weather-app",
"author": "Anna Nowak",
"class": "3TI",
"status": "in_progress",
"created_at": "2026-02-08 10:15:00"
},
{
"id": 3,
"title": "Sklep internetowy",
"description": "E-commerce z koszykiem, płatnościami i panelem administracyjnym.",
"technologies": ["PHP", "MySQL", "CSS", "PayU API"],
"url": "https://gitlab.com/piotrw/shop",
"author": "Piotr Wiśniewski",
"class": "4TI",
"status": "completed",
"created_at": "2026-01-20 16:45:00"
}
]
}
  • Folderrejestr-projektow/
    • index.php (katalog projektów)
    • dodaj.php (formularz dodawania)
    • projekt.php (szczegóły projektu)
    • edytuj.php (edycja - wariant C)
    • Folderincludes/
      • config.php
      • functions.php
      • validation.php
      • auth.php (wariant C)
    • Folderdata/
      • projects.json
      • technologies.json
      • users.json (wariant C)
    • Foldercss/
      • style.css
    • Folderjs/
      • validation.js
      • filter.js

Wymagane funkcje:

  • Formularz: tytuł, opis, technologie (checkboxy lub input), URL
  • Walidacja PHP (tytuł wymagany, URL format)
  • Min. 1 walidacja JavaScript
  • Zapis projektów do pliku JSON
  • Lista projektów + szczegóły
  • Komunikaty błędów i sukcesu

Przykładowy scenariusz:

Użytkownik wpisuje tytuł “Moja aplikacja”, opis, zaznacza technologie PHP i JavaScript, podaje link do GitHub. Po zapisie widzi swój projekt w katalogu jako kartę z listą technologii.

Ocena: 3.0

Walidacja projektu:

$title = trim($_POST['title'] ?? '');
$description = trim($_POST['description'] ?? '');
$technologies = $_POST['technologies'] ?? [];
$url = trim($_POST['url'] ?? '');
if (empty($title)) {
$errors[] = "Tytuł projektu jest wymagany";
}
if (strlen($title) > 100) {
$errors[] = "Tytuł może mieć maksymalnie 100 znaków";
}
if (strlen($description) < 20) {
$errors[] = "Opis powinien mieć co najmniej 20 znaków";
}
if (strlen($description) > 1000) {
$errors[] = "Opis może mieć maksymalnie 1000 znaków";
}
if (empty($technologies)) {
$errors[] = "Wybierz co najmniej jedną technologię";
}
// Walidacja technologii z dozwolonej listy
$validTech = getAvailableTechnologies();
foreach ($technologies as $tech) {
if (!in_array($tech, $validTech)) {
$errors[] = "Nieprawidłowa technologia: " . htmlspecialchars($tech);
}
}
if (!empty($url) && !filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = "Podaj prawidłowy adres URL";
}
// Sprawdź czy URL to GitHub/GitLab (opcjonalnie)
if (!empty($url) && !preg_match('/^https?:\/\/(github\.com|gitlab\.com|bitbucket\.org)/', $url)) {
$warnings[] = "Zalecamy linki do GitHub, GitLab lub Bitbucket";
}

Dostępne technologie:

function getAvailableTechnologies(): array {
return [
'PHP', 'JavaScript', 'TypeScript', 'Python', 'Java', 'C#',
'HTML', 'CSS', 'SCSS', 'Bootstrap', 'Tailwind',
'React', 'Vue', 'Angular', 'Node.js', 'Express',
'MySQL', 'PostgreSQL', 'MongoDB', 'SQLite',
'REST API', 'GraphQL', 'Docker', 'Git',
];
}
function getTechnologyInfo(string $tech): array {
$techInfo = [
'PHP' => ['color' => '#777BB4', 'icon' => '🐘'],
'JavaScript' => ['color' => '#F7DF1E', 'icon' => ''],
'TypeScript' => ['color' => '#3178C6', 'icon' => '📘'],
'Python' => ['color' => '#3776AB', 'icon' => '🐍'],
'React' => ['color' => '#61DAFB', 'icon' => '⚛️'],
'Vue' => ['color' => '#4FC08D', 'icon' => '💚'],
'MySQL' => ['color' => '#4479A1', 'icon' => '🗄️'],
'Node.js' => ['color' => '#339933', 'icon' => '🟢'],
];
return $techInfo[$tech] ?? ['color' => '#6c757d', 'icon' => '🔧'];
}

Dodawanie projektu:

function addProject(array &$projects, array $data): int {
$newId = empty($projects) ? 1 : max(array_column($projects, 'id')) + 1;
$newProject = [
'id' => $newId,
'title' => $data['title'],
'description' => $data['description'],
'technologies' => $data['technologies'],
'url' => $data['url'],
'author' => $data['author'] ?? 'Anonim',
'class' => $data['class'] ?? '',
'status' => $data['status'] ?? 'in_progress',
'created_at' => date('Y-m-d H:i:s'),
];
$projects[] = $newProject;
return $newId;
}

Filtrowanie po technologii:

function filterByTechnology(array $projects, string $technology): array {
if (empty($technology)) {
return $projects;
}
return array_filter($projects, function($project) use ($technology) {
return in_array($technology, $project['technologies']);
});
}
function filterByMultipleTechnologies(array $projects, array $technologies): array {
if (empty($technologies)) {
return $projects;
}
return array_filter($projects, function($project) use ($technologies) {
// Projekt musi zawierać wszystkie wybrane technologie
return count(array_intersect($technologies, $project['technologies'])) === count($technologies);
});
}
function filterByAnyTechnology(array $projects, array $technologies): array {
if (empty($technologies)) {
return $projects;
}
return array_filter($projects, function($project) use ($technologies) {
// Projekt musi zawierać co najmniej jedną z wybranych technologii
return !empty(array_intersect($technologies, $project['technologies']));
});
}

Sortowanie projektów:

function sortProjects(array $projects, string $sortBy = 'created_at', string $order = 'desc'): array {
usort($projects, function($a, $b) use ($sortBy, $order) {
if ($sortBy === 'title') {
$result = strcasecmp($a['title'], $b['title']);
} else {
$result = strcmp($a[$sortBy] ?? '', $b[$sortBy] ?? '');
}
return $order === 'desc' ? -$result : $result;
});
return $projects;
}

Wyszukiwanie pełnotekstowe:

function searchProjects(array $projects, string $query): array {
if (empty($query)) {
return $projects;
}
$query = mb_strtolower($query);
return array_filter($projects, function($project) use ($query) {
$title = mb_strtolower($project['title']);
$description = mb_strtolower($project['description']);
$technologies = mb_strtolower(implode(' ', $project['technologies']));
$author = mb_strtolower($project['author'] ?? '');
return strpos($title, $query) !== false ||
strpos($description, $query) !== false ||
strpos($technologies, $query) !== false ||
strpos($author, $query) !== false;
});
}

Wyświetlanie karty projektu:

function renderProjectCard(array $project): string {
$title = htmlspecialchars($project['title']);
$description = htmlspecialchars(mb_substr($project['description'], 0, 150) . '...');
$author = htmlspecialchars($project['author'] ?? 'Anonim');
$url = htmlspecialchars($project['url'] ?? '#');
$status = $project['status'] === 'completed' ? 'Ukończony' : 'W trakcie';
$statusClass = $project['status'] === 'completed' ? 'completed' : 'in-progress';
$techBadges = '';
foreach ($project['technologies'] as $tech) {
$info = getTechnologyInfo($tech);
$techBadges .= sprintf(
'<span class="tech-badge" style="background: %s">%s %s</span>',
$info['color'],
$info['icon'],
htmlspecialchars($tech)
);
}
return <<<HTML
<div class="project-card">
<div class="card-header">
<h3 class="project-title">{$title}</h3>
<span class="status-badge {$statusClass}">{$status}</span>
</div>
<p class="project-description">{$description}</p>
<div class="technologies">{$techBadges}</div>
<div class="card-footer">
<span class="author">👤 {$author}</span>
<a href="{$url}" target="_blank" class="repo-link">📦 Repozytorium</a>
</div>
</div>
HTML;
}

JavaScript - filtrowanie na żywo:

function setupTechFilter() {
const checkboxes = document.querySelectorAll('.tech-filter');
const projects = document.querySelectorAll('.project-card');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', filterProjects);
});
function filterProjects() {
const selected = Array.from(checkboxes)
.filter(cb => cb.checked)
.map(cb => cb.value);
projects.forEach(project => {
const projectTech = project.dataset.technologies.split(',');
if (selected.length === 0) {
project.style.display = 'block';
} else {
const hasMatch = selected.some(tech => projectTech.includes(tech));
project.style.display = hasMatch ? 'block' : 'none';
}
});
}
}
// Wyszukiwarka
function setupSearch() {
const searchInput = document.getElementById('search');
const projects = document.querySelectorAll('.project-card');
searchInput.addEventListener('input', function() {
const query = this.value.toLowerCase();
projects.forEach(project => {
const title = project.querySelector('.project-title').textContent.toLowerCase();
const desc = project.querySelector('.project-description').textContent.toLowerCase();
project.style.display = (title.includes(query) || desc.includes(query)) ? 'block' : 'none';
});
});
}

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!