diff --git a/src/components/BMDashboard/Issues/IssuesList.jsx b/src/components/BMDashboard/Issues/IssuesList.jsx new file mode 100644 index 0000000000..141943ae6e --- /dev/null +++ b/src/components/BMDashboard/Issues/IssuesList.jsx @@ -0,0 +1,320 @@ +import { useState, useEffect } from 'react'; +import DatePicker from 'react-datepicker'; +import Select from 'react-select'; +import 'react-datepicker/dist/react-datepicker.css'; +import { Table, Button, Dropdown, Form, Row, Col, Container } from 'react-bootstrap'; +import './IssuesList.css'; +import { useDispatch, useSelector } from 'react-redux'; +import { fetchBMProjects } from 'actions/bmdashboard/projectActions'; +import { fetchIssues } from 'actions/bmdashboard/issueChartActions'; +export default function IssueList() { + const [tagFilter, setTagFilter] = useState(null); + const [selectedProjects, setSelectedProjects] = useState([]); + const [dateRange, setDateRange] = useState([null, null]); + const [startDate, endDate] = dateRange; + const [issues, setIssues] = useState([]); + const [editingId, setEditingId] = useState(null); + const [editedName, setEditedName] = useState(''); + const darkMode = useSelector(state => state.theme.darkMode); + + const [dropdownOpenId, setDropdownOpenId] = useState(null); + const closeDropdown = () => setDropdownOpenId(null); + + const handleTagClick = tag => { + if (tagFilter === tag) { + setTagFilter(null); + return; + } + setTagFilter(tag); + }; + const dispatch = useDispatch(); + + const { issues: rawIssues } = useSelector(state => state.bmIssues); + const projects = useSelector(state => state.bmProjects); + const projectMap = Object.fromEntries(projects.map(p => [p._id, p.name])); + + useEffect(() => { + dispatch(fetchIssues()); + dispatch(fetchBMProjects()); + }, [dispatch]); + + const getDaysSinceCreated = createdDateStr => { + const created = new Date(createdDateStr); + const now = new Date(); + const diffTime = now - created; // difference in milliseconds + const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); // convert to days + return diffDays; + }; + + useEffect(() => { + if (Array.isArray(rawIssues) && rawIssues.length > 0) { + const processed = rawIssues.map(issue => ({ + id: issue._id, + name: issue.issueTitle?.[0] ?? 'Untitled', + tag: issue.tag || '', + date: new Date(issue.createdDate.split('T')[0]), + project: projectMap[issue.projectId] || 'Unknown Project', + openSince: getDaysSinceCreated(issue.createdDate.split('T')[0]), + cost: issue.cost, + person: issue.person, + })); + setIssues(processed); + } + }, [rawIssues]); + + const uniqueProjects = [...new Set(issues.map(issue => issue.project))]; + const projectOptions = uniqueProjects.map(project => ({ + label: project, + value: project, + })); + + const handleRename = issueId => { + const issue = issues.find(i => i.id === issueId); + if (issue) { + setEditingId(issueId); + setEditedName(issue.name); + } + closeDropdown(); + }; + + const handleDelete = issueId => { + closeDropdown(); + }; + + const handleCloseIssue = issueId => { + closeDropdown(); + }; + + const handleNameChange = e => { + setEditedName(e.target.value); + }; + + const handleReset = () => { + setTagFilter(null); + setSelectedProjects([]); + setDateRange([null, null]); + }; + + const handleNameSubmit = issueId => { + setEditingId(null); + setEditedName(''); + }; + + const filteredIssues = issues.filter(issue => { + const inDateRange = + !startDate || !endDate || (issue.date >= startDate && issue.date <= endDate); + return ( + (!tagFilter || issue.tag === tagFilter) && + (selectedProjects.length === 0 || selectedProjects.includes(issue.project)) && + inDateRange + ); + }); + + const [currentPage, setCurrentPage] = useState(1); + const [pageGroupStart, setPageGroupStart] = useState(1); + const itemsPerPage = 5; + + const totalPages = Math.ceil(filteredIssues.length / itemsPerPage); + + // Slice data for current page + const indexOfLastItem = currentPage * itemsPerPage; + const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const currentItems = filteredIssues.slice(indexOfFirstItem, indexOfLastItem); + const handlePageClick = page => { + setCurrentPage(page); + + // Shift page group if needed + if (page >= pageGroupStart + 5) { + setPageGroupStart(pageGroupStart + 5); + } else if (page < pageGroupStart) { + setPageGroupStart(pageGroupStart - 5); + } + }; + + const renderPageNumbers = () => { + const pageNumbers = []; + const end = Math.min(pageGroupStart + 4, totalPages); + // eslint-disable-next-line no-plusplus + for (let i = pageGroupStart; i <= end; i++) { + pageNumbers.push( + , + ); + } + return pageNumbers; + }; + + const formatDate = date => { + return date.toISOString().slice(0, 10); // YYYY-MM-DD + }; + + const formattedRange = + startDate && endDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : ''; + + return ( + +

A List of Issues

+ + + {/* Date Filter */} + +
+ setDateRange(update)} + isClearable={false} // we'll use our own clear button + placeholderText="Filter by Date Range" + className="form-control" + value={startDate && endDate ? formattedRange : ''} + /> + +
+ + + {/* Project Filter */} + +
+