quote-app/client/src/pages/Browse.jsx
2026-07-20 16:43:47 +00:00

110 lines
3.3 KiB
JavaScript

import { useEffect, useState } from 'react';
import {
Alert,
Box,
Card,
CardContent,
Grid,
IconButton,
Skeleton,
Tooltip,
Typography,
} from '@mui/material';
import FavoriteIcon from '@mui/icons-material/Favorite';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import { api } from '../api';
import { useAuth } from '../AuthContext';
export default function Browse() {
const { user } = useAuth();
const [quotes, setQuotes] = useState(null);
const [favoriteIds, setFavoriteIds] = useState(new Set());
const [error, setError] = useState('');
useEffect(() => {
api.list().then(setQuotes).catch((err) => setError(err.message));
}, []);
useEffect(() => {
if (!user) {
setFavoriteIds(new Set());
return;
}
api
.favorites()
.then((rows) => setFavoriteIds(new Set(rows.map((r) => r.id))))
.catch(() => setFavoriteIds(new Set()));
}, [user]);
async function toggleFavorite(quoteId) {
if (!user) return;
const isFav = favoriteIds.has(quoteId);
try {
if (isFav) {
await api.removeFavorite(quoteId);
} else {
await api.addFavorite(quoteId);
}
setFavoriteIds((prev) => {
const next = new Set(prev);
isFav ? next.delete(quoteId) : next.add(quoteId);
return next;
});
} catch (err) {
setError(err.message);
}
}
return (
<Box>
<Typography variant="h4" component="h1" gutterBottom>
Browse quotes
</Typography>
{error && (
<Alert severity="error" sx={{ mb: 2 }} onClose={() => setError('')}>
{error}
</Alert>
)}
<Grid container spacing={2}>
{quotes === null &&
Array.from({ length: 6 }).map((_, i) => (
<Grid key={i} size={{ xs: 12, sm: 6, md: 4 }}>
<Skeleton variant="rounded" height={160} />
</Grid>
))}
{quotes?.map((q) => (
<Grid key={q.id} size={{ xs: 12, sm: 6, md: 4 }}>
<Card variant="outlined" sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<CardContent sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography variant="body1" sx={{ flexGrow: 1 }}>
{q.text}
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Typography variant="body2" color="text.secondary">
{q.author}
</Typography>
<Tooltip title={user ? 'Toggle favorite' : 'Sign in to favorite'}>
<span>
<IconButton
size="small"
disabled={!user}
onClick={() => toggleFavorite(q.id)}
>
{favoriteIds.has(q.id) ? (
<FavoriteIcon fontSize="small" sx={{ color: 'tertiary.main' }} />
) : (
<FavoriteBorderIcon fontSize="small" />
)}
</IconButton>
</span>
</Tooltip>
</Box>
</CardContent>
</Card>
</Grid>
))}
</Grid>
</Box>
);
}