Implemented project settings page, search issues modal, general refactoring
This commit is contained in:
101
client/src/Project/IssueSearch/index.jsx
Normal file
101
client/src/Project/IssueSearch/index.jsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import useApi from 'shared/hooks/api';
|
||||
import { sortByNewest } from 'shared/utils/javascript';
|
||||
import { IssueTypeIcon } from 'shared/components';
|
||||
|
||||
import NoResultsSVG from './NoResultsSvg';
|
||||
import {
|
||||
IssueSearch,
|
||||
SearchInputCont,
|
||||
SearchInputDebounced,
|
||||
SearchIcon,
|
||||
SearchSpinner,
|
||||
Issue,
|
||||
IssueData,
|
||||
IssueTitle,
|
||||
IssueTypeId,
|
||||
SectionTitle,
|
||||
NoResults,
|
||||
NoResultsTitle,
|
||||
NoResultsTip,
|
||||
} from './Styles';
|
||||
|
||||
const propTypes = {
|
||||
project: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
const ProjectIssueSearch = ({ project }) => {
|
||||
const [isSearchTermEmpty, setIsSearchTermEmpty] = useState(true);
|
||||
|
||||
const [{ data, isLoading }, fetchIssues] = useApi.get('/issues', {}, { lazy: true });
|
||||
|
||||
const matchingIssues = get(data, 'issues', []);
|
||||
|
||||
const recentIssues = sortByNewest(project.issues, 'createdAt').slice(0, 10);
|
||||
|
||||
const handleSearchChange = value => {
|
||||
const searchTerm = value.trim();
|
||||
|
||||
setIsSearchTermEmpty(!searchTerm);
|
||||
|
||||
if (searchTerm) {
|
||||
fetchIssues({ searchTerm });
|
||||
}
|
||||
};
|
||||
|
||||
const renderIssue = issue => (
|
||||
<Link key={issue.id} to={`/project/board/issues/${issue.id}`}>
|
||||
<Issue>
|
||||
<IssueTypeIcon type={issue.type} size={25} />
|
||||
<IssueData>
|
||||
<IssueTitle>{issue.title}</IssueTitle>
|
||||
<IssueTypeId>{`${issue.type}-${issue.id}`}</IssueTypeId>
|
||||
</IssueData>
|
||||
</Issue>
|
||||
</Link>
|
||||
);
|
||||
|
||||
return (
|
||||
<IssueSearch>
|
||||
<SearchInputCont>
|
||||
<SearchInputDebounced
|
||||
autoFocus
|
||||
placeholder="Search issues by summary, description..."
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
<SearchIcon type="search" size={22} />
|
||||
{isLoading && <SearchSpinner />}
|
||||
</SearchInputCont>
|
||||
|
||||
{isSearchTermEmpty && recentIssues.length > 0 && (
|
||||
<>
|
||||
<SectionTitle>Recent Issues</SectionTitle>
|
||||
{recentIssues.map(renderIssue)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isSearchTermEmpty && matchingIssues.length > 0 && (
|
||||
<>
|
||||
<SectionTitle>Matching Issues</SectionTitle>
|
||||
{matchingIssues.map(renderIssue)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isSearchTermEmpty && !isLoading && matchingIssues.length === 0 && (
|
||||
<NoResults>
|
||||
<NoResultsSVG />
|
||||
<NoResultsTitle>We couldn't find anything matching your search</NoResultsTitle>
|
||||
<NoResultsTip>Try again with a different term.</NoResultsTip>
|
||||
</NoResults>
|
||||
)}
|
||||
</IssueSearch>
|
||||
);
|
||||
};
|
||||
|
||||
ProjectIssueSearch.propTypes = propTypes;
|
||||
|
||||
export default ProjectIssueSearch;
|
||||
Reference in New Issue
Block a user