diff --git a/src/CONST.ts b/src/CONST.ts index 826215398dcd..4d022a7a449c 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6419,6 +6419,7 @@ const CONST = { DATA_TYPES: { EXPENSE: 'expense', INVOICE: 'invoice', + TASK: 'task', TRIP: 'trip', CHAT: 'chat', }, @@ -6487,6 +6488,11 @@ const CONST = { LINKS: 'links', PINNED: 'pinned', }, + TASK: { + ALL: 'all', + OUTSTANDING: 'outstanding', + COMPLETED: 'completed', + }, }, TABLE_COLUMNS: { RECEIPT: 'receipt', @@ -6501,6 +6507,10 @@ const CONST = { TYPE: 'type', ACTION: 'action', TAX_AMOUNT: 'taxAmount', + TITLE: 'title', + ASSIGNEE: 'assignee', + CREATED_BY: 'createdBy', + IN: 'in', }, SYNTAX_OPERATORS: { AND: 'and', @@ -6541,6 +6551,9 @@ const CONST = { PAID: 'paid', EXPORTED: 'exported', POSTED: 'posted', + TITLE: 'title', + ASSIGNEE: 'assignee', + CREATED_BY: 'createdBy', REIMBURSABLE: 'reimbursable', BILLABLE: 'billable', POLICY_ID: 'policyID', @@ -6579,6 +6592,9 @@ const CONST = { PAID: 'paid', EXPORTED: 'exported', POSTED: 'posted', + TITLE: 'title', + ASSIGNEE: 'assignee', + CREATED_BY: 'created-by', REIMBURSABLE: 'reimbursable', BILLABLE: 'billable', }, diff --git a/src/ROUTES.ts b/src/ROUTES.ts index de567ef28fdc..84f6c30d9af9 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -68,6 +68,9 @@ const ROUTES = { SEARCH_ADVANCED_FILTERS_PAID: 'search/filters/paid', SEARCH_ADVANCED_FILTERS_EXPORTED: 'search/filters/exported', SEARCH_ADVANCED_FILTERS_POSTED: 'search/filters/posted', + SEARCH_ADVANCED_FILTERS_TITLE: 'search/filters/title', + SEARCH_ADVANCED_FILTERS_ASSIGNEE: 'search/filters/assignee', + SEARCH_ADVANCED_FILTERS_CREATED_BY: 'search/filters/createdBy', SEARCH_ADVANCED_FILTERS_REIMBURSABLE: 'search/filters/reimbursable', SEARCH_ADVANCED_FILTERS_BILLABLE: 'search/filters/billable', SEARCH_ADVANCED_FILTERS_WORKSPACE: 'search/filters/workspace', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 751589eab100..166821a84534 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -61,6 +61,9 @@ const SCREENS = { ADVANCED_FILTERS_TAG_RHP: 'Search_Advanced_Filters_Tag_RHP', ADVANCED_FILTERS_FROM_RHP: 'Search_Advanced_Filters_From_RHP', ADVANCED_FILTERS_TO_RHP: 'Search_Advanced_Filters_To_RHP', + ADVANCED_FILTERS_TITLE_RHP: 'Search_Advanced_Filters_Title_RHP', + ADVANCED_FILTERS_ASSIGNEE_RHP: 'Search_Advanced_Filters_Assignee_RHP', + ADVANCED_FILTERS_CREATED_BY_RHP: 'Search_Advanced_Filters_Created_By_RHP', ADVANCED_FILTERS_REIMBURSABLE_RHP: 'Search_Advanced_Filters_Reimbursable_RHP', ADVANCED_FILTERS_BILLABLE_RHP: 'Search_Advanced_Filters_Billable_RHP', ADVANCED_FILTERS_WORKSPACE_RHP: 'Search_Advanced_Filters_Workspace_RHP', diff --git a/src/components/Search/SearchAutocompleteList.tsx b/src/components/Search/SearchAutocompleteList.tsx index 4be2810d9d1a..ec8e6f5e4367 100644 --- a/src/components/Search/SearchAutocompleteList.tsx +++ b/src/components/Search/SearchAutocompleteList.tsx @@ -165,7 +165,13 @@ function SearchAutocompleteList( const typeAutocompleteList = Object.values(CONST.SEARCH.DATA_TYPES); const groupByAutocompleteList = Object.values(CONST.SEARCH.GROUP_BY); - const statusAutocompleteList = Object.values({...CONST.SEARCH.STATUS.EXPENSE, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}); + const statusAutocompleteList = Object.values({ + ...CONST.SEARCH.STATUS.EXPENSE, + ...CONST.SEARCH.STATUS.INVOICE, + ...CONST.SEARCH.STATUS.CHAT, + ...CONST.SEARCH.STATUS.TRIP, + ...CONST.SEARCH.STATUS.TASK, + }); const expenseTypes = Object.values(CONST.SEARCH.TRANSACTION_TYPE); const booleanTypes = Object.values(CONST.SEARCH.BOOLEAN); @@ -308,28 +314,21 @@ function SearchAutocompleteList( mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE, })); } + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CREATED_BY: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.ASSIGNEE: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO: case CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM: { - const filteredParticipants = getParticipantsAutocompleteList() - .filter((participant) => participant.name.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(participant.name.toLowerCase())) - .slice(0, 10); + const filterKey = autocompleteKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CREATED_BY ? CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.CREATED_BY : autocompleteKey; - return filteredParticipants.map((participant) => ({ - filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.FROM, - text: participant.name, - autocompleteID: participant.accountID, - mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, - })); - } - case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO: { const filteredParticipants = getParticipantsAutocompleteList() .filter((participant) => participant.name.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(participant.name.toLowerCase())) .slice(0, 10); return filteredParticipants.map((participant) => ({ - filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.TO, + filterKey, text: participant.name, autocompleteID: participant.accountID, - mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, + mapKey: autocompleteKey, })); } case CONST.SEARCH.SYNTAX_FILTER_KEYS.IN: { diff --git a/src/components/Search/SearchContext.tsx b/src/components/Search/SearchContext.tsx index db808e3b71f1..e593e8dc8d3f 100644 --- a/src/components/Search/SearchContext.tsx +++ b/src/components/Search/SearchContext.tsx @@ -1,5 +1,5 @@ import React, {useCallback, useContext, useMemo, useState} from 'react'; -import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; +import type {ReportActionListItemType, ReportListItemType, TaskListItemType, TransactionListItemType} from '@components/SelectionList/types'; import {isMoneyRequestReport} from '@libs/ReportUtils'; import {isReportListItemType, isTransactionListItemType} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; @@ -27,7 +27,10 @@ const defaultSearchContext: SearchContext = { const Context = React.createContext(defaultSearchContext); -function getReportsFromSelectedTransactions(data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[], selectedTransactions: SelectedTransactions) { +function getReportsFromSelectedTransactions( + data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[] | TaskListItemType[], + selectedTransactions: SelectedTransactions, +) { if (data.length === 0) { return []; } @@ -81,17 +84,20 @@ function SearchContextProvider({children}: ChildrenProps) { })); }, []); - const setSelectedTransactions = useCallback((selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[]) => { - // When selecting transactions, we also need to manage the reports to which these transactions belong. This is done to ensure proper exporting to CSV. - const selectedReports = getReportsFromSelectedTransactions(data, selectedTransactions); + const setSelectedTransactions = useCallback( + (selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[] | TaskListItemType[]) => { + // When selecting transactions, we also need to manage the reports to which these transactions belong. This is done to ensure proper exporting to CSV. + const selectedReports = getReportsFromSelectedTransactions(data, selectedTransactions); - setSearchContextData((prevState) => ({ - ...prevState, - selectedTransactions, - shouldTurnOffSelectionMode: false, - selectedReports, - })); - }, []); + setSearchContextData((prevState) => ({ + ...prevState, + selectedTransactions, + shouldTurnOffSelectionMode: false, + selectedReports, + })); + }, + [], + ); const clearSelectedTransactions = useCallback( (searchHash?: number, shouldTurnOffSelectionMode = false) => { diff --git a/src/components/Search/SearchList.tsx b/src/components/Search/SearchList.tsx index e9dd032cfdaf..ae8df77f663e 100644 --- a/src/components/Search/SearchList.tsx +++ b/src/components/Search/SearchList.tsx @@ -13,8 +13,9 @@ import Modal from '@components/Modal'; import {PressableWithFeedback} from '@components/Pressable'; import type ChatListItem from '@components/SelectionList/ChatListItem'; import type ReportListItem from '@components/SelectionList/Search/ReportListItem'; +import type TaskListItem from '@components/SelectionList/Search/TaskListItem'; import type TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; -import type {ExtendedTargetedEvent, ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; +import type {ExtendedTargetedEvent, ReportListItemType, SearchListItem} from '@components/SelectionList/types'; import Text from '@components/Text'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; @@ -31,8 +32,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -type SearchListItem = TransactionListItemType | ReportListItemType | ReportActionListItemType; -type SearchListItemComponentType = typeof TransactionListItem | typeof ChatListItem | typeof ReportListItem; +type SearchListItemComponentType = typeof TransactionListItem | typeof ChatListItem | typeof ReportListItem | typeof TaskListItem; type SearchListHandle = { scrollAndHighlightItem?: (items: string[]) => void; @@ -336,19 +336,27 @@ function SearchList( ], ); + const tableHeaderVisible = canSelectMultiple || !!SearchTableHeader; + const selectAllButtonVisible = canSelectMultiple && !SearchTableHeader; + return ( - {canSelectMultiple && ( + {tableHeaderVisible && ( - 0 && selectedItemsLength !== flattenedTransactions.length} - onPress={() => { - onAllCheckboxPress(); - }} - /> - {SearchTableHeader ?? ( + {canSelectMultiple && ( + 0 && selectedItemsLength !== flattenedTransactions.length} + onPress={() => { + onAllCheckboxPress(); + }} + /> + )} + + {SearchTableHeader} + + {selectAllButtonVisible && ( )} + = [ + { + type: CONST.SEARCH.DATA_TYPES.TASK, + status: CONST.SEARCH.STATUS.TASK.ALL, + icon: Expensicons.All, + text: 'common.all', + }, + { + type: CONST.SEARCH.DATA_TYPES.TASK, + status: CONST.SEARCH.STATUS.TASK.OUTSTANDING, + icon: Expensicons.Hourglass, + text: 'common.outstanding', + }, + { + type: CONST.SEARCH.DATA_TYPES.TASK, + status: CONST.SEARCH.STATUS.TASK.COMPLETED, + icon: Expensicons.Checkbox, + text: 'search.filters.completed', + }, +]; + function getOptions(type: SearchDataTypes, groupBy: SearchGroupBy | undefined) { switch (type) { case CONST.SEARCH.DATA_TYPES.INVOICE: @@ -206,6 +227,8 @@ function getOptions(type: SearchDataTypes, groupBy: SearchGroupBy | undefined) { return tripOptions; case CONST.SEARCH.DATA_TYPES.CHAT: return chatOptions; + case CONST.SEARCH.DATA_TYPES.TASK: + return taskOptions; case CONST.SEARCH.DATA_TYPES.EXPENSE: default: return groupBy === CONST.SEARCH.GROUP_BY.REPORTS ? expenseReportOptions : expenseOptions; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index a278e4b8142c..a8a3d2f87eff 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -6,7 +6,7 @@ import {useOnyx} from 'react-native-onyx'; import FullPageErrorView from '@components/BlockingViews/FullPageErrorView'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import SearchTableHeader from '@components/SelectionList/SearchTableHeader'; -import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; +import type {ReportActionListItemType, ReportListItemType, SearchListItem, TransactionListItemType} from '@components/SelectionList/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import useLocalize from '@hooks/useLocalize'; import useMobileSelectionMode from '@hooks/useMobileSelectionMode'; @@ -32,6 +32,7 @@ import { isReportListItemType, isSearchDataLoaded, isSearchResultsEmpty as isSearchResultsEmptyUtil, + isTaskListItemType, isTransactionListItemType, shouldShowEmptyState, shouldShowYear as shouldShowYearUtil, @@ -78,12 +79,14 @@ function mapToTransactionItemWithSelectionInfo(item: TransactionListItemType, se return {...item, shouldAnimateInHighlight, isSelected: selectedTransactions[item.keyForList]?.isSelected && canSelectMultiple}; } -function mapToItemWithSelectionInfo( - item: TransactionListItemType | ReportListItemType | ReportActionListItemType, - selectedTransactions: SelectedTransactions, - canSelectMultiple: boolean, - shouldAnimateInHighlight: boolean, -) { +function mapToItemWithSelectionInfo(item: SearchListItem, selectedTransactions: SelectedTransactions, canSelectMultiple: boolean, shouldAnimateInHighlight: boolean) { + if (isTaskListItemType(item)) { + return { + ...item, + shouldAnimateInHighlight, + }; + } + if (isReportActionListItemType(item)) { return { ...item, @@ -158,7 +161,6 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS const shouldGroupByReports = groupBy === CONST.SEARCH.GROUP_BY.REPORTS; const {canUseTableReportView} = usePermissions(); - const canSelectMultiple = isSmallScreenWidth ? !!selectionMode?.isEnabled : true; useEffect(() => { clearSelectedTransactions(hash); @@ -327,7 +329,7 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS }, [isFocused, data, searchResults?.search?.hasMoreResults, selectedTransactions, setExportMode, setShouldShowExportModeOption, shouldGroupByReports]); const openReport = useCallback( - (item: TransactionListItemType | ReportListItemType | ReportActionListItemType, isOpenedAsReport?: boolean) => { + (item: SearchListItem, isOpenedAsReport?: boolean) => { const isFromSelfDM = item.reportID === CONST.REPORT.UNREPORTED_REPORTID; const isTransactionItem = isTransactionListItemType(item); @@ -407,7 +409,11 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS const ListItem = getListItem(type, status, shouldGroupByReports); const sortedData = getSortedSections(type, status, data, sortBy, sortOrder, shouldGroupByReports); + const isChat = type === CONST.SEARCH.DATA_TYPES.CHAT; + const isTask = type === CONST.SEARCH.DATA_TYPES.TASK; + const canSelectMultiple = !isChat && !isTask && isLargeScreenWidth; + const sortedSelectedData = sortedData.map((item) => { const baseKey = isChat ? `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(item as ReportActionListItemType).reportActionID}` @@ -453,10 +459,13 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS ); } - const toggleTransaction = (item: TransactionListItemType | ReportListItemType | ReportActionListItemType) => { + const toggleTransaction = (item: SearchListItem) => { if (isReportActionListItemType(item)) { return; } + if (isTaskListItemType(item)) { + return; + } if (isTransactionListItemType(item)) { if (!item.keyForList) { return; @@ -518,6 +527,7 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS const shouldShowYear = shouldShowYearUtil(searchResults?.data); const shouldShowSorting = !Array.isArray(status) && !shouldGroupByReports; + const shouldShowTableHeader = isLargeScreenWidth && !isChat; return ( @@ -528,11 +538,12 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS onSelectRow={openReport} onCheckboxPress={toggleTransaction} onAllCheckboxPress={toggleAllTransactions} - canSelectMultiple={type !== CONST.SEARCH.DATA_TYPES.CHAT && canSelectMultiple} + canSelectMultiple={canSelectMultiple} shouldPreventLongPressRow={isChat} SearchTableHeader={ - !isLargeScreenWidth ? undefined : ( + !shouldShowTableHeader ? undefined : ( ; type InvoiceSearchStatus = ValueOf; type TripSearchStatus = ValueOf; type ChatSearchStatus = ValueOf; -type SearchStatus = ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus | ChatSearchStatus | Array; +type TaskSearchStatus = ValueOf; +type SearchStatus = + | ExpenseSearchStatus + | InvoiceSearchStatus + | TripSearchStatus + | ChatSearchStatus + | TaskSearchStatus + | Array; type SearchGroupBy = ValueOf; type TableColumnSize = ValueOf; @@ -66,7 +73,7 @@ type SearchContext = { selectedTransactions: SelectedTransactions; selectedReports: SelectedReports[]; setCurrentSearchHash: (hash: number) => void; - setSelectedTransactions: (selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[]) => void; + setSelectedTransactions: (selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[] | TaskListItemType[]) => void; clearSelectedTransactions: (hash?: number, shouldTurnOffSelectionMode?: boolean) => void; shouldTurnOffSelectionMode: boolean; shouldShowStatusBarLoading: boolean; @@ -167,6 +174,7 @@ export type { InvoiceSearchStatus, TripSearchStatus, ChatSearchStatus, + TaskSearchStatus, SearchAutocompleteResult, PaymentData, SearchAutocompleteQueryRange, diff --git a/src/components/SelectionList/Search/AvatarWithTextCell.tsx b/src/components/SelectionList/Search/AvatarWithTextCell.tsx new file mode 100644 index 000000000000..74329ed32bdb --- /dev/null +++ b/src/components/SelectionList/Search/AvatarWithTextCell.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import {View} from 'react-native'; +import Avatar from '@components/Avatar'; +import Text from '@components/Text'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import type {Icon} from '@src/types/onyx/OnyxCommon'; + +type AvatarWithTextCellProps = { + reportName?: string; + icon?: Icon; +}; + +function AvatarWithTextCell({reportName, icon}: AvatarWithTextCellProps) { + const styles = useThemeStyles(); + const {isLargeScreenWidth} = useResponsiveLayout(); + + if (!reportName || !icon) { + return null; + } + + return ( + + {!!icon && ( + + )} + + {!!reportName && ( + + {reportName} + + )} + + ); +} + +AvatarWithTextCell.displayName = 'ReportInfoCell'; + +export default AvatarWithTextCell; diff --git a/src/components/SelectionList/Search/DateCell.tsx b/src/components/SelectionList/Search/DateCell.tsx new file mode 100644 index 000000000000..98cf88b0a2de --- /dev/null +++ b/src/components/SelectionList/Search/DateCell.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import TextWithTooltip from '@components/TextWithTooltip'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import CONST from '@src/CONST'; + +type DateCellProps = { + created: string; + showTooltip: boolean; + isLargeScreenWidth: boolean; +}; + +function DateCell({created, showTooltip, isLargeScreenWidth}: DateCellProps) { + const styles = useThemeStyles(); + + const date = DateUtils.formatWithUTCTimeZone(created, DateUtils.doesDateBelongToAPastYear(created) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT); + + return ( + + ); +} + +DateCell.displayName = 'DateCell'; +export default DateCell; diff --git a/src/components/SelectionList/Search/TaskListItem.tsx b/src/components/SelectionList/Search/TaskListItem.tsx new file mode 100644 index 000000000000..6546e27d6cf4 --- /dev/null +++ b/src/components/SelectionList/Search/TaskListItem.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import BaseListItem from '@components/SelectionList/BaseListItem'; +import type {ListItem, TaskListItemProps, TaskListItemType} from '@components/SelectionList/types'; +import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; +import TaskListItemRow from './TaskListItemRow'; + +function TaskListItem({ + item, + isFocused, + showTooltip, + isDisabled, + canSelectMultiple, + onSelectRow, + onFocus, + onLongPressRow, + shouldSyncFocus, +}: TaskListItemProps) { + const taskItem = item as unknown as TaskListItemType; + const styles = useThemeStyles(); + const theme = useTheme(); + + const {isLargeScreenWidth} = useResponsiveLayout(); + + const listItemPressableStyle = [ + styles.selectionListPressableItemWrapper, + styles.pv3, + styles.ph3, + // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle + styles.bgTransparent, + item.isSelected && styles.activeComponentBG, + styles.mh0, + ]; + + const listItemWrapperStyle = [ + styles.flex1, + styles.userSelectNone, + isLargeScreenWidth ? {...styles.flexRow, ...styles.justifyContentBetween, ...styles.alignItemsCenter} : {...styles.flexColumn, ...styles.alignItemsStretch}, + ]; + + const animatedHighlightStyle = useAnimatedHighlightStyle({ + borderRadius: variables.componentBorderRadius, + shouldHighlight: item?.shouldAnimateInHighlight ?? false, + highlightColor: theme.messageHighlightBG, + backgroundColor: theme.highlightBG, + }); + + return ( + + + + ); +} + +TaskListItem.displayName = 'TaskListItem'; + +export default TaskListItem; diff --git a/src/components/SelectionList/Search/TaskListItemRow.tsx b/src/components/SelectionList/Search/TaskListItemRow.tsx new file mode 100644 index 000000000000..60e31d53c869 --- /dev/null +++ b/src/components/SelectionList/Search/TaskListItemRow.tsx @@ -0,0 +1,256 @@ +import React from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import Avatar from '@components/Avatar'; +import Badge from '@components/Badge'; +import Button from '@components/Button'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import {useSession} from '@components/OnyxProvider'; +import type {TaskListItemType} from '@components/SelectionList/types'; +import TextWithTooltip from '@components/TextWithTooltip'; +import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {callFunctionIfActionIsAllowed} from '@libs/actions/Session'; +import {canActionTask, completeTask} from '@libs/actions/Task'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; +import AvatarWithTextCell from './AvatarWithTextCell'; +import DateCell from './DateCell'; +import UserInfoCell from './UserInfoCell'; + +type TaskListItemRowProps = { + item: TaskListItemType; + showTooltip: boolean; + containerStyle?: StyleProp; +}; + +type CellProps = { + // eslint-disable-next-line react/no-unused-prop-types + showTooltip: boolean; + isLargeScreenWidth: boolean; +}; + +type TaskCellProps = { + taskItem: TaskListItemType; +} & CellProps; + +function TitleCell({taskItem, showTooltip, isLargeScreenWidth}: TaskCellProps) { + const styles = useThemeStyles(); + + return ( + + ); +} + +function DescriptionCell({taskItem, showTooltip, isLargeScreenWidth}: TaskCellProps) { + const styles = useThemeStyles(); + + return ( + + ); +} + +function ActionCell({taskItem, isLargeScreenWidth}: TaskCellProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const session = useSession(); + const {translate} = useLocalize(); + + const taskAssigneeID = taskItem.assignee.accountID; + const taskCreatorID = taskItem.createdBy.accountID; + const isTaskCompleted = taskItem.statusNum === CONST.REPORT.STATUS_NUM.APPROVED && taskItem.stateNum === CONST.REPORT.STATE_NUM.APPROVED; + + if (isTaskCompleted) { + return ( + + + + ); + } + + return ( +