// src/pages/ArticlList.jsx
import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import {
Container,
Typography,
Button,
List,
ListItem,
ListItemText,
IconButton,
Tooltip,
Box,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Chip,
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import debounce from "lodash.debounce";
const ArticleList = () => {
const [articles, setArticles] = useState([]);
const [categories, setCategories] = useState([]);
const [tags, setTags] = useState([]);
const [searchQuery, setSearchQuery] = useState("");
const [selectedCategory, setSelectedCategory] = useState("");
const [selectedTags, setSelectedTags] = useState([]);
const [filteredArticles, setFilteredArticles] = useState([]);
useEffect(() => {
const fetchArticles = async () => {
try {
const response = await axios
.get("http://localhost:5000/api/articles");
setArticles(response.data);'
// Extract unique categories and tags
const allCategories = [
...new Set(response.data.flatMap((article) => article.categories)),
];
const allTags = [
...new Set(response.data.flatMap((article) => article.tags)),
];
setCategories(allCategories);
setTags(allTags);
} catch (error) {
console.error("Error fetching articles:", error);
}
};
fetchArticles();
}, []);
const debounceSearch = useCallback(
debounce((query) => {
const filtered = articles.filter((article) => {
const matchesSearch = article.title
.toLowerCase()
.includes(query.toLowerCase());
const matchesCategory =
!selectedCategory || article
.categories.includes(selectedCategory);
const matchesTags = selectedTags
.every((tag) => article.tags.includes(tag));
return matchesSearch && matchesCategory && matchesTags;
});
setFilteredArticles(filtered);
}, 500), // Debounce time in milliseconds
[articles, selectedCategory, selectedTags]
);
useEffect(() => {
debounceSearch(searchQuery);
}, [searchQuery, debounceSearch]);
const deleteArticle = async (id) => {
try {
await axios.delete(`http://localhost:5000/api/articles/${id}`);
setArticles(articles.filter((article) => article._id !== id));
} catch (error) {
console.error("Error deleting article:", error);
}
};
const handleTagChange = (event) => {
const { value } = event.target;
setSelectedTags(typeof value === "string" ? value.split(",") : value);
};
return (
<Container maxWidth="md" sx={{ mt: 4 }}>
<Typography variant="h4" gutterBottom>
Articles
</Typography>
<Button
variant="contained"
color="primary"
component={Link}
to="/create-article"
sx={{ mb: 2 }}
>
Write New Article
</Button>
<Box sx={{ mb: 2 }}>
<TextField
fullWidth
label="Search by title"
variant="outlined"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</Box>
<Box sx={{ mb: 2 }}>
<FormControl fullWidth sx={{ mb: 2 }}>
<InputLabel>Category</InputLabel>
<Select
value={selectedCategory}
onChange={(e) => setSelectedCategory(e.target.value)}
label="Category"
>
<MenuItem value="">All Categories</MenuItem>
{categories.map((category) => (
<MenuItem key={category} value={category}>
{category}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Tags</InputLabel>
<Select
multiple
value={selectedTags}
onChange={handleTagChange}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
>
{tags.map((tag) => (
<MenuItem key={tag} value={tag}>
{tag}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
<List>
{filteredArticles.map((article) => (
<ListItem
key={article._id}
secondaryAction={
<>
<Tooltip title="Edit">
<IconButton
edge="end"
component={Link}
to={`/update-article/${article._id}`}
color="primary"
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton
edge="end"
onClick={() => deleteArticle(article._id)}
color="error"
>
<DeleteIcon />
</IconButton>
</Tooltip>
</>
}
>
<ListItemText
primary={
<Link
to={`/articles/${article._id}?authorId=${article.author._id}`}>
{article.title}
</Link>
}
secondary={`By ${article.author.name} on ${new Date(
article.createdAt
).toLocaleDateString()} | Categories: ${
article.categories
.join(", ")} | Tags: ${article.tags.join(", ")}`}
/>
</ListItem>
))}
</List>
</Container>
);
};
export default ArticleList;