274 lines
9.7 KiB
JavaScript
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 >
|
|
);
|
|
}
|