feat: add RandomMovie component for enhanced user experience; implement movie filtering and random selection functionality, and update page layout to include new component

This commit is contained in:
Norbert Maciaszek 2025-10-25 23:02:25 +02:00
parent af4689d726
commit 68fb45d6ef
4 changed files with 195 additions and 32 deletions

9
.prettierrc Normal file
View File

@ -0,0 +1,9 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "avoid"
}

View File

@ -1,12 +1,14 @@
import { GenreList } from "@/components/molecules/GenreList";
import { MovieList } from "@/components/molecules/MovieList";
import { TrackedMovies } from "@/components/molecules/TrackedMovies";
import { GenreList } from '@/components/molecules/GenreList';
import { MovieList } from '@/components/molecules/MovieList';
import { RandomMovie } from '@/components/molecules/RandomMovie';
import { TrackedMovies } from '@/components/molecules/TrackedMovies';
export default async function Home() {
return (
<>
<TrackedMovies />
<MovieList heading="Moja lista" />
<RandomMovie heading="Ciężko wybrać?" />
<GenreList heading="Odkrywaj nowe filmy według gatunku" />
</>
);

View File

@ -0,0 +1,137 @@
'use client';
import { FC, useMemo, useState } from 'react';
import { useGlobalStore } from '@/app/store/globalStore';
import { Button } from '@/components/atoms/Button';
import { FaDice } from 'react-icons/fa';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { Movie } from '@/types/global';
type StoreFilter = 'all' | 'not_seen' | 'released' | 'favorites' | 'to_watch';
type Props = {
heading?: string;
storeFilter?: StoreFilter;
colors?: keyof typeof colorsMap;
className?: string;
};
export const RandomMovie: FC<Props> = ({
heading = 'Losowy film',
storeFilter = 'not_seen',
colors = 'purple',
className = '',
}) => {
const { movies } = useGlobalStore();
const [selectedMovie, setSelectedMovie] = useState<Movie | null>(null);
// Filter movies based on the selected store filter.
const filteredMovies = useMemo(() => {
const today = new Date();
return movies.filter(movie => {
switch (storeFilter) {
case 'not_seen':
return !movie.seen;
case 'released':
return new Date(movie.release_date) < today;
case 'favorites':
return movie.favorite;
case 'to_watch':
return !movie.seen && new Date(movie.release_date) < today;
case 'all':
default:
return true;
}
});
}, [movies, storeFilter]);
const handleRandomize = () => {
if (filteredMovies.length === 0) return;
const randomIndex = Math.floor(Math.random() * filteredMovies.length);
const randomMovie = filteredMovies[randomIndex];
setSelectedMovie(randomMovie);
};
if (filteredMovies.length === 0) {
return (
<section className={`blocks ${className}`}>
<div className="container">
{heading && (
<div className="flex items-center gap-3 mb-6">
<div className={`p-2 rounded-lg ${colorsMap[colors]}`}>
<FaDice className="text-white" />
</div>
<h2
className={`text-3xl font-bold ${colorsMap[colors]} bg-clip-text text-transparent`}
>
{heading}
</h2>
</div>
)}
<div className="text-center py-12">
<p className="text-text/60 text-lg">Brak filmów w kategorii</p>
</div>
</div>
</section>
);
}
return (
<section className={`blocks ${className}`}>
<div className="container">
{heading && (
<div className="flex justify-center mb-6">
<h2
className={`text-3xl font-bold ${colorsMap[colors]} bg-clip-text text-transparent`}
>
{heading}
</h2>
</div>
)}
<div className="text-center mb-4">
<p className="text-text/70 text-sm">
Dostępnych {filteredMovies.length} filmów
</p>
</div>
<div className="flex justify-center">
<Button
size="small"
theme="secondary"
onClick={handleRandomize}
className="flex items-center gap-2"
>
<FaDice />
Losuj film
</Button>
</div>
{selectedMovie && (
<div className="text-center mt-4">
<h3 className="text-2xl font-bold">
<Link href={`/film/${selectedMovie.id}`}>
{selectedMovie.title}
</Link>
</h3>
</div>
)}
</div>
</section>
);
};
const colorsMap = {
white: 'bg-gradient-to-r from-white to-gray-300',
yellow: 'bg-gradient-to-r from-yellow-400 to-orange-400',
blue: 'bg-gradient-to-r from-blue-400 to-purple-400',
green: 'bg-gradient-to-r from-green-400 to-teal-400',
red: 'bg-gradient-to-r from-red-400 to-pink-400',
purple: 'bg-gradient-to-r from-purple-400 to-pink-400',
orange: 'bg-gradient-to-r from-orange-400 to-yellow-400',
pink: 'bg-gradient-to-r from-pink-400 to-purple-400',
teal: 'bg-gradient-to-r from-teal-400 to-green-400',
gray: 'bg-gradient-to-r from-gray-400 to-gray-400',
};

73
todo.md
View File

@ -1,36 +1,51 @@
#
UI/UX Improvements
Dark/Light Mode Toggle - Obecnie tylko ciemny motyw
Responsywny design na urządzenia mobilne - Niektóre komponenty mogą wymagać poprawy
Loading states - Dodać skeletony zamiast spinnerów
Infinite scroll - Zamiast paginacji dla lepszego UX
Gesture support - Swipe na mobilnych dla akcji (dodaj/usuń film)
## ✅ `TODO.md` Etapy rozwoju aplikacji
Zarządzanie filmami
Własne notatki do filmów - Pole w bazie danych już wspomniane w README
Tagi/kategorie użytkownika - Własne etykiety
Oceny użytkownika - Osobne od TMDB
Data obejrzenia - Kiedy użytkownik obejrzał film
Lista "Do obejrzenia" - Oddzielna od "Obejrzane"
Planowanie seansów - Kalendarz z datami
Eksport/import listy - JSON/CSV backup
```md
# TODO MovieBox
Funkcje społecznościowe
Udostępnianie list - Link do publicznej listy
Rekomendacje na podstawie gustu - ML/AI sugestie
Porównanie list z znajomymi - Wspólne filmy
## 🔧 Faza 1 MVP (funkcjonalna wersja lokalna)
Dodatkowe dane i integracje
Informacje o aktorach - Rozszerzone profile (już częściowo jest)
Gdzie obejrzeć - Streaming platforms API
Zwiastuny - YouTube API integration
Recenzje użytkowników - Własne mini-forum
Galeria zdjęć z filmu - Więcej materiałów wizualnych
- [ ] Integracja z TMDB API (wyszukiwanie filmów)
- [ ] Utworzenie bazy danych (SQLite + Drizzle)
- [ ] Modele: Movie, WatchlistEntry
- [ ] Dodanie filmu do watchlisty (z podglądem szczegółów)
- [ ] Lista “Do obejrzenia” i “Obejrzane”
- [ ] Możliwość dodania tagu lub notatki do filmu
- [ ] UI (Tailwind + ShadCN) responsywna siatka filmów
Performance i techniczne
PWA - Offline support, push notifications o premierach
Lepsze caching - Redis/SWR optimizations
Lazy loading - Obrazy i komponenty
Search indexing - Full-text search w bazie
API rate limiting - Lepsze zarządzanie requestami do TMDB
## 🌐 Faza 2 Rozszerzenie
Statystyki i analytics
Dashboard statystyk - Filmy obejrzane/miesiąc, ulubione gatunki
Streak tracking - Dni z rzędu oglądania filmów
Cele filmowe - X filmów do obejrzenia w roku
Porównanie z poprzednimi latami - Trendy
- [ ] Podgląd dat premier z TMDB
- [ ] Filtrowanie według daty premiery
- [ ] Sortowanie / filtrowanie po tagach/statusie
Powiadomienia
Email notifications - O premierach z listy
Push notifications - PWA alerts
Reminder system - Przypomnienia o filmach do obejrzenia
## 🔐 Faza 3 Rozszerzenia prywatne
- [ ] Dodanie Auth.js (logowanie)
- [ ] Migracja bazy do PostgreSQL
- [ ] Eksport listy filmów (np. JSON)
- [ ] Backup na GitHub (np. GitHub Actions)
## 💡 Pomysły na później
- [ ] System rekomendacji (podobne filmy)
- [ ] Powiadomienia o premierach
- [ ] Integracja z Letterboxd
```
Baza danych i backend
Migracja na PostgreSQL - Jak wspomniano w README
User authentication - Currently brak systemu użytkowników
API endpoints - Własne REST API
Backup system - Automatyczne kopie zapasowe