refactor: migrate from drizzle to PocketBase

This commit is contained in:
Norbert Maciaszek
2025-10-27 20:29:49 +01:00
parent 68fb45d6ef
commit 9231e0642c
23 changed files with 240 additions and 1198 deletions

View File

@@ -1,12 +1,11 @@
"use client";
import { FC } from "react";
import { useGlobalStore } from "@/app/store/globalStore";
import { Movie } from "@/types/global";
import { FaFire, FaPlusCircle, FaTrash } from "react-icons/fa";
import Link from "next/link";
import { RxEyeOpen } from "react-icons/rx";
import { MdFavorite } from "react-icons/md";
import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri";
'use client';
import { FC } from 'react';
import { useGlobalStore } from '@/app/store/globalStore';
import { FaFire, FaPlusCircle, FaTrash } from 'react-icons/fa';
import Link from 'next/link';
import { RxEyeOpen } from 'react-icons/rx';
import { MdFavorite } from 'react-icons/md';
import { RiCalendarCheckLine, RiCalendarScheduleLine } from 'react-icons/ri';
type Props = Movie & {
showDayCounter?: boolean;
@@ -27,7 +26,7 @@ export const MovieCard: FC<Props> = ({
const { vote_average, popularity, poster_path, title, overview } = movie;
const { id } = movie;
const alreadyInStore = movies.find((m) => m.id === id);
const alreadyInStore = movies.find(m => m.id === id);
const seen = alreadyInStore?.seen || movie.seen;
const favorite = alreadyInStore?.favorite || movie.favorite;
@@ -35,10 +34,10 @@ export const MovieCard: FC<Props> = ({
const isReleased = new Date(movie.release_date) < new Date();
const scoreColor =
vote_average >= 8
? "from-emerald-400 to-teal-400"
? 'from-emerald-400 to-teal-400'
: vote_average >= 6
? "from-yellow-400 to-orange-400"
: "from-red-400 to-pink-400";
? 'from-yellow-400 to-orange-400'
: 'from-red-400 to-pink-400';
const handleAdd = () => {
addMovieToStore(movie);
@@ -129,8 +128,8 @@ export const MovieCard: FC<Props> = ({
<div
className={`${
seen
? "bg-gradient-to-r from-emerald-500/95 to-emerald-600/90"
: "bg-gradient-to-r from-white/25 to-white/15"
? 'bg-gradient-to-r from-emerald-500/95 to-emerald-600/90'
: 'bg-gradient-to-r from-white/25 to-white/15'
} p-2 rounded-full cursor-pointer hover:bg-emerald-400 transition-colors border border-white/10 shadow-lg`}
onClick={handleSeen}
>
@@ -139,8 +138,8 @@ export const MovieCard: FC<Props> = ({
<div
className={`${
favorite
? "bg-gradient-to-r from-rose-500/95 to-rose-600/90"
: "bg-gradient-to-r from-white/25 to-white/15"
? 'bg-gradient-to-r from-rose-500/95 to-rose-600/90'
: 'bg-gradient-to-r from-white/25 to-white/15'
} p-2 rounded-full cursor-pointer hover:bg-rose-400 transition-colors border border-white/10 shadow-lg`}
onClick={handleFavorite}
>
@@ -185,7 +184,7 @@ export const MovieCard: FC<Props> = ({
<div className="flex items-center gap-3">
<div
className={`flex items-center gap-1 text-sm ${
isReleased ? "text-emerald-400" : "text-amber-400"
isReleased ? 'text-emerald-400' : 'text-amber-400'
}`}
>
{isReleased ? (
@@ -194,10 +193,10 @@ export const MovieCard: FC<Props> = ({
<RiCalendarScheduleLine />
)}
<span className="font-medium">
{releaseDate.toLocaleDateString("pl-PL", {
day: "numeric",
month: "short",
year: "numeric",
{releaseDate.toLocaleDateString('pl-PL', {
day: 'numeric',
month: 'short',
year: 'numeric',
})}
</span>
</div>

View File

@@ -1,11 +1,10 @@
"use client";
import { formatter } from "@/helpers/formater";
import { Movie } from "@/types/global";
import Link from "next/link";
import { FC } from "react";
import { FaCalendar, FaClock, FaStar, FaEye, FaHeart } from "react-icons/fa";
import { motion, useAnimationControls, useMotionValue } from "framer-motion";
import { useGlobalStore } from "@/app/store/globalStore";
'use client';
import { formatter } from '@/helpers/formater';
import Link from 'next/link';
import { FC } from 'react';
import { FaCalendar, FaClock, FaStar, FaEye, FaHeart } from 'react-icons/fa';
import { motion, useAnimationControls, useMotionValue } from 'framer-motion';
import { useGlobalStore } from '@/app/store/globalStore';
type Props = {
movie: Movie;
@@ -31,7 +30,7 @@ export const MovieRow: FC<Props> = ({
);
// Check if movie is already in store.
const movieInStore = movies.find((m) => m.id === movie.id);
const movieInStore = movies.find(m => m.id === movie.id);
const isWatched = movieInStore?.seen || false;
const isFavorite = movieInStore?.favorite || false;
@@ -83,7 +82,7 @@ export const MovieRow: FC<Props> = ({
dragConstraints={{ left: -80, right: 80 }}
dragElastic={0.01}
dragMomentum={false}
whileDrag={{ cursor: "grabbing" }}
whileDrag={{ cursor: 'grabbing' }}
onDragEnd={handleDragAction}
className="relative z-10"
>
@@ -142,8 +141,8 @@ export const MovieRow: FC<Props> = ({
<div
className={`text-xs px-2 py-1 rounded-full font-medium ${
isUpcoming
? "bg-blue-500/20 text-blue-400"
: "bg-green-500/20 text-green-400"
? 'bg-blue-500/20 text-blue-400'
: 'bg-green-500/20 text-green-400'
}`}
>
{isUpcoming

View File

@@ -1,14 +1,13 @@
"use client";
import { FC, ReactNode, useState } from "react";
import { MovieCard } from "@/components/atoms/MovieCard";
import { Movie } from "@/types/global";
import { useGlobalStore } from "@/app/store/globalStore";
import { Dropdown } from "@/components/atoms/Dropdown";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { Button } from "@/components/atoms/Button";
import { Label } from "@/components/atoms/Label";
import { FaList } from "react-icons/fa";
import { MovieRow } from "@/components/atoms/MovieRow";
'use client';
import { FC, ReactNode, useState } from 'react';
import { MovieCard } from '@/components/atoms/MovieCard';
import { useGlobalStore } from '@/app/store/globalStore';
import { Dropdown } from '@/components/atoms/Dropdown';
import { useAutoAnimate } from '@formkit/auto-animate/react';
import { Button } from '@/components/atoms/Button';
import { Label } from '@/components/atoms/Label';
import { FaList } from 'react-icons/fa';
import { MovieRow } from '@/components/atoms/MovieRow';
type Props = {
heading?: string;
@@ -16,7 +15,7 @@ type Props = {
colors?: keyof typeof colorsMap;
overrideMovies?: Movie[];
overrideDisplayType?: "grid" | "list";
overrideDisplayType?: 'grid' | 'list';
showFilters?: boolean;
filterSeen?: 0 | 1;
@@ -26,8 +25,8 @@ type Props = {
fluid?: boolean;
showSorting?: boolean;
sort?: "title" | "releaseDate" | "popularity";
sortDirection?: "asc" | "desc";
sort?: 'title' | 'releaseDate' | 'popularity';
sortDirection?: 'asc' | 'desc';
loadMore?: boolean;
};
@@ -35,7 +34,7 @@ type Props = {
export const MovieList: FC<Props> = ({
heading,
icon,
colors = "white",
colors = 'white',
overrideMovies,
showFilters = true,
filterSeen: filterSeenInitial,
@@ -44,8 +43,8 @@ export const MovieList: FC<Props> = ({
filterReleased: filterReleasedInitial,
fluid = false,
showSorting = true,
sort: sortType = "releaseDate",
sortDirection = "asc",
sort: sortType = 'releaseDate',
sortDirection = 'asc',
loadMore = false,
overrideDisplayType,
}) => {
@@ -63,14 +62,14 @@ export const MovieList: FC<Props> = ({
upcoming: filterUpcomingInitial,
released: filterReleasedInitial,
});
const [sort, setSort] = useState<"title" | "releaseDate" | "popularity">(
const [sort, setSort] = useState<'title' | 'releaseDate' | 'popularity'>(
sortType
);
const [loaded, setLoaded] = useState(8);
const [parent] = useAutoAnimate();
const filteredMovies = movies.filter((movie) => {
const filteredMovies = movies.filter(movie => {
let result = true;
const today = new Date();
if (filter.seen !== undefined) {
@@ -93,16 +92,16 @@ export const MovieList: FC<Props> = ({
});
let sortedMovies = filteredMovies.sort((a, b) => {
if (sort === "title") return a.title.localeCompare(b.title);
if (sort === "releaseDate")
if (sort === 'title') return a.title.localeCompare(b.title);
if (sort === 'releaseDate')
return (
new Date(b.release_date).getTime() - new Date(a.release_date).getTime()
);
if (sort === "popularity") return b.popularity - a.popularity;
if (sort === 'popularity') return b.popularity - a.popularity;
return 0;
});
if (sortDirection === "desc") {
if (sortDirection === 'desc') {
sortedMovies = sortedMovies.reverse();
}
sortedMovies = sortedMovies.slice(0, loadMore ? loaded : movies.length);
@@ -119,7 +118,7 @@ export const MovieList: FC<Props> = ({
return (
<section className="blocks">
<div className={`${fluid ? "max-w-full px-4" : "container"}`}>
<div className={`${fluid ? 'max-w-full px-4' : 'container'}`}>
{heading && (
<div className="flex items-center gap-3">
{icon && (
@@ -151,17 +150,17 @@ export const MovieList: FC<Props> = ({
</Label>
<Label
active={filter.favorites !== undefined}
onClick={() => handleFilter("favorites")}
onClick={() => handleFilter('favorites')}
>
Ulubione ({movies.filter((movie) => movie.favorite).length})
Ulubione ({movies.filter(movie => movie.favorite).length})
</Label>
<Label
active={
filter.seen !== undefined && filter.released === undefined
}
onClick={() => handleFilter("seen")}
onClick={() => handleFilter('seen')}
>
Obejrzane ({movies.filter((movie) => movie.seen).length})
Obejrzane ({movies.filter(movie => movie.seen).length})
</Label>
<Label
active={
@@ -179,7 +178,7 @@ export const MovieList: FC<Props> = ({
Do obejrzenia (
{
movies.filter(
(movie) =>
movie =>
new Date(movie.release_date) < new Date() && !movie.seen
).length
}
@@ -187,12 +186,12 @@ export const MovieList: FC<Props> = ({
</Label>
<Label
active={filter.upcoming !== undefined}
onClick={() => handleFilter("upcoming")}
onClick={() => handleFilter('upcoming')}
>
Nadchodzące (
{
movies.filter(
(movie) => new Date(movie.release_date) > new Date()
movie => new Date(movie.release_date) > new Date()
).length
}
)
@@ -205,7 +204,7 @@ export const MovieList: FC<Props> = ({
size="icon"
theme="slate"
onClick={() =>
setDisplayType(displayType === "grid" ? "list" : "grid")
setDisplayType(displayType === 'grid' ? 'list' : 'grid')
}
>
<FaList />
@@ -213,12 +212,12 @@ export const MovieList: FC<Props> = ({
)}
<Dropdown
items={[
{ label: "Tytuł", value: "title" },
{ label: "Data premiery", value: "releaseDate" },
{ label: "Popularność", value: "popularity" },
{ label: 'Tytuł', value: 'title' },
{ label: 'Data premiery', value: 'releaseDate' },
{ label: 'Popularność', value: 'popularity' },
]}
defaultValue={sort}
callback={(value) => setSort(value as "title")}
callback={value => setSort(value as 'title')}
/>
</div>
)}
@@ -232,8 +231,8 @@ export const MovieList: FC<Props> = ({
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-y-6 gap-3 sm:gap-6 mt-8 justify-center"
ref={parent}
>
{sortedMovies.map((movie) =>
displayType === "grid" ? (
{sortedMovies.map(movie =>
displayType === 'grid' ? (
<MovieCard key={movie.id} {...movie} />
) : (
<MovieRow key={movie.id} movie={movie} compact />
@@ -258,14 +257,14 @@ export const MovieList: FC<Props> = ({
};
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",
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',
};

View File

@@ -3,9 +3,7 @@ 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';

View File

@@ -1,9 +1,9 @@
"use client";
import { SearchResult } from "@/lib/tmdb/types";
import { MovieCard } from "@/components/atoms/MovieCard";
import { FC } from "react";
import { FaStar } from "react-icons/fa";
import { Carousel } from "../Carousel";
'use client';
import { SearchResult } from '@/lib/tmdb/types';
import { MovieCard } from '@/components/atoms/MovieCard';
import { FC } from 'react';
import { FaStar } from 'react-icons/fa';
import { Carousel } from '../Carousel';
type Props = {
movies: SearchResult;
@@ -26,7 +26,7 @@ export const RecommendedMovies: FC<Props> = ({ movies }) => {
new Date(b.release_date).getTime() -
new Date(a.release_date).getTime()
)
.map((movie) => (
.map(movie => (
<MovieCard
key={movie.id}
id={movie.id}
@@ -38,7 +38,7 @@ export const RecommendedMovies: FC<Props> = ({ movies }) => {
popularity={movie.popularity}
adult={movie.adult}
backdrop_path={movie.backdrop_path}
genre_ids={movie.genre_ids.join(",")}
genre_ids={movie.genre_ids.join(',')}
original_language={movie.original_language}
original_title={movie.original_title}
video={movie.video}

View File

@@ -1,9 +1,8 @@
"use client";
import { FC } from "react";
import { useGlobalStore } from "@/app/store/globalStore";
import { FaCalendar, FaClock } from "react-icons/fa";
import { MovieRow } from "@/components/atoms/MovieRow";
import { Movie } from "@/types/global";
'use client';
import { FC } from 'react';
import { useGlobalStore } from '@/app/store/globalStore';
import { FaCalendar, FaClock } from 'react-icons/fa';
import { MovieRow } from '@/components/atoms/MovieRow';
type Props = {
overrideMovies?: Movie[];
@@ -15,8 +14,8 @@ type Props = {
export const TrackedMovies: FC<Props> = ({
overrideMovies,
daysLimit = 30,
labelCurrent = "Aktualnie w kinach",
labelUpcoming = "Nadchodzące premiery",
labelCurrent = 'Aktualnie w kinach',
labelUpcoming = 'Nadchodzące premiery',
}) => {
const { movies: storeMovies } = useGlobalStore();
@@ -27,7 +26,7 @@ export const TrackedMovies: FC<Props> = ({
}
const today = new Date();
const upcoming = movies.filter((movie) => {
const upcoming = movies.filter(movie => {
const daysSinceRelease = Math.abs(
Math.floor(
(new Date().getTime() - new Date(movie.release_date).getTime()) /
@@ -38,7 +37,7 @@ export const TrackedMovies: FC<Props> = ({
new Date(movie.release_date) > today && daysSinceRelease <= daysLimit
);
});
const inCinema = movies.filter((movie) => {
const inCinema = movies.filter(movie => {
const daysSinceRelease = Math.floor(
(new Date().getTime() - new Date(movie.release_date).getTime()) /
(1000 * 60 * 60 * 24)
@@ -73,7 +72,7 @@ export const TrackedMovies: FC<Props> = ({
{labelCurrent} ({sortedInCinema.length})
</h3>
<div className="space-y-2">
{sortedInCinema.map((movie) => (
{sortedInCinema.map(movie => (
<MovieRow key={movie.id} movie={movie} isUpcoming={false} />
))}
</div>
@@ -87,7 +86,7 @@ export const TrackedMovies: FC<Props> = ({
{labelUpcoming} ({sortedUpcoming.length})
</h3>
<div className="space-y-2">
{sortedUpcoming.map((movie) => (
{sortedUpcoming.map(movie => (
<MovieRow key={movie.id} movie={movie} isUpcoming />
))}
</div>

View File

@@ -1,17 +1,16 @@
"use client";
import { FC, useState, useEffect, useCallback } from "react";
import { Movie } from "@/types/global";
'use client';
import { FC, useState, useEffect, useCallback } from 'react';
import {
FaPlus,
FaFire,
FaChevronLeft,
FaChevronRight,
FaMinus,
} from "react-icons/fa";
import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri";
import { useGlobalStore } from "@/app/store/globalStore";
import Link from "next/link";
import { Button } from "@/components/atoms/Button";
} from 'react-icons/fa';
import { RiCalendarCheckLine, RiCalendarScheduleLine } from 'react-icons/ri';
import { useGlobalStore } from '@/app/store/globalStore';
import Link from 'next/link';
import { Button } from '@/components/atoms/Button';
type Props = {
movies: Movie[];
@@ -50,7 +49,7 @@ export const Hero: FC<Props> = ({
vote_average,
} = currentMovie;
const alreadyInStore = storedMovies.find((m) => m.id === id);
const alreadyInStore = storedMovies.find(m => m.id === id);
const isReleased = new Date(release_date) < new Date();
const releaseDate = new Date(release_date);
@@ -58,7 +57,7 @@ export const Hero: FC<Props> = ({
if (isTransitioning) return;
setIsTransitioning(true);
setTimeout(() => {
setCurrentIndex((prev) => (prev + 1) % movies.length);
setCurrentIndex(prev => (prev + 1) % movies.length);
setIsTransitioning(false);
}, 500);
}, [movies.length, isTransitioning]);
@@ -67,7 +66,7 @@ export const Hero: FC<Props> = ({
if (isTransitioning) return;
setIsTransitioning(true);
setTimeout(() => {
setCurrentIndex((prev) => (prev - 1 + movies.length) % movies.length);
setCurrentIndex(prev => (prev - 1 + movies.length) % movies.length);
setIsTransitioning(false);
}, 500);
}, [movies.length, isTransitioning]);
@@ -108,7 +107,7 @@ export const Hero: FC<Props> = ({
<div
key={movie.id}
className={`absolute inset-0 transition-opacity duration-500 ${
index === currentIndex ? "opacity-100" : "opacity-0"
index === currentIndex ? 'opacity-100' : 'opacity-0'
}`}
>
<img
@@ -146,7 +145,7 @@ export const Hero: FC<Props> = ({
<div className="container relative z-10">
<div
className={`flex flex-col lg:flex-row items-center gap-8 lg:gap-12 transition-opacity duration-500 ${
isTransitioning ? "opacity-0" : "opacity-100"
isTransitioning ? 'opacity-0' : 'opacity-100'
}`}
>
{/* Poster */}
@@ -176,7 +175,7 @@ export const Hero: FC<Props> = ({
<div className="flex items-center gap-2">
<span
className={`flex items-center gap-1 text-sm ${
isReleased ? "text-green-400" : "text-yellow-400"
isReleased ? 'text-green-400' : 'text-yellow-400'
}`}
>
{isReleased ? (
@@ -184,10 +183,10 @@ export const Hero: FC<Props> = ({
) : (
<RiCalendarScheduleLine />
)}
{releaseDate.toLocaleDateString("pl-PL", {
day: "numeric",
month: "long",
year: "numeric",
{releaseDate.toLocaleDateString('pl-PL', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</span>
</div>
@@ -211,12 +210,12 @@ export const Hero: FC<Props> = ({
{/* Action buttons */}
<div className="flex flex-col sm:flex-row gap-4 justify-center lg:justify-start">
<Button
theme={alreadyInStore ? "primary" : "secondary"}
theme={alreadyInStore ? 'primary' : 'secondary'}
onClick={alreadyInStore ? handleRemove : handleAdd}
>
{alreadyInStore ? <FaMinus /> : <FaPlus />}
<span>
{alreadyInStore ? "Usuń z listy" : "Dodaj do listy"}
{alreadyInStore ? 'Usuń z listy' : 'Dodaj do listy'}
</span>
</Button>
</div>
@@ -234,8 +233,8 @@ export const Hero: FC<Props> = ({
disabled={isTransitioning}
className={`w-3 h-3 rounded-full transition-all duration-300 disabled:cursor-not-allowed cursor-pointer ${
index === currentIndex
? "bg-secondary scale-125"
: "bg-white/50 hover:bg-secondary/70"
? 'bg-secondary scale-125'
: 'bg-white/50 hover:bg-secondary/70'
}`}
/>
))}