simedkom/resources/js/pages/home/news/components/MainContent.jsx

274 lines
9.7 KiB
JavaScript

import { router } from '@inertiajs/react';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Chip from '@mui/material/Chip';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import { styled } from '@mui/material/styles';
import Headline from '../../components/Headline';
import { Pagination } from '@mui/material';
import { useState, useEffect, useRef } from "react";
import Author from './Author';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
import { Search } from './Search';
const StyledCard = styled(Card)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
padding: 0,
height: '100%',
backgroundColor: (theme.vars || theme).palette.background.paper,
'&:hover': {
backgroundColor: 'transparent',
cursor: 'pointer',
},
'&:focus-visible': {
outline: '3px solid',
outlineColor: 'hsla(210, 98%, 48%, 0.5)',
outlineOffset: '2px',
},
}));
const StyledCardContent = styled(CardContent)({
display: 'flex',
flexDirection: 'column',
gap: 4,
padding: 16,
flexGrow: 1,
'&:last-child': {
paddingBottom: 16,
},
});
const StyledTypography = styled(Typography)({
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 2,
overflow: 'hidden',
textOverflow: 'ellipsis',
});
export default function MainContent({ pageTitle, pageDescription, categories, news, filters }) {
const [selectedCategory, setSelectedCategory] = useState(filters?.category || 'all');
const [search, setSearch] = useState(filters?.search || '');
const [focusedCardIndex, setFocusedCardIndex] = useState(null);
const searchDebounceRef = useRef(null);
const handleFocus = (index) => {
setFocusedCardIndex(index);
};
useEffect(() => {
if (search === (filters?.search || '')) {
return;
}
if (searchDebounceRef.current) {
clearTimeout(searchDebounceRef.current);
}
searchDebounceRef.current = setTimeout(() => {
router.get(window.location.pathname, {
category: selectedCategory,
search: search
}, {
preserveState: true,
preserveScroll: true,
replace: true,
});
}, 500);
return () => {
if (searchDebounceRef.current) {
clearTimeout(searchDebounceRef.current);
}
};
}, [search]);
const handleBlur = () => {
setFocusedCardIndex(null);
};
const handleCategory = (category) => {
if (searchDebounceRef.current) {
clearTimeout(searchDebounceRef.current);
}
setSelectedCategory(category);
router.get(window.location.pathname, {
category: category,
search: search
}, {
preserveState: true,
preserveScroll: true
});
};
const handleSearch = () => {
if (searchDebounceRef.current) {
clearTimeout(searchDebounceRef.current);
}
router.get(window.location.pathname, {
category: selectedCategory,
search: search
}, {
preserveState: true,
preserveScroll: true
});
};
const handlePageChange = (event, value) => {
router.get(window.location.pathname, {
page: value,
category: selectedCategory,
search: search
}, {
preserveState: true,
preserveScroll: true,
});
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<div>
<Headline pageTitle={pageTitle} pageDescription={pageDescription} />
</div>
<Box
sx={{
display: { xs: 'flex', sm: 'none' },
flexDirection: 'row',
gap: 1,
width: { xs: '100%', md: 'fit-content' },
overflow: 'auto',
}}
>
<Search value={search} onChange={(e) => setSearch(e.target.value)} onSearch={handleSearch} />
</Box>
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column-reverse', md: 'row' },
width: '100%',
justifyContent: 'space-between',
alignItems: { xs: 'start', md: 'center' },
gap: 4,
overflow: 'auto',
}}
>
<Box
sx={{
display: 'inline-flex',
flexDirection: 'row',
gap: 1,
overflow: 'auto',
scrollbarWidth: 'none',
'&::-webkit-scrollbar': { display: 'none' },
}}
>
<Chip
onClick={() => handleCategory('all')}
size="medium"
label="Semua"
sx={{
backgroundColor: selectedCategory === 'all' ? '' : 'transparent',
}}
/>
{categories.map((category, index) =>
<Chip
onClick={() => handleCategory(category.id)}
size="medium"
label={category.name}
key={index}
sx={{
backgroundColor: selectedCategory === category.id ? '' : 'transparent',
}}
/>
)}
</Box>
<Box
sx={{
display: { xs: 'none', sm: 'flex' },
flexDirection: 'row',
gap: 1,
width: { xs: '100%', md: 'fit-content' },
overflow: 'auto',
}}
>
<Search value={search} onChange={(e) => setSearch(e.target.value)} onSearch={handleSearch} />
</Box>
</Box>
<Grid container spacing={2} columns={12}>
{news.data.map((news, index) =>
<Grid size={{ xs: 12, md: 6 }} key={index}>
<StyledCard
variant="outlined"
onFocus={() => handleFocus(news.id)}
onBlur={handleBlur}
tabIndex={0}
className={focusedCardIndex === news.id ? 'Mui-focused' : ''}
onClick={() => router.get(`/news/${news.slug}`)}
>
<CardMedia
component="img"
alt={news.title}
image={news.thumbnail}
sx={{
aspectRatio: '16 / 9',
borderBottom: '1px solid',
borderColor: 'divider',
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{news.categories.map((category, index) =>
<Chip label={category.name} color="success" variant="outlined" sx={{ mr: 1 }} key={index} />
)}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{news.title}
</Typography>
<StyledTypography
variant="body2"
color="text.secondary"
gutterBottom
dangerouslySetInnerHTML={{ __html: news.excerpt }}
/>
</StyledCardContent>
<Author author={news.author?.name} publishedAt={news.formatted_published_at} />
</StyledCard>
</Grid>
)}
</Grid>
{news.data.length > 0 ? (
<Box sx={{ display: 'flex', flexDirection: 'row', py: 4 }}>
<Pagination
count={news.last_page}
showFirstButton
showLastButton
page={news.current_page}
onChange={handlePageChange}
/>
</Box>
) : (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column' }}>
<DotLottieReact
src="/assets/lottie/cat-fishing.lottie"
loop
autoplay
style={{ width: '240px', height: '240px' }}
/>
<Typography gutterBottom variant="body1" component="div">
Tidak ada berita yang ditemukan
</Typography>
</Box>
)
}
</Box >
);
}