diff --git a/__tests__/Unit/Components/Tasks/FilterModal.test.tsx b/__tests__/Unit/Components/Tasks/FilterDropdown.test.tsx similarity index 92% rename from __tests__/Unit/Components/Tasks/FilterModal.test.tsx rename to __tests__/Unit/Components/Tasks/FilterDropdown.test.tsx index 53c020799..1b89837b5 100644 --- a/__tests__/Unit/Components/Tasks/FilterModal.test.tsx +++ b/__tests__/Unit/Components/Tasks/FilterDropdown.test.tsx @@ -1,15 +1,15 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { Tab } from '@/interfaces/task.type'; -import FilterModal from '@/components/tasks/TaskSearch/FilterModal'; +import FilterDropdown from '@/components/tasks/TaskSearch/FilterDropdown'; const mockOnSelect = jest.fn(); const mockOnClose = jest.fn(); -describe('FilterModal', () => { +describe('FilterDropdown', () => { test('renders the modal with correct title and buttons', () => { render( - { test('renders the modal having overdue tab with correct title and buttons', () => { render( - { test('renders the modal with correct title and buttons when dev is true', () => { render( - { test('calls onSelect and onClose when a status button is clicked', () => { render( - { test('calls onClose when the close button is clicked', () => { render( - { }); test('calls onClose when clicked on outside', () => { render( - ); @@ -138,12 +136,11 @@ describe('FilterModal', () => { }); test('calls onClose when escape button is clicked', () => { render( - ); @@ -154,7 +151,7 @@ describe('FilterModal', () => { test('renders the modal with correct active tab', () => { render( - { test('renders the modal with correct active tab when dev is true', () => { render( - { test('render the filter model having BACKLOG tab with correct title and buttons when dev is true', () => { render( - { test('onSelect Function Gets Called When the Backlog Status button is Clicked when dev is true', () => { render( - { test('Selection of the Backlog Button when dev is true', () => { render( - { const mockOnClickHandler = jest.fn(); diff --git a/__tests__/Unit/Components/Tasks/Options.test.tsx b/__tests__/Unit/Components/Tasks/Options.test.tsx index c3badfb9b..7d8fe4e89 100644 --- a/__tests__/Unit/Components/Tasks/Options.test.tsx +++ b/__tests__/Unit/Components/Tasks/Options.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; -import Options from '@/components/tasks/TaskSearchDev/Suggestion/Options'; +import Options from '@/components/tasks/TaskSearch/Suggestion/Options'; import { TaskSearchOption } from '@/interfaces/searchOptions.type'; describe('Option component', () => { diff --git a/__tests__/Unit/Components/Tasks/Pill.test.tsx b/__tests__/Unit/Components/Tasks/Pill.test.tsx index 7b9e73c41..f697ef1de 100644 --- a/__tests__/Unit/Components/Tasks/Pill.test.tsx +++ b/__tests__/Unit/Components/Tasks/Pill.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import RenderPills, { PillProps, -} from '@/components/tasks/TaskSearchDev/Suggestion/Pill'; +} from '@/components/tasks/TaskSearch/Suggestion/Pill'; describe('RenderPills', () => { const setNewPillValue = jest.fn(); diff --git a/__tests__/Unit/Components/Tasks/TaskSearch.test.tsx b/__tests__/Unit/Components/Tasks/TaskSearch.test.tsx index 341af2a53..b213d3f95 100644 --- a/__tests__/Unit/Components/Tasks/TaskSearch.test.tsx +++ b/__tests__/Unit/Components/Tasks/TaskSearch.test.tsx @@ -1,9 +1,8 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import TaskSearch from '@/components/tasks/TaskSearch/TaskSearch'; -import { Tab } from '@/interfaces/task.type'; -import TaskSearchDev from '@/components/tasks/TaskSearchDev/TaskSearchDev'; import { Provider } from 'react-redux'; import { store } from '@/app/store'; +import { Tab } from '@/interfaces/task.type'; jest.mock('@/utils/getChangedStatusName', () => ({ getChangedStatusName: jest.fn((tab) => tab), @@ -20,213 +19,84 @@ jest.mock('@/app/services/usersApi', () => ({ describe('TaskSearch', () => { test('renders the search input', () => { const onSelect = jest.fn(); - const onInputChange = jest.fn(); const onClickSearchButton = jest.fn(); - render( ); - const searchInput = screen.getByTestId('search-input'); expect(searchInput).toBeInTheDocument(); }); - test('calls onClickSearchButton when Search button is clicked', () => { - const onSelect = jest.fn(); - const onInputChange = jest.fn(); - const onClickSearchButton = jest.fn(); - - render( - - ); - const searchInput = screen.getByTestId('search-input'); - fireEvent.change(searchInput, { target: { value: 'is:active' } }); - const searchButton = screen.getByText('Search'); - fireEvent.click(searchButton); - expect(onClickSearchButton).toHaveBeenCalledTimes(1); - }); - test('opens the filter modal when the Filter button is clicked', () => { const onSelect = jest.fn(); - const onInputChange = jest.fn(); const onClickSearchButton = jest.fn(); - render( ); - const filterButton = screen.getByText('Filter'); fireEvent.click(filterButton); const modalTitle = screen.getByTestId('filter-modal'); expect(modalTitle).toBeInTheDocument(); }); - test('calls onInputChange when the search input value changes', () => { - const onSelect = jest.fn(); - const onInputChange = jest.fn(); - const onClickSearchButton = jest.fn(); - - render( - - ); - - const searchInput = screen.getByTestId('search-input'); - fireEvent.change(searchInput, { target: { value: 'is:merged' } }); - expect(onInputChange).toHaveBeenCalledWith('is:merged'); - }); - - test('calls onInputChange when the search input value changes when dev is true', () => { - const onSelect = jest.fn(); - const onInputChange = jest.fn(); - const onClickSearchButton = jest.fn(); - - render( - - ); - - const searchInput = screen.getByTestId('search-input'); - fireEvent.change(searchInput, { target: { value: 'is:done' } }); - expect(onInputChange).toHaveBeenCalledWith('is:done'); - }); - test('calls onSelect when the any one filter button is clicked', () => { const onSelect = jest.fn(); - const onInputChange = jest.fn(); const onClickSearchButton = jest.fn(); - render( ); const filterButton = screen.getByText('Filter'); fireEvent.click(filterButton); - const assignedButton = screen.getByText(/assigned/i); + const assignedButton = screen.getByText(/in_progress/i); fireEvent.click(assignedButton); - expect(onSelect).toHaveBeenCalledWith('ASSIGNED'); - }); - - test('calls onSelect when the any one filter button is clicked and dev is true', () => { - const onSelect = jest.fn(); - const onClickSearchButton = jest.fn(); - - render( - - - - ); - const filterButton = screen.getByText('Filter'); - fireEvent.click(filterButton); - const unassignedButton = screen.getByText(/unassigned/i); - fireEvent.click(unassignedButton); - expect(onSelect).toHaveBeenCalledWith('UNASSIGNED'); + expect(onSelect).toHaveBeenCalledWith('IN_PROGRESS'); }); - test('calls onClickSearchButton when enter key is pressed', () => { + test('calls onClickSearchButton when enter key is pressed', async () => { const onSelect = jest.fn(); - const onInputChange = jest.fn(); const onClickSearchButton = jest.fn(); - render( ); - const searchInput = screen.getByTestId('search-input'); - fireEvent.change(searchInput, { target: { value: 'is:merged' } }); - fireEvent.keyDown(searchInput, { key: 'Enter', code: 'Enter' }); - expect(onClickSearchButton).toBeCalled(); - }); + fireEvent.change(searchInput, { target: { value: 'hello' } }); - test('Should not display status:all in search bar in dev mode', () => { - const onSelect = jest.fn(); - const onClickSearchButton = jest.fn(); - - render( - - - - ); - - const filterButton = screen.getByText('Filter'); - fireEvent.click(filterButton); - const blockedButton = screen.getByRole('button', { name: /all/i }); - fireEvent.click(blockedButton); - expect(onSelect).toHaveBeenCalledWith('ALL'); - expect(screen.queryByText('status:all')).not.toBeInTheDocument(); - }); - test('Should display status:all in search bar', () => { - const onSelect = jest.fn(); - const onInputChange = jest.fn(); - const onClickSearchButton = jest.fn(); - - render( - + await waitFor( + () => { + screen.getByText('title: hello'); + }, + { timeout: 1000 } ); - const filterButton = screen.getByText('Filter'); - fireEvent.click(filterButton); - const blockedButton = screen.getByRole('button', { name: /all/i }); - fireEvent.click(blockedButton); - expect(onSelect).toHaveBeenCalledWith('ALL'); - expect(screen.getByDisplayValue('status:all')).toBeInTheDocument(); + fireEvent.keyDown(searchInput, { key: 'Enter', code: 'Enter' }); + expect(onClickSearchButton).toBeCalled(); }); }); -describe('Multi select task search in dev mode', () => { +describe('Multi select task search', () => { const onSelect = jest.fn(); - const onInputChange = jest.fn(); const onClickSearchButton = jest.fn(); test('renders search input with empty string if dev mode is enabled', () => { render( - { const { getByTestId } = render( - { const onClickSearchButton = jest.fn(); render( - { const { getByTestId } = render( - { const onClickSearchButton = jest.fn(); const { getByTestId } = render( - { test('should generate suggestion once clicked on option pill', async () => { const { getByTestId } = render( - { const onClickSearchButton = jest.fn(); const { getByTestId } = render( - { test('should discard changes and set focus back to input if escape is pressed', async () => { const { getByTestId } = render( - { test('should take 2 backspaces to remove a pill, if pressed in an empty input field', async () => { render( - { test('should delete the pill if entire value of its is cleared', async () => { const { getByTestId } = render( - { test('should be able to traverse between options through arrow keys', async () => { render( - { test('should be able to select options using ENTER key', async () => { const { getByTestId } = render( - { test('should fetch tasks if ENTER is pressed', async () => { render( - { it('should remove pill from UI if clicked on delete button', () => { render( - { test('should discard changes made in pill and set focus back to input if clicked outside of pill', async () => { const { getByTestId } = render( - { const onClickSearchButton = jest.fn(); const { getByTestId } = render( - { const onClickSearchButton = jest.fn(); render( - { const onClickSearchButton = jest.fn(); const { getByTestId } = render( - { global.dispatchEvent(new Event('resize')); }; - test('renders tabs component', async () => { - setWindowInnerWidth(breakpointToShowTabs); - - renderWithRouter( - - - - ); - - expect(screen.getByText(/loading/i)).toBeInTheDocument(); - - await screen.findByTestId('tabs'); - }); - - test('select tab and set active', async () => { - setWindowInnerWidth(breakpointToShowTabs); - renderWithRouter( - - - , - { - query: { section: 'unassigned' }, - } - ); - await screen.findByTestId('tabs'); - const tabsContainer = within( - screen.getByTestId('status-tabs-container') - ); - const assignedButton = tabsContainer.getByRole('button', { - name: 'UNASSIGNED', - }); - expect(assignedButton).toHaveTextContent('UNASSIGNED'); - await screen.findByText(NO_TASKS_FOUND_MESSAGE); - expect(screen.getByText(NO_TASKS_FOUND_MESSAGE)).toBeInTheDocument(); - }); - test('displays "No tasks found" message when there are no tasks', async () => { server.use(noTasksFoundHandler); const { findByText } = renderWithRouter( @@ -108,124 +72,6 @@ describe('tasks content', () => { expect(task).toBeInTheDocument(); }); - test('Selecting a tab pushes into query params', async () => { - setWindowInnerWidth(breakpointToShowTabs); - const mockPushFunction = jest.fn(); - renderWithRouter( - - - , - { push: mockPushFunction } - ); - - await screen.findByTestId('tabs'); - const tabsContainer = within( - screen.getByTestId('status-tabs-container') - ); - const inProgressBtn = tabsContainer.getByRole('button', { - name: /IN PROGRESS/i, - }); - expect(inProgressBtn).toHaveClass('tabButton'); - const unassignedButton = tabsContainer.getByRole('button', { - name: /UNASSIGNED/i, - }); - fireEvent.click(unassignedButton); - expect(mockPushFunction).toBeCalledTimes(1); - expect(mockPushFunction).toBeCalledWith({ - query: { - q: 'status:available', - }, - }); - }); - - test('should show status-tabs-container after 450px of screen width', async () => { - setWindowInnerWidth(breakpointToShowTabs); - const mockPushFunction = jest.fn(); - renderWithRouter( - - - , - { push: mockPushFunction } - ); - - await screen.findByTestId('tabs'); - const tabsContainer = screen.getByTestId('status-tabs-container'); - const tabStyles = getComputedStyle(tabsContainer); - expect(tabStyles.display).toBe('block'); - }); - - test('should show status-select-container 450px below status-tabs-container', async () => { - setWindowInnerWidth(breakpointToShowSelect); - const mockPushFunction = jest.fn(); - renderWithRouter( - - - , - { push: mockPushFunction } - ); - await screen.findByTestId('status-select-container'); - const selectContainer = screen.getByTestId('status-select-container'); - const selectStyles = getComputedStyle(selectContainer); - - expect(selectStyles.display).toBe('block'); - }); - - test('Selecting a value from dropdown pushes into query params', async () => { - setWindowInnerWidth(breakpointToShowSelect); - const mockPushFunction = jest.fn(); - renderWithRouter( - - - , - { push: mockPushFunction } - ); - - await screen.findByTestId('status-select-container'); - const selectContainer = screen?.getByTestId( - 'selected-option-container' - ); - - fireEvent.click(selectContainer); - fireEvent.keyDown(selectContainer, { - key: 'ArrowDown', - code: 'ArrowDown', - }); - - fireEvent.keyDown(selectContainer, { - key: 'Enter', - code: 'Enter', - }); - - expect(mockPushFunction).toBeCalledTimes(1); - expect(mockPushFunction).toBeCalledWith({ - query: { - q: 'status:in-progress', - }, - }); - }); - - test('searchButtonHandler when search button is clicked', async () => { - setWindowInnerWidth(breakpointToShowTabs); - const mockPushFunction = jest.fn(); - renderWithRouter( - - - , - { push: mockPushFunction } - ); - - const searchButton = await screen.findByTestId('search-button'); - fireEvent.click(searchButton); - await waitFor(() => { - expect(mockPushFunction).toBeCalledTimes(1); - }); - expect(mockPushFunction).toBeCalledWith({ - query: { - q: 'status:all', - }, - }); - }); - test('setInputValue when input value is changed', async () => { setWindowInnerWidth(breakpointToShowTabs); const mockPushFunction = jest.fn(); @@ -239,34 +85,4 @@ describe('tasks content', () => { fireEvent.change(searchInput, { target: { value: 'test' } }); expect(searchInput).toHaveValue('test'); }); - - test('Query param should be changed when tab is selected', async () => { - const mockPushFunction = jest.fn(); - renderWithRouter( - - - , - { - push: mockPushFunction, - query: { - q: 'status:all assignee:satyam Add', - }, - } - ); - - await screen.findByTestId('tabs'); - const tabsContainer = within( - screen.getByTestId('status-tabs-container') - ); - const assignedButton = tabsContainer.getByRole('button', { - name: 'ASSIGNED', - }); - fireEvent.click(assignedButton); - expect(mockPushFunction).toBeCalledTimes(1); - expect(mockPushFunction).toBeCalledWith({ - query: { - q: 'status:assigned assignee:satyam Add', - }, - }); - }); }); diff --git a/src/components/tasks/TabSection.tsx b/src/components/tasks/TabSection.tsx deleted file mode 100644 index eee9ef01f..000000000 --- a/src/components/tasks/TabSection.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { TABS, Tab } from '@/interfaces/task.type'; -import styles from '@/styles/tasks.module.scss'; -import Tabs from '../Tabs'; -export const TabSection = ({ - onSelect, - activeTab, - dev, -}: { - onSelect: (tab: Tab) => void; - activeTab: Tab; - dev?: boolean; -}) => { - return ( -
- -
- ); -}; diff --git a/src/components/tasks/TaskSearchDev/FilterDropdown.tsx b/src/components/tasks/TaskSearch/FilterDropdown.tsx similarity index 100% rename from src/components/tasks/TaskSearchDev/FilterDropdown.tsx rename to src/components/tasks/TaskSearch/FilterDropdown.tsx diff --git a/src/components/tasks/TaskSearch/FilterModal.tsx b/src/components/tasks/TaskSearch/FilterModal.tsx deleted file mode 100644 index 174d51b31..000000000 --- a/src/components/tasks/TaskSearch/FilterModal.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import styles from './tasksearch.module.scss'; -import { - Tab, - depreciatedTaskStatus, - newTaskStatus, -} from '@/interfaces/task.type'; -import { getChangedStatusName } from '@/utils/getChangedStatusName'; -import { useEffect } from 'react'; - -type FilterModalProps = { - tabs: Tab[]; - onSelect: (tab: Tab) => void; - activeTab?: Tab; - onClose: () => void; - dev?: boolean; -}; - -const FilterModal = ({ - tabs, - onSelect, - activeTab, - onClose, - dev, -}: FilterModalProps) => { - const onKeyDownHandler = (event: KeyboardEvent) => { - if (dev && event.key === 'Escape') onClose(); - }; - useEffect(() => { - document.addEventListener('keydown', onKeyDownHandler); - return () => document.removeEventListener('keydown', onKeyDownHandler); - }, []); - - return ( - <> - {dev && ( -
- )} -
-
- Filter - - × - -
-
- {tabs - .filter((tab: Tab) => - dev - ? !depreciatedTaskStatus.includes(tab) - : tab != 'BACKLOG' && - !newTaskStatus.includes(tab) - ) - .map((tab) => ( - - ))} -
-
- - ); -}; - -export default FilterModal; diff --git a/src/components/tasks/TaskSearchDev/Suggestion/Option.tsx b/src/components/tasks/TaskSearch/Suggestion/Option.tsx similarity index 100% rename from src/components/tasks/TaskSearchDev/Suggestion/Option.tsx rename to src/components/tasks/TaskSearch/Suggestion/Option.tsx diff --git a/src/components/tasks/TaskSearchDev/Suggestion/Options.tsx b/src/components/tasks/TaskSearch/Suggestion/Options.tsx similarity index 100% rename from src/components/tasks/TaskSearchDev/Suggestion/Options.tsx rename to src/components/tasks/TaskSearch/Suggestion/Options.tsx diff --git a/src/components/tasks/TaskSearchDev/Suggestion/Pill.tsx b/src/components/tasks/TaskSearch/Suggestion/Pill.tsx similarity index 98% rename from src/components/tasks/TaskSearchDev/Suggestion/Pill.tsx rename to src/components/tasks/TaskSearch/Suggestion/Pill.tsx index 9f963438f..f493003c4 100644 --- a/src/components/tasks/TaskSearchDev/Suggestion/Pill.tsx +++ b/src/components/tasks/TaskSearch/Suggestion/Pill.tsx @@ -11,7 +11,7 @@ export interface PillProps { removePill: (idx: number) => void; selectedPill: false | number; pillToBeRemoved: number; - setSelectedPill: (key: number | false) => void; + setSelectedPill: (key: number) => void; } export default function RenderPills({ diff --git a/src/components/tasks/TaskSearchDev/Suggestion/option.module.scss b/src/components/tasks/TaskSearch/Suggestion/option.module.scss similarity index 100% rename from src/components/tasks/TaskSearchDev/Suggestion/option.module.scss rename to src/components/tasks/TaskSearch/Suggestion/option.module.scss diff --git a/src/components/tasks/TaskSearchDev/Suggestion/pill.module.scss b/src/components/tasks/TaskSearch/Suggestion/pill.module.scss similarity index 100% rename from src/components/tasks/TaskSearchDev/Suggestion/pill.module.scss rename to src/components/tasks/TaskSearch/Suggestion/pill.module.scss diff --git a/src/components/tasks/TaskSearch/TaskSearch.tsx b/src/components/tasks/TaskSearch/TaskSearch.tsx index eb804d7bd..acaee9698 100644 --- a/src/components/tasks/TaskSearch/TaskSearch.tsx +++ b/src/components/tasks/TaskSearch/TaskSearch.tsx @@ -1,8 +1,15 @@ -import { useState } from 'react'; -import { LuChevronDown } from 'react-icons/lu'; +import { useEffect, useState, useRef } from 'react'; import styles from './tasksearch.module.scss'; import { TABS, Tab } from '@/interfaces/task.type'; -import FilterModal from './FilterModal'; +import FilterDropdown from './FilterDropdown'; +import useDebounce from '@/hooks/useDebounce'; +import { TaskSearchOption } from '@/interfaces/searchOptions.type'; +import Options from './Suggestion/Options'; +import Pills from './Suggestion/Pill'; +import convertStringToOptions from '@/utils/convertStringToOptions'; +import convertSearchOptionsToQuery from '@/utils/convertSearchOptionsToQuery'; +import findCoordinates from '@/helperFunctions/findCoordinates'; +import { useFilterSuggestion } from './useFilterSuggestion'; interface SuggestionCoordinates { left: number | null; @@ -16,84 +23,317 @@ const initialSuggestionCoordinates: SuggestionCoordinates = { }; type TaskSearchProps = { - onSelect: (tab: Tab) => void; + onFilterDropdownSelect: (tab: Tab) => void; + filterDropdownActiveTab?: Tab; inputValue: string; - activeTab?: Tab; - onInputChange: (value: string) => void; onClickSearchButton: (param?: string) => void; }; const TaskSearch = ({ - onSelect, + onFilterDropdownSelect, + filterDropdownActiveTab, inputValue, - activeTab, - onInputChange, onClickSearchButton, }: TaskSearchProps) => { - const [modalOpen, setModalOpen] = useState(false); + const [filterDropdownModelOpen, setFilterDropdownModelOpen] = + useState(false); + + const userInputRef = useRef(null); + const [typedInput, setTypedInput] = useState(''); + const defferedUserInput: string = useDebounce(typedInput, 300); + + const [selectedFilters, setSelectedFilters] = useState< + Array + >(convertStringToOptions(inputValue)); + const [onEditSelectedFilterValue, setOnEditSelectedFilterValue] = + useState(''); + const [onEditSelectedFilterIndex, setOnEditSelectedFilterIndex] = + useState(-1); + const [onRemoveSelectedFilterIndex, setOnRemoveSelectedFilterIndex] = + useState(-1); + const defferedPillValue = useDebounce(onEditSelectedFilterValue, 300); + + const [ + activeFilterSuggestionDropdownIndex, + setActiveFilterSuggestionDropdownIndex, + ] = useState(-1); + const [suggestionCoordinates, setSuggestionCoordinates] = + useState(initialSuggestionCoordinates); const searchButtonHandler = () => { - onClickSearchButton(); + if (onEditSelectedFilterIndex === -1) { + onClickSearchButton(convertSearchOptionsToQuery(selectedFilters)); + } }; + const { filterSuggestions } = useFilterSuggestion({ + typedInput, + defferedPillValue, + defferedUserInput, + onEditSelectedFilterIndex, + onEditSelectedFilterValue, + selectedFilters, + activeFilterSuggestionDropdownIndex, + setActiveFilterSuggestionDropdownIndex, + }); + const handleModal = () => { - setModalOpen(!modalOpen); + setFilterDropdownModelOpen(!filterDropdownModelOpen); }; + useEffect(() => { + setSelectedFilters(convertStringToOptions(inputValue)); + }, [inputValue]); + const toggleInputFocus = (inFocus = true) => { + inFocus && userInputRef.current?.focus(); + !inFocus && userInputRef.current?.blur(); + }; const handleKeyPress = (event: React.KeyboardEvent) => { switch (event.key) { - case 'Enter': - if (inputValue.length > 0) { + case 'Backspace': + if ( + onEditSelectedFilterIndex !== -1 && + onEditSelectedFilterValue.length === 1 + ) { + const newOptions = selectedFilters.filter( + (_, idx) => idx !== onEditSelectedFilterIndex + ); + setSelectedFilters(newOptions); + setOnEditSelectedFilterIndex(-1); + } else if ( + typedInput.length === 0 && + onEditSelectedFilterIndex === -1 + ) { + if (onRemoveSelectedFilterIndex === -1) { + setOnRemoveSelectedFilterIndex( + selectedFilters.length - 1 + ); + } else { + const newOptions = selectedFilters.filter( + (_, idx) => idx !== onRemoveSelectedFilterIndex + ); + setSelectedFilters(newOptions); + setOnEditSelectedFilterIndex(-1); + onClickSearchButton( + convertSearchOptionsToQuery(newOptions) + ); + setOnRemoveSelectedFilterIndex(-1); + } + } + + break; + + case 'ArrowUp': + if (activeFilterSuggestionDropdownIndex > -1) { + setActiveFilterSuggestionDropdownIndex( + activeFilterSuggestionDropdownIndex - 1 + ); + event.preventDefault(); + } + break; + case 'ArrowDown': + if ( + filterSuggestions.length - 1 > + activeFilterSuggestionDropdownIndex + ) { + setActiveFilterSuggestionDropdownIndex( + activeFilterSuggestionDropdownIndex + 1 + ); + event.preventDefault(); + } + break; + case 'Enter': { + if (activeFilterSuggestionDropdownIndex > -1) { + onSuggestionSelected(); + } else if ( + onEditSelectedFilterIndex !== -1 && + onEditSelectedFilterValue.length > 0 && + activeFilterSuggestionDropdownIndex !== -1 + ) { + onSuggestionSelected(); + } else if ( + onEditSelectedFilterValue.length === 0 && + typedInput.length === 0 + ) { + setActiveFilterSuggestionDropdownIndex(-1); searchButtonHandler(); } break; + } + case 'Escape': + onEditSelectedFilterIndex !== -1 && + setOnEditSelectedFilterIndex(-1); + break; default: break; } }; + const onResizeHandler = () => { + setSuggestionCoordinates(findCoordinates()); + }; + useEffect(onResizeHandler, [ + defferedPillValue, + defferedUserInput, + filterSuggestions.length, + ]); + const removePill = (idx: number) => { + const updatedOptions = selectedFilters.filter( + (_, index) => index !== idx + ); + setSelectedFilters(updatedOptions); + setOnEditSelectedFilterValue(''); + toggleInputFocus(); + onClickSearchButton(convertSearchOptionsToQuery(updatedOptions)); + }; + + const onSuggestionSelected = ( + idx = activeFilterSuggestionDropdownIndex + ) => { + if (!filterSuggestions?.[idx]) return; + + if (onEditSelectedFilterIndex === -1) { + const optionDetails = filterSuggestions[idx]; + if (optionDetails) { + setSelectedFilters([...selectedFilters, optionDetails]); + setTypedInput(''); + onClickSearchButton( + convertSearchOptionsToQuery([ + ...selectedFilters, + optionDetails, + ]) + ); + } + } else { + const newOptions = selectedFilters; + newOptions[onEditSelectedFilterIndex] = filterSuggestions[idx]; + setSelectedFilters(newOptions); + setOnEditSelectedFilterIndex(-1); + onClickSearchButton(convertSearchOptionsToQuery(newOptions)); + } + setActiveFilterSuggestionDropdownIndex(-1); + toggleInputFocus(true); + }; + + const handleClickOutside = (event: React.MouseEvent) => { + const target = event.target as HTMLElement; + if ( + target && + target.className.includes('pill-input-wrapper') && + onEditSelectedFilterIndex !== -1 + ) { + setOnEditSelectedFilterIndex(-1); + } else if (target && target.className.includes('pill-input-wrapper')) { + setOnEditSelectedFilterValue(''); + setOnRemoveSelectedFilterIndex(-1); + toggleInputFocus(); + } + }; + useEffect(() => { + if (onEditSelectedFilterIndex === -1) { + toggleInputFocus(); + setOnRemoveSelectedFilterIndex(-1); + setOnEditSelectedFilterValue(''); + } + }, [onEditSelectedFilterIndex]); + + useEffect(() => { + filterSuggestions.length === 0 && + setSuggestionCoordinates(initialSuggestionCoordinates); + }, [filterSuggestions.length]); + + useEffect(() => { + window.addEventListener('resize', onResizeHandler); + return () => window.removeEventListener('resize', onResizeHandler); + }, []); return (
-

Filter

- - {modalOpen && ( - )}
- onInputChange(e.target.value)} - spellCheck="false" - /> -
-
- +
+
+ {selectedFilters.map((value, key) => ( + + ))} + + {onEditSelectedFilterIndex === -1 && ( +
+ + setActiveFilterSuggestionDropdownIndex( + -1 + ) + } + className={`task-search-input ${ + styles['search-input-dev'] + } ${ + onRemoveSelectedFilterIndex !== -1 + ? styles['remove-caret'] + : '' + }`} + data-testid="search-input" + type="text" + value={typedInput} + placeholder="Eg: status:done assignee:joy title:New Feature" + onChange={(e) => { + onRemoveSelectedFilterIndex !== -1 && + setOnRemoveSelectedFilterIndex(-1); + setTypedInput(e.target.value); + }} + onKeyDown={handleKeyPress} + spellCheck="false" + /> +
+ )} +
+ + {filterSuggestions.length > 0 && + (typedInput || + (onEditSelectedFilterIndex !== -1 && + onEditSelectedFilterValue)) && ( + + )} +
); diff --git a/src/components/tasks/TaskSearch/tasksearch.module.scss b/src/components/tasks/TaskSearch/tasksearch.module.scss index bed048a06..08e25e759 100644 --- a/src/components/tasks/TaskSearch/tasksearch.module.scss +++ b/src/components/tasks/TaskSearch/tasksearch.module.scss @@ -37,6 +37,7 @@ } .filter-button { + width: 5rem; height: 2rem; position: relative; border-radius: $radius 0 0 $radius; @@ -50,17 +51,19 @@ display: flex; flex-direction: row; align-items: center; - column-gap: 0.4rem; - padding: 0 0.5rem; -} + padding-left: 5px; -.filter-chevron { - width: 1rem; - height: 1rem; -} + &:after { + content: ''; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid $black; + margin-left: 0.5rem; + } -.filter-chevron-open { - transform: rotate(180deg); + @media (max-width: 768px) { + width: 6rem; + } } .search-bar-div { @@ -213,9 +216,9 @@ } .filter-modal { - position: absolute; - top: 2.534rem; - left: 0; + position: relative; + top: 11.5rem; + left: -2.8rem; min-width: 15rem; background-color: $white; border-radius: $radius; @@ -225,6 +228,13 @@ justify-content: space-between; align-items: center; z-index: 3; + @media (max-width: 768px) { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80%; + } } .filter-modal-dev { top: 13.5rem; diff --git a/src/components/tasks/TaskSearchDev/useFilterSuggestion.tsx b/src/components/tasks/TaskSearch/useFilterSuggestion.tsx similarity index 96% rename from src/components/tasks/TaskSearchDev/useFilterSuggestion.tsx rename to src/components/tasks/TaskSearch/useFilterSuggestion.tsx index f8e99d17a..4eb736b4a 100644 --- a/src/components/tasks/TaskSearchDev/useFilterSuggestion.tsx +++ b/src/components/tasks/TaskSearch/useFilterSuggestion.tsx @@ -5,7 +5,7 @@ import generateSuggestions from '@/utils/generateSuggestions'; type Props = { typedInput: string; defferedUserInput: string; - onEditSelectedFilterIndex: false | number; + onEditSelectedFilterIndex: number; onEditSelectedFilterValue: string; defferedPillValue: string; selectedFilters: Array; @@ -41,12 +41,12 @@ export const useFilterSuggestion = ({ function getUserInput() { let userInput; if ( - onEditSelectedFilterIndex === false && + onEditSelectedFilterIndex === -1 && typedInput === defferedUserInput ) { userInput = defferedUserInput; } else if ( - onEditSelectedFilterIndex !== false && + onEditSelectedFilterIndex !== -1 && onEditSelectedFilterValue === defferedPillValue ) { userInput = defferedPillValue; diff --git a/src/components/tasks/TaskSearchDev/TaskSearchDev.tsx b/src/components/tasks/TaskSearchDev/TaskSearchDev.tsx deleted file mode 100644 index 7df5c2bcd..000000000 --- a/src/components/tasks/TaskSearchDev/TaskSearchDev.tsx +++ /dev/null @@ -1,341 +0,0 @@ -import { useEffect, useState, useRef } from 'react'; -import styles from './tasksearch.module.scss'; -import { TABS, Tab } from '@/interfaces/task.type'; -import FilterDropdown from './FilterDropdown'; -import useDebounce from '@/hooks/useDebounce'; -import { TaskSearchOption } from '@/interfaces/searchOptions.type'; -import Options from './Suggestion/Options'; -import RenderPills from './Suggestion/Pill'; -import convertStringToOptions from '@/utils/convertStringToOptions'; -import convertSearchOptionsToQuery from '@/utils/convertSearchOptionsToQuery'; -import findCoordinates from '@/helperFunctions/findCoordinates'; -import { useFilterSuggestion } from './useFilterSuggestion'; - -interface SuggestionCoordinates { - left: number | null; - maxWidth: number | null; - top: number | null; -} -const initialSuggestionCoordinates: SuggestionCoordinates = { - left: null, - maxWidth: null, - top: null, -}; - -type TaskSearchProps = { - onFilterDropdownSelect: (tab: Tab) => void; - filterDropdownActiveTab?: Tab; - inputValue: string; - onClickSearchButton: (param?: string) => void; -}; - -const TaskSearchDev = ({ - onFilterDropdownSelect, - filterDropdownActiveTab, - inputValue, - onClickSearchButton, -}: TaskSearchProps) => { - const [filterDropdownModelOpen, setFilterDropdownModelOpen] = - useState(false); - - const userInputRef = useRef(null); - const [typedInput, setTypedInput] = useState(''); - const defferedUserInput: string = useDebounce(typedInput, 300); - - const [selectedFilters, setSelectedFilters] = useState< - Array - >(convertStringToOptions(inputValue)); - const [onEditSelectedFilterValue, setOnEditSelectedFilterValue] = - useState(''); - const [onEditSelectedFilterIndex, setOnEditSelectedFilterIndex] = - useState(false); - const [onRemoveSelectedFilterIndex, setOnRemoveSelectedFilterIndex] = - useState(-1); - const defferedPillValue = useDebounce(onEditSelectedFilterValue, 300); - - const [ - activeFilterSuggestionDropdownIndex, - setActiveFilterSuggestionDropdownIndex, - ] = useState(-1); - const [suggestionCoordinates, setSuggestionCoordinates] = - useState(initialSuggestionCoordinates); - - const searchButtonHandler = () => { - if (onEditSelectedFilterIndex === false) { - onClickSearchButton(convertSearchOptionsToQuery(selectedFilters)); - } - }; - - const { filterSuggestions } = useFilterSuggestion({ - typedInput, - defferedPillValue, - defferedUserInput, - onEditSelectedFilterIndex, - onEditSelectedFilterValue, - selectedFilters, - activeFilterSuggestionDropdownIndex, - setActiveFilterSuggestionDropdownIndex, - }); - - const handleModal = () => { - setFilterDropdownModelOpen(!filterDropdownModelOpen); - }; - useEffect(() => { - setSelectedFilters(convertStringToOptions(inputValue)); - }, [inputValue]); - - const toggleInputFocus = (inFocus = true) => { - inFocus && userInputRef.current?.focus(); - !inFocus && userInputRef.current?.blur(); - }; - const handleKeyPress = (event: React.KeyboardEvent) => { - switch (event.key) { - case 'Backspace': - if ( - onEditSelectedFilterIndex !== false && - onEditSelectedFilterValue.length === 1 - ) { - const newOptions = selectedFilters.filter( - (_, idx) => idx !== onEditSelectedFilterIndex - ); - setSelectedFilters(newOptions); - setOnEditSelectedFilterIndex(false); - } else if ( - typedInput.length === 0 && - onEditSelectedFilterIndex === false - ) { - if (onRemoveSelectedFilterIndex === -1) { - setOnRemoveSelectedFilterIndex( - selectedFilters.length - 1 - ); - } else { - const newOptions = selectedFilters.filter( - (_, idx) => idx !== onRemoveSelectedFilterIndex - ); - setSelectedFilters(newOptions); - setOnEditSelectedFilterIndex(false); - onClickSearchButton( - convertSearchOptionsToQuery(newOptions) - ); - setOnRemoveSelectedFilterIndex(-1); - } - } - - break; - - case 'ArrowUp': - if (activeFilterSuggestionDropdownIndex > -1) { - setActiveFilterSuggestionDropdownIndex( - activeFilterSuggestionDropdownIndex - 1 - ); - event.preventDefault(); - } - break; - case 'ArrowDown': - if ( - filterSuggestions.length - 1 > - activeFilterSuggestionDropdownIndex - ) { - setActiveFilterSuggestionDropdownIndex( - activeFilterSuggestionDropdownIndex + 1 - ); - event.preventDefault(); - } - break; - case 'Enter': { - if (activeFilterSuggestionDropdownIndex > -1) { - onSuggestionSelected(); - } else if ( - onEditSelectedFilterIndex !== false && - onEditSelectedFilterValue.length > 0 && - activeFilterSuggestionDropdownIndex !== -1 - ) { - onSuggestionSelected(); - } else if ( - onEditSelectedFilterValue.length === 0 && - typedInput.length === 0 - ) { - setActiveFilterSuggestionDropdownIndex(-1); - searchButtonHandler(); - } - break; - } - case 'Escape': - onEditSelectedFilterIndex !== false && - setOnEditSelectedFilterIndex(false); - break; - default: - break; - } - }; - - const onResizeHandler = () => { - setSuggestionCoordinates(findCoordinates()); - }; - useEffect(onResizeHandler, [ - defferedPillValue, - defferedUserInput, - filterSuggestions.length, - ]); - const removePill = (idx: number) => { - const updatedOptions = selectedFilters.filter( - (_, index) => index !== idx - ); - setSelectedFilters(updatedOptions); - setOnEditSelectedFilterValue(''); - toggleInputFocus(); - onClickSearchButton(convertSearchOptionsToQuery(updatedOptions)); - }; - - const onSuggestionSelected = ( - idx = activeFilterSuggestionDropdownIndex - ) => { - if (!filterSuggestions?.[idx]) return; - - if (onEditSelectedFilterIndex === false) { - const optionDetails = filterSuggestions[idx]; - if (optionDetails) { - setSelectedFilters([...selectedFilters, optionDetails]); - setTypedInput(''); - onClickSearchButton( - convertSearchOptionsToQuery([ - ...selectedFilters, - optionDetails, - ]) - ); - } - } else { - const newOptions = selectedFilters; - newOptions[onEditSelectedFilterIndex] = filterSuggestions[idx]; - setSelectedFilters(newOptions); - setOnEditSelectedFilterIndex(false); - onClickSearchButton(convertSearchOptionsToQuery(newOptions)); - } - setActiveFilterSuggestionDropdownIndex(-1); - toggleInputFocus(true); - }; - - const handleClickOutside = (event: React.MouseEvent) => { - const target = event.target as HTMLElement; - if ( - target && - target.className.includes('pill-input-wrapper') && - onEditSelectedFilterIndex !== false - ) { - setOnEditSelectedFilterIndex(false); - } else if (target && target.className.includes('pill-input-wrapper')) { - setOnEditSelectedFilterValue(''); - setOnRemoveSelectedFilterIndex(-1); - toggleInputFocus(); - } - }; - useEffect(() => { - if (onEditSelectedFilterIndex === false) { - toggleInputFocus(); - setOnRemoveSelectedFilterIndex(-1); - setOnEditSelectedFilterValue(''); - } - }, [onEditSelectedFilterIndex]); - - useEffect(() => { - filterSuggestions.length === 0 && - setSuggestionCoordinates(initialSuggestionCoordinates); - }, [filterSuggestions.length]); - - useEffect(() => { - window.addEventListener('resize', onResizeHandler); - }, []); - return ( -
-
-
- Filter - {filterDropdownModelOpen && ( - - )} -
- -
-
- {selectedFilters.map((value, key) => ( - - ))} - - {onEditSelectedFilterIndex === false && ( -
- - setActiveFilterSuggestionDropdownIndex( - -1 - ) - } - className={`task-search-input ${ - styles['search-input-dev'] - } ${ - onRemoveSelectedFilterIndex !== -1 - ? styles['remove-caret'] - : '' - }`} - data-testid="search-input" - type="text" - value={typedInput} - placeholder="Eg: status:done assignee:joy title:New Feature" - onChange={(e) => { - onRemoveSelectedFilterIndex !== -1 && - setOnRemoveSelectedFilterIndex(-1); - setTypedInput(e.target.value); - }} - onKeyDown={handleKeyPress} - spellCheck="false" - /> -
- )} -
- - {filterSuggestions.length > 0 && - (typedInput || - (onEditSelectedFilterIndex !== false && - onEditSelectedFilterValue)) && ( - - )} -
-
-
- ); -}; - -export default TaskSearchDev; diff --git a/src/components/tasks/TaskSearchDev/tasksearch.module.scss b/src/components/tasks/TaskSearchDev/tasksearch.module.scss deleted file mode 100644 index 08e25e759..000000000 --- a/src/components/tasks/TaskSearchDev/tasksearch.module.scss +++ /dev/null @@ -1,301 +0,0 @@ -@import '../../../styles/variables.scss'; - -.task-search-container { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - padding: 1rem; - - @media (max-width: 768px) { - flex-direction: column; - justify-content: center; - align-items: center; - } -} - -.filter-container { - width: 100%; - display: flex; - position: relative; - flex-direction: row; - justify-content: space-between; - align-items: center; - border-radius: $radius; - margin-right: 1rem; - border: 1px solid $lighter-gray; - - @media (max-width: 768px) { - width: 100%; - margin-right: 0; - } - - @media (max-width: 368px) { - width: 90%; - margin-right: 0; - } -} - -.filter-button { - width: 5rem; - height: 2rem; - position: relative; - border-radius: $radius 0 0 $radius; - background-color: $white; - color: $black; - font-weight: bold; - cursor: pointer; - border: none; - border-right: none; - outline: none; - display: flex; - flex-direction: row; - align-items: center; - padding-left: 5px; - - &:after { - content: ''; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 5px solid $black; - margin-left: 0.5rem; - } - - @media (max-width: 768px) { - width: 6rem; - } -} - -.search-bar-div { - width: 43vw; - flex: 1; - min-height: 2rem; - border-radius: 0 $radius $radius 0; - background-color: $white; - border-left: 1px solid $lighter-gray; - background-image: url('https://img.icons8.com/ios-filled/16/000000/search.png'); - background-repeat: no-repeat; - background-position: 0.5rem; - padding-left: 1.8rem; - display: flex; - align-items: center; - flex-wrap: wrap; -} -.in-focus { - border: 1px solid $gray; - cursor: pointer; - color: $black; -} -.search-input { - width: 100%; - height: 2rem; - padding: 0.5rem 0.5rem 0.5rem 2rem; - border-radius: 0 $radius $radius 0; - border: none; - outline: none; - background-image: url('https://img.icons8.com/ios-filled/16/000000/search.png'); - background-repeat: no-repeat; - background-position: 0.5rem; - padding-left: 2rem; - background-color: $white; - border-left: 1px solid $lighter-gray; - - &:focus { - border: 1px solid $theme-primary; - } -} -.search-bar-div:focus-within { - border: 1px solid $gray; - cursor: pointer; - color: $black; - margin-top: -1px; - margin-bottom: -1px; -} -.search-input-parent { - display: flex; - flex-wrap: wrap; - padding-top: 4px; - margin-top: -3px; - - @media (min-width: 558px) { - min-width: 347px; - } - @media (max-width: 557px) { - width: 100% !important; - } - @media (max-width: 562px) and (min-width: 558px) { - min-width: 302px !important; - } - @media (max-width: 617px) and (min-width: 601px) { - min-width: 310px !important; - } - @media (max-width: 793px) and (min-width: 769px) { - min-width: 328px !important; - } -} -.pill-input-wrapper { - width: 100%; - padding: 3px 5px 3px 5px; - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 2px; -} -.remove-caret { - caret-color: transparent; -} -.search-input-dev { - width: 100%; - height: 27px; - padding: 0.45rem; - border-radius: 0 $radius $radius 0; - border: none; - outline: none; -} -.suggestion-box { - width: 100%; - top: 104%; - z-index: 5; - position: absolute; - text-align: left; - background-color: $white; -} -.empty-box { - padding: 5.3px; - text-align: center; - border: 1px solid $lighter-gray; - border-top: none; -} - -.search-button-container { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - width: 10rem; - @media (max-width: 768px) { - width: 100%; - margin-top: 1rem; - } - @media (max-width: 368px) { - width: 90%; - margin-top: 1rem; - } -} - -.search-button { - width: 100%; - height: 2rem; - border-radius: $radius; - background-color: $white; - color: $black; - font-weight: bold; - cursor: pointer; - border: 1px solid $gray; - outline: none; - - &:hover { - background-color: $theme-primary; - color: $white; - border: 1px solid $theme-primary; - } - - @media (max-width: 768px) { - width: 100%; - border-radius: $radius; - margin-left: 0; - } -} -.filter-modal-background { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - z-index: 3; -} - -.filter-modal { - position: relative; - top: 11.5rem; - left: -2.8rem; - min-width: 15rem; - background-color: $white; - border-radius: $radius; - border: 1px solid $light-gray; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - z-index: 3; - @media (max-width: 768px) { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 80%; - } -} -.filter-modal-dev { - top: 13.5rem; - - @media (max-width: 768px) { - top: 14.5rem; - left: 7.5rem; - } -} - -.filter-modal-title { - width: 100%; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid $gray; - padding: 0.5rem 0.5rem 0.5rem 1rem; - - span { - font-size: 1rem; - font-weight: bold; - } -} - -.status-filter { - width: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; - - @media (max-width: 768px) { - left: 0; - top: 0; - width: 100%; - height: 100%; - } - - .status-button { - width: 100%; - height: 2rem; - border: none; - outline: none; - text-align: start; - padding-left: 2rem; - cursor: pointer; - background-color: transparent; - border-bottom: 1px solid $light-gray; - border-left: none; - &:hover { - background-color: $light-gray; - } - } - - .status-button-active { - background-color: $light-gray; - border-left: 3px solid $theme-primary; - } -} - -.close { - cursor: pointer; - font-size: 1.5rem; -} diff --git a/src/components/tasks/TasksContent.tsx b/src/components/tasks/TasksContent.tsx index 85973face..4c430345b 100644 --- a/src/components/tasks/TasksContent.tsx +++ b/src/components/tasks/TasksContent.tsx @@ -1,14 +1,13 @@ import { ElementRef } from 'react'; import styles from '@/styles/tasks.module.scss'; import { useGetAllTasksQuery } from '@/app/services/tasksApi'; -import { TABS, Tab, TabTasksData } from '@/interfaces/task.type'; +import { Tab, TabTasksData } from '@/interfaces/task.type'; import { useState, useEffect, useRef } from 'react'; import { NO_TASKS_FOUND_MESSAGE, TASKS_FETCH_ERROR_MESSAGE, } from '../../constants/messages'; import { EMPTY_TASKS_DATA } from '@/constants/tasks'; -import { TabSection } from './TabSection'; import TaskList from './TaskList/TaskList'; import { useRouter } from 'next/router'; @@ -21,11 +20,8 @@ import { getQueryParamTitle, } from '@/utils/taskQueryParams'; -import { Select } from '../Select'; -import { getChangedStatusName } from '@/utils/getChangedStatusName'; import useIntersection from '@/hooks/useIntersection'; import TaskSearch from './TaskSearch/TaskSearch'; -import TaskSearchDev from './TaskSearchDev/TaskSearchDev'; export const TasksContent = ({ dev }: { dev?: boolean }) => { const router = useRouter(); @@ -80,10 +76,6 @@ export const TasksContent = ({ dev }: { dev?: boolean }) => { setNextTasks(''); }; - const searchInputHandler = (value: string) => { - setInputValue(value); - }; - const searchButtonHandler = (searchString?: string) => { const { status, assignees, title } = extractQueryParams( searchString || inputValue @@ -135,81 +127,20 @@ export const TasksContent = ({ dev }: { dev?: boolean }) => { earlyReturn: loadedTasks[selectedTab].length === 0, }); - const taskSelectOptions = TABS.map((item) => ({ - label: getChangedStatusName(item), - value: item, - })); - if (isLoading) return

Loading...

; if (isError) return

{TASKS_FETCH_ERROR_MESSAGE}

; return (
- {dev ? ( - - searchNewTasks(selectedTab, queryAssignees, queryTitle) - } - filterDropdownActiveTab={selectedTab} - inputValue={inputValue} - onClickSearchButton={searchButtonHandler} - /> - ) : ( - - searchNewTasks(selectedTab, queryAssignees, queryTitle) - } - inputValue={inputValue} - activeTab={selectedTab} - onInputChange={(value) => searchInputHandler(value)} - onClickSearchButton={searchButtonHandler} - /> - )} - {dev !== true ? ( - <> -
- - searchNewTasks( - status, - queryAssignees, - queryTitle - ) - } - activeTab={selectedTab} - /> -
-
-