feat: titles page
Some checks failed
Build and Deploy Go App / build (push) Failing after 11m8s
Build and Deploy Go App / deploy (push) Has been skipped

This commit is contained in:
nihonium 2025-11-18 05:15:38 +03:00
parent 6836cfa057
commit b976c35b8e
Signed by: nihonium
GPG key ID: 0251623741027CFC
44 changed files with 1539 additions and 107 deletions

View file

@ -0,0 +1,59 @@
.container {
padding: 24px;
}
.header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
}
.searchInput {
padding: 8px;
width: 240px;
}
.list {
display: grid;
gap: 12px;
}
.card {
display: flex;
padding: 10px;
border: 1px solid #ddd;
border-radius: 8px;
gap: 12px;
}
.poster {
width: 80px;
height: 120px;
object-fit: cover;
border-radius: 4px;
}
.posterPlaceholder {
width: 80px;
height: 120px;
background: #eee;
display: flex;
align-items: center;
justify-content: center;
}
.cardInfo {
display: flex;
flex-direction: column;
}
.loadMore {
margin-top: 16px;
padding: 8px 16px;
}
.loader,
.error {
padding: 20px;
text-align: center;
}

View file

@ -0,0 +1,114 @@
import React, { useEffect, useState } from "react";
import { DefaultService } from "../../api/services/DefaultService";
import type { Title } from "../../api/models/Title";
import styles from "./TitlesPage.module.css";
const LIMIT = 20;
const TitlesPage: React.FC = () => {
const [titles, setTitles] = useState<Title[]>([]);
const [search, setSearch] = useState("");
const [offset, setOffset] = useState(0);
const [loading, setLoading] = useState(true);
const [loadingMore, setLoadingMore] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchTitles = async (reset: boolean) => {
try {
if (reset) {
setLoading(true);
setOffset(0);
} else {
setLoadingMore(true);
}
const result = await DefaultService.getTitles(
search || undefined,
undefined, // status
undefined, // rating
undefined, // release_year
undefined, // release_season
LIMIT,
reset ? 0 : offset,
"all"
);
if (reset) {
setTitles(result);
} else {
setTitles(prev => [...prev, ...result]);
}
if (result.length > 0) {
setOffset(prev => prev + LIMIT);
}
} catch (err) {
console.error(err);
setError("Failed to fetch titles.");
} finally {
setLoading(false);
setLoadingMore(false);
}
};
useEffect(() => {
fetchTitles(true);
}, [search]);
if (loading) return <div className={styles.loader}>Loading...</div>;
if (error) return <div className={styles.error}>{error}</div>;
return (
<div className={styles.container}>
<div className={styles.header}>
<h1>Titles</h1>
<input
className={styles.searchInput}
placeholder="Search titles..."
value={search}
onChange={e => setSearch(e.target.value)}
/>
</div>
<div className={styles.list}>
{titles.map((t) => (
<div key={t.id} className={styles.card}>
{t.poster_id ? (
<img
src={`/images/${t.poster_id}.png`}
alt="Poster"
className={styles.poster}
/>
) : (
<div className={styles.posterPlaceholder}>No Image</div>
)}
<div className={styles.cardInfo}>
<h3 className={styles.titleName}>{t.name}</h3>
<p className={styles.meta}>
{t.release_year} {t.release_season}
</p>
<p className={styles.rating}>Rating: {t.rating}</p>
<p className={styles.status}>{t.status}</p>
</div>
</div>
))}
</div>
{titles.length > 0 && (
<button
className={styles.loadMore}
onClick={() => fetchTitles(false)}
disabled={loadingMore}
>
{loadingMore ? "Loading..." : "Load More"}
</button>
)}
</div>
);
};
export default TitlesPage;