diff --git a/src/components/AnimatedCollapsible/index.tsx b/src/components/AnimatedCollapsible/index.tsx index 4e5a4f9cb03de..d9ee8feb5647f 100644 --- a/src/components/AnimatedCollapsible/index.tsx +++ b/src/components/AnimatedCollapsible/index.tsx @@ -139,7 +139,7 @@ function AnimatedCollapsible({ & WithSentryLabel; -function DomainMenuItem({item, index}: DomainMenuItemProps) { +function DomainMenuItem({item, index, isLastItem}: DomainMenuItemProps) { const icons = useMemoizedLazyExpensifyIcons(['Globe']); const styles = useThemeStyles(); + const {isLargeScreenWidth} = useResponsiveLayout(); const {translate} = useLocalize(); const {isAdmin, isValidated, action} = item; @@ -79,7 +84,7 @@ function DomainMenuItem({item, index}: DomainMenuItemProps) { clearDomainErrors(item.accountID)} @@ -97,6 +102,7 @@ function DomainMenuItem({item, index}: DomainMenuItemProps) { isHovered={hovered} menuItems={threeDotsMenuItems} brickRoadIndicator={item.brickRoadIndicator} + isLastItem={isLastItem} /> )} diff --git a/src/components/Domain/DomainsListRow.tsx b/src/components/Domain/DomainsListRow.tsx index f3dd5068eb97a..6fabf82e511b1 100644 --- a/src/components/Domain/DomainsListRow.tsx +++ b/src/components/Domain/DomainsListRow.tsx @@ -7,6 +7,7 @@ import type {PopoverMenuItem} from '@components/PopoverMenu'; import TextWithTooltip from '@components/TextWithTooltip'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; @@ -26,16 +27,37 @@ type DomainsListRowProps = { /** The type of brick road indicator to show */ brickRoadIndicator?: ValueOf; + + /** Whether this is the last domain row in the list */ + isLastItem?: boolean; }; -function DomainsListRow({title, isHovered, badgeText, brickRoadIndicator, menuItems}: DomainsListRowProps) { +function DomainsListRow({title, isHovered, badgeText, brickRoadIndicator, menuItems, isLastItem}: DomainsListRowProps) { const styles = useThemeStyles(); + const {isLargeScreenWidth} = useResponsiveLayout(); const theme = useTheme(); const icons = useMemoizedLazyExpensifyIcons(['Globe', 'ArrowRight', 'DotIndicator']); return ( - + (shouldUseNarrowLayout ? {height: shouldShowCheckbox ? MOBILE_HEIGHT_WITH_CHECKBOX : MOBILE_HEIGHT_WITHOUT_CHECKBOX} : {height: DESKTOP_HEIGHT, minHeight: DESKTOP_HEIGHT}), + () => (shouldUseNarrowLayout ? {height: shouldShowCheckbox ? MOBILE_HEIGHT_WITH_CHECKBOX : MOBILE_HEIGHT_WITHOUT_CHECKBOX} : {minHeight: 40}), [shouldUseNarrowLayout, shouldShowCheckbox], ); @@ -91,7 +95,7 @@ function MoneyRequestReportGroupHeader({ return ( - + {shouldShowCheckbox && ( canUseTouchScreen() && ControlSelection.block()} @@ -137,7 +144,12 @@ function MoneyRequestReportTransactionItem({ }} disabled={isTransactionPendingDelete(transaction)} ref={viewRef} - wrapperStyle={[animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[ + animatedHighlightStyle, + styles.userSelectNone, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: theme.border}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} > {({hovered}) => ( { handleOnPress(transaction.transactionID); }} diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index 1239c32f21e84..67f1e63afad66 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -30,7 +30,7 @@ import useOnyx from '@hooks/useOnyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useResponsiveLayoutOnWideRHP from '@hooks/useResponsiveLayoutOnWideRHP'; -import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; @@ -59,7 +59,6 @@ import {getAmount, getCategory, getCreated, getMerchant, getTag, getTransactionP import shouldShowTransactionYear from '@libs/TransactionUtils/shouldShowTransactionYear'; import Navigation from '@navigation/Navigation'; import type {ReportsSplitNavigatorParamList} from '@navigation/types'; -import variables from '@styles/variables'; import {createTransactionThreadReport} from '@userActions/Report'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -165,11 +164,11 @@ function MoneyRequestReportTransactionList({ }: MoneyRequestReportTransactionListProps) { useCopySelectionHelper(); const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); + const theme = useTheme(); const expensifyIcons = useMemoizedLazyExpensifyIcons(['Location', 'CheckSquare', 'ReceiptPlus', 'Columns']); const {translate, localeCompare} = useLocalize(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {isSmallScreenWidth, isMediumScreenWidth} = useResponsiveLayout(); + const {isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); const {shouldUseNarrowLayout} = useResponsiveLayoutOnWideRHP(); const {markReportIDAsExpense} = useWideRHPActions(); const [isModalVisible, setIsModalVisible] = useState(false); @@ -528,22 +527,23 @@ function MoneyRequestReportTransactionList({ const transactionListContent = ( {shouldShowGroupedTransactions - ? groupedTransactions.map((group) => { + ? groupedTransactions.map((group, groupIndex) => { const selectionState = groupSelectionState.get(group.groupKey) ?? { isSelected: false, isIndeterminate: false, isDisabled: false, pendingAction: undefined, }; + const isLastGroup = groupIndex === groupedTransactions.length - 1; return ( - {group.transactions.map((transaction) => { + {group.transactions.map((transaction, transactionIndex) => { return ( ); })} ); }) - : sortedTransactions.map((transaction) => ( + : sortedTransactions.map((transaction, index) => ( ))} @@ -607,8 +609,22 @@ function MoneyRequestReportTransactionList({ const tableHeaderContent = ( - - + + { if (selectedTransactionIDs.length !== 0) { diff --git a/src/components/ReportActionAvatars/SearchReportAvatar.tsx b/src/components/ReportActionAvatars/SearchReportAvatar.tsx index 89a89d8394aab..51ea2c2403b05 100644 --- a/src/components/ReportActionAvatars/SearchReportAvatar.tsx +++ b/src/components/ReportActionAvatars/SearchReportAvatar.tsx @@ -24,7 +24,7 @@ function SearchReportAvatar({primaryAvatar, secondaryAvatar, avatarType, shouldS {tableHeaderVisible && ( - + {canSelectMultiple && ( + {shouldShowSelectedDropdown ? ( ) : ( diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 1969beccbbe0b..416770bffb7be 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -342,6 +342,7 @@ function BaseSelectionList({ index={index} isFocused={isItemFocused} isDisabled={isItemDisabled} + isLastItem={index === data.length - 1} canSelectMultiple={canSelectMultiple} onDismissError={onDismissError} onLongPressRow={onLongPressRow} diff --git a/src/components/SelectionList/ListItem/ListItemRenderer.tsx b/src/components/SelectionList/ListItem/ListItemRenderer.tsx index 35ee72e3d89ce..0a53fda0b6337 100644 --- a/src/components/SelectionList/ListItem/ListItemRenderer.tsx +++ b/src/components/SelectionList/ListItem/ListItemRenderer.tsx @@ -53,6 +53,7 @@ function ListItemRenderer({ shouldDisableHoverStyle, shouldShowRightCaret, errorRowStyles, + isLastItem, }: ListItemRendererProps) { const handleOnCheckboxPress = () => { if (isTransactionGroupListItemType(item)) { @@ -110,6 +111,7 @@ function ListItemRenderer({ shouldHighlightSelectedItem={shouldHighlightSelectedItem} shouldDisableHoverStyle={shouldDisableHoverStyle} shouldShowRightCaret={shouldShowRightCaret} + isLastItem={isLastItem} /> {item.footerContent && item.footerContent} diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index ec40a586e2738..098714b22669a 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -4,9 +4,11 @@ import Checkbox from '@components/Checkbox'; import ReportActionAvatars from '@components/ReportActionAvatars'; import TextWithTooltip from '@components/TextWithTooltip'; import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; import BaseListItem from './BaseListItem'; import type {ListItem, TableListItemProps} from './types'; @@ -27,10 +29,12 @@ function TableListItem({ shouldUseDefaultRightHandSideCheckmark, shouldShowRightCaret, errorRowStyles, + isLastItem, }: TableListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); + const {isLargeScreenWidth} = useResponsiveLayout(); const animatedHighlightStyle = useAnimatedHighlightStyle({ borderRadius: styles.selectionListPressableItemWrapper.borderRadius, @@ -56,14 +60,24 @@ function TableListItem({ pressableStyle={[ styles.selectionListPressableItemWrapper, styles.mh0, + isLargeScreenWidth && [ + styles.ph3, + styles.pv2, + {minHeight: variables.tableRowHeight, borderRadius: 0, ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {})}, + ], // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle item.shouldAnimateInHighlight ? styles.bgTransparent : undefined, item.isSelected && styles.activeComponentBG, item.cursorStyle, ]} - pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} + pressableWrapperStyle={[ + styles.mh5, + animatedHighlightStyle, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} wrapperStyle={[styles.flexRow, styles.flex1, styles.justifyContentBetween, styles.userSelectNone, styles.alignItemsCenter]} - containerStyle={styles.mb2} + containerStyle={!isLargeScreenWidth ? styles.mb2 : undefined} isFocused={isFocused} isDisabled={isDisabled} showTooltip={showTooltip} diff --git a/src/components/SelectionList/ListItem/types.ts b/src/components/SelectionList/ListItem/types.ts index a770364c203a7..4b917328e2311 100644 --- a/src/components/SelectionList/ListItem/types.ts +++ b/src/components/SelectionList/ListItem/types.ts @@ -217,6 +217,9 @@ type CommonListItemProps = { /** Whether product training tooltips can be displayed */ canShowProductTrainingTooltip?: boolean; + + /** Whether this is the last item in the list (used for bottom border-radius styling on desktop) */ + isLastItem?: boolean; } & TRightHandSideComponent & WithSentryLabel; diff --git a/src/components/SelectionList/components/ListHeader.tsx b/src/components/SelectionList/components/ListHeader.tsx index 976746329dd97..61b17ce129372 100644 --- a/src/components/SelectionList/components/ListHeader.tsx +++ b/src/components/SelectionList/components/ListHeader.tsx @@ -59,7 +59,16 @@ function ListHeader({ return ( diff --git a/src/components/SelectionListWithModal/CustomListHeader.tsx b/src/components/SelectionListWithModal/CustomListHeader.tsx index 1bfc65cb142fd..2077e18cd1294 100644 --- a/src/components/SelectionListWithModal/CustomListHeader.tsx +++ b/src/components/SelectionListWithModal/CustomListHeader.tsx @@ -4,6 +4,7 @@ import {View} from 'react-native'; import Text from '@components/Text'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; type CustomListHeaderProps = { @@ -30,7 +31,8 @@ function CustomListHeader({ }: CustomListHeaderProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); + const theme = useTheme(); + const {isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const header = ( {header}; + return ( + + {header} + + ); } export default CustomListHeader; diff --git a/src/components/SelectionListWithModal/index.tsx b/src/components/SelectionListWithModal/index.tsx index 27170e170a784..27dd52f8652fb 100644 --- a/src/components/SelectionListWithModal/index.tsx +++ b/src/components/SelectionListWithModal/index.tsx @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useMobileSelectionMode from '@hooks/useMobileSelectionMode'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import CONST from '@src/CONST'; @@ -39,10 +40,11 @@ function SelectionListWithModal({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); const styles = useThemeStyles(); + const theme = useTheme(); // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout here because there is a race condition that causes shouldUseNarrowLayout to change indefinitely in this component // See https://github.com/Expensify/App/issues/48675 for more details // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {isSmallScreenWidth} = useResponsiveLayout(); + const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); const isFocused = useIsFocused(); const icons = useMemoizedLazyExpensifyIcons(['CheckSquare'] as const); @@ -81,9 +83,25 @@ function SelectionListWithModal({ useHandleSelectionMode(selectedItems); + const desktopHeaderStyle = isLargeScreenWidth + ? { + marginHorizontal: 20, + paddingLeft: 12, + paddingRight: 8, + paddingTop: 8, + paddingBottom: 8, + minHeight: 40, + borderBottomWidth: 1, + borderColor: theme.border, + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + backgroundColor: theme.highlightBG, + } + : undefined; + const style = { ...styleProp, - listHeaderWrapperStyle: [styles.baseListHeaderWrapperStyle, styleProp?.listHeaderWrapperStyle], + listHeaderWrapperStyle: [isLargeScreenWidth ? desktopHeaderStyle : styles.baseListHeaderWrapperStyle, styleProp?.listHeaderWrapperStyle], }; const handleLongPressRow = (item: TItem) => { diff --git a/src/components/SelectionListWithSections/Search/BaseListItemHeader.tsx b/src/components/SelectionListWithSections/Search/BaseListItemHeader.tsx index dab3578dc7023..b76d7a68ed6a5 100644 --- a/src/components/SelectionListWithSections/Search/BaseListItemHeader.tsx +++ b/src/components/SelectionListWithSections/Search/BaseListItemHeader.tsx @@ -139,16 +139,18 @@ function BaseListItemHeader({ return ( - + {!!canSelectMultiple && ( onCheckboxPress?.(item as unknown as TItem)} isChecked={isSelectAllChecked} isIndeterminate={isIndeterminate} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20)]} disabled={!!isDisabled || item.isDisabledCheckbox} accessibilityLabel={translate('common.select')} - style={isLargeScreenWidth && styles.mr1} + shouldStopMouseDownPropagation + style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle()]} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_SELECT_ALL_CHECKBOX} /> )} diff --git a/src/components/SelectionListWithSections/Search/CardListItem.tsx b/src/components/SelectionListWithSections/Search/CardListItem.tsx index 402525d7e12ee..4bb9b6686e409 100644 --- a/src/components/SelectionListWithSections/Search/CardListItem.tsx +++ b/src/components/SelectionListWithSections/Search/CardListItem.tsx @@ -102,7 +102,8 @@ function CardListItem({ > ({ @@ -93,8 +93,8 @@ function CardListItemHeader({ > ), @@ -121,16 +121,18 @@ function CardListItemHeader({ return ( - + {!!canSelectMultiple && ( onCheckboxPress?.(cardItem as unknown as TItem)} isChecked={isSelectAllChecked} isIndeterminate={isIndeterminate} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20)]} disabled={!!isDisabled || cardItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} - style={isLargeScreenWidth && styles.mr1} + shouldStopMouseDownPropagation + style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle()]} /> )} {!isLargeScreenWidth && ( @@ -140,6 +142,7 @@ function CardListItemHeader({ subscriptAvatarBorderColor={backgroundColor} noRightMarginOnSubscriptContainer accountIDs={[cardItem.accountID]} + size={CONST.AVATAR_SIZE.SMALL} /> ({ /> @@ -164,6 +167,7 @@ function CardListItemHeader({ subscriptAvatarBorderColor={backgroundColor} noRightMarginOnSubscriptContainer accountIDs={[cardItem.accountID]} + size={CONST.AVATAR_SIZE.SMALL} /> diff --git a/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx b/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx index 106453b5b9338..3390e72cd0144 100644 --- a/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx +++ b/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx @@ -47,6 +47,7 @@ function ExpenseReportListItem({ isDEWBetaEnabled, lastPaymentMethod, personalPolicyID, + isLastItem, }: ExpenseReportListItemProps) { const reportItem = item as unknown as ExpenseReportListItemType; const styles = useThemeStyles(); @@ -161,14 +162,15 @@ function ExpenseReportListItem({ const listItemPressableStyle = useMemo( () => [ styles.selectionListPressableItemWrapper, - styles.pv3, + isLargeScreenWidth ? styles.pv2 : styles.pv3, styles.ph3, + isLargeScreenWidth && {minHeight: variables.tableRowHeight, borderRadius: 0, ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {})}, // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, ], - [styles, item.isSelected], + [styles, item.isSelected, isLargeScreenWidth, isLastItem], ); const listItemWrapperStyle = useMemo( @@ -181,7 +183,7 @@ function ExpenseReportListItem({ ); const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -231,7 +233,7 @@ function ExpenseReportListItem({ item={item} pressableStyle={listItemPressableStyle} wrapperStyle={listItemWrapperStyle} - containerStyle={[styles.mb2]} + containerStyle={!isLargeScreenWidth ? [styles.mb2] : undefined} isFocused={isFocused} showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} @@ -242,7 +244,12 @@ function ExpenseReportListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} + pressableWrapperStyle={[ + styles.mh5, + animatedHighlightStyle, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} accessible={false} shouldShowRightCaret={false} shouldUseDefaultRightHandSideCheckmark={false} diff --git a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx index 63a42d1d59513..fe37bb4690b13 100644 --- a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx +++ b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx @@ -153,7 +153,7 @@ function ExpenseReportListItemRow({ ), [CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: ( - + )} diff --git a/src/components/SelectionListWithSections/Search/MemberListItemHeader.tsx b/src/components/SelectionListWithSections/Search/MemberListItemHeader.tsx index 8827f31ae807e..4f50cef9f933f 100644 --- a/src/components/SelectionListWithSections/Search/MemberListItemHeader.tsx +++ b/src/components/SelectionListWithSections/Search/MemberListItemHeader.tsx @@ -104,16 +104,18 @@ function MemberListItemHeader({ return ( - + {!!canSelectMultiple && ( onCheckboxPress?.(memberItem as unknown as TItem)} isChecked={isSelectAllChecked} isIndeterminate={isIndeterminate} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20)]} disabled={!!isDisabled || memberItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} - style={isLargeScreenWidth && styles.mr1} + shouldStopMouseDownPropagation + style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle()]} /> )} {!isLargeScreenWidth && ( @@ -125,6 +127,7 @@ function MemberListItemHeader({ type={CONST.ICON_TYPE_AVATAR} name={formattedDisplayName} avatarID={memberItem.accountID} + size={CONST.AVATAR_SIZE.SMALL} /> @@ -150,6 +153,7 @@ function MemberListItemHeader({ type={CONST.ICON_TYPE_AVATAR} name={formattedDisplayName} avatarID={memberItem.accountID} + size={CONST.AVATAR_SIZE.SMALL} /> diff --git a/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx b/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx index 33d1927cf60d8..9423339d41f98 100644 --- a/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx +++ b/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx @@ -133,7 +133,7 @@ function HeaderFirstRow({ const currency = reportItem.currency ?? CONST.CURRENCY.USD; return ( - + {!!canSelectMultiple && ( ({ }); }; return !isLargeScreenWidth ? ( - + ({ const listItemPressableStyle = [ styles.selectionListPressableItemWrapper, - styles.pv3, + isLargeScreenWidth ? styles.pv2 : styles.pv3, styles.ph3, + isLargeScreenWidth && {minHeight: variables.tableRowHeight, borderRadius: 0}, // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle styles.bgTransparent, item.isSelected && styles.activeComponentBG, @@ -46,7 +47,7 @@ function TaskListItem({ ]; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -59,7 +60,7 @@ function TaskListItem({ item={item} pressableStyle={listItemPressableStyle} wrapperStyle={listItemWrapperStyle} - containerStyle={[styles.mb2]} + containerStyle={!isLargeScreenWidth ? [styles.mb2] : undefined} isFocused={isFocused} isDisabled={isDisabled} showTooltip={showTooltip} @@ -71,7 +72,11 @@ function TaskListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} + pressableWrapperStyle={[ + styles.mh5, + animatedHighlightStyle, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border}, + ]} forwardedFSClass={fsClass} > ); } @@ -63,8 +63,8 @@ function DescriptionCell({taskItem, showTooltip, isLargeScreenWidth}: TaskCellPr ); } @@ -154,7 +154,7 @@ function TaskListItemRow({item, containerStyle, showTooltip}: TaskListItemRowPro - + ); } diff --git a/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx b/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx index 7cb5be7d361d9..a5a9a4d191eea 100644 --- a/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx +++ b/src/components/SelectionListWithSections/Search/TransactionGroupListExpanded.tsx @@ -207,7 +207,7 @@ function TransactionGroupListExpanded({ )} {visibleTransactions.map((transaction, index) => { - const shouldShowBottomBorder = !isLastTransaction(index) && !isLargeScreenWidth; + const shouldShowBottomBorder = !isLastTransaction(index); const exportedReportActions = Object.values(transactionsSnapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction?.reportID}`] ?? {}); const transactionRow = ( @@ -229,7 +229,13 @@ function TransactionGroupListExpanded({ onButtonPress={() => { openReportInRHP(transaction); }} - style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv1Half], styles.flex1]} + style={[ + styles.noBorderRadius, + styles.p3, + isLargeScreenWidth && styles.pv1, + styles.flex1, + isLargeScreenWidth && isLastTransaction(index) && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} isReportItemChild isInSingleTransactionReport={isInSingleTransactionReport} shouldShowBottomBorder={shouldShowBottomBorder} diff --git a/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx b/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx index 69ead1923bc19..6941b12bda3bb 100644 --- a/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx +++ b/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx @@ -79,6 +79,7 @@ function TransactionGroupListItem({ isDEWBetaEnabled, lastPaymentMethod, personalPolicyID, + isLastItem, }: TransactionGroupListItemProps) { const groupItem = item as unknown as TransactionGroupListItemType; @@ -176,7 +177,7 @@ function TransactionGroupListItem({ }; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -184,7 +185,11 @@ function TransactionGroupListItem({ const isItemSelected = isSelectAllChecked || item?.isSelected; - const pressableStyle = [styles.transactionGroupListItemStyle, isItemSelected && styles.activeComponentBG]; + const pressableStyle = [ + styles.transactionGroupListItemStyle, + isLargeScreenWidth && {minHeight: variables.tableRowHeight, borderRadius: 0, ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {})}, + isItemSelected && styles.activeComponentBG, + ]; const StyleUtils = useStyleUtils(); const pressableRef = useRef(null); @@ -470,7 +475,14 @@ function TransactionGroupListItem({ isFocused && StyleUtils.getItemBackgroundColorStyle(!!isItemSelected, !!isFocused, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG), ]} onFocus={onFocus} - wrapperStyle={[styles.mb2, styles.mh5, animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[ + !isLargeScreenWidth && styles.mb2, + styles.mh5, + animatedHighlightStyle, + styles.userSelectNone, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} > {({hovered}) => ( @@ -478,7 +490,7 @@ function TransactionGroupListItem({ isExpanded={isExpanded} header={getHeader(hovered)} onPress={onExpandIconPress} - expandButtonStyle={styles.pv4Half} + expandButtonStyle={styles.pv2} shouldShowToggleButton={isLargeScreenWidth} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_EXPAND_TOGGLE} > diff --git a/src/components/SelectionListWithSections/Search/TransactionListItem.tsx b/src/components/SelectionListWithSections/Search/TransactionListItem.tsx index a66ddcae04897..5a7612c7d0612 100644 --- a/src/components/SelectionListWithSections/Search/TransactionListItem.tsx +++ b/src/components/SelectionListWithSections/Search/TransactionListItem.tsx @@ -52,6 +52,7 @@ function TransactionListItem({ isDEWBetaEnabled, lastPaymentMethod, personalPolicyID, + isLastItem, }: TransactionListItemProps) { const transactionItem = item as unknown as TransactionListItemType; const styles = useThemeStyles(); @@ -100,12 +101,13 @@ function TransactionListItem({ const pressableStyle = [ styles.transactionListItemStyle, !isLargeScreenWidth && styles.pt3, + isLargeScreenWidth && {minHeight: variables.tableRowHeight, borderRadius: 0, ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {})}, item.isSelected && styles.activeComponentBG, isLargeScreenWidth ? {...styles.flexRow, ...styles.justifyContentBetween, ...styles.alignItemsCenter} : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -194,7 +196,15 @@ function TransactionListItem({ isFocused && StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, !!isFocused, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG), ]} onFocus={onFocus} - wrapperStyle={[styles.mb2, styles.mh5, styles.flex1, animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[ + !isLargeScreenWidth && styles.mb2, + styles.mh5, + styles.flex1, + animatedHighlightStyle, + styles.userSelectNone, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} > {({hovered}) => ( <> @@ -228,7 +238,13 @@ function TransactionListItem({ taxAmountColumnSize={taxAmountColumnSize} shouldShowCheckbox={!!canSelectMultiple} checkboxSentryLabel={CONST.SENTRY_LABEL.SEARCH.TRANSACTION_LIST_ITEM_CHECKBOX} - style={[styles.p3, styles.pv2, shouldUseNarrowLayout ? styles.pt2 : {}]} + style={[ + styles.p3, + isLargeScreenWidth && styles.noBorderRadius, + styles.pv2, + shouldUseNarrowLayout ? styles.pt2 : {}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} violations={transactionViolations} onArrowRightPress={() => onSelectRow(item, transactionPreviewData)} isHover={hovered} diff --git a/src/components/SelectionListWithSections/Search/UserInfoAndActionButtonRow.tsx b/src/components/SelectionListWithSections/Search/UserInfoAndActionButtonRow.tsx index dee7b599a76f3..858c0d2803d3e 100644 --- a/src/components/SelectionListWithSections/Search/UserInfoAndActionButtonRow.tsx +++ b/src/components/SelectionListWithSections/Search/UserInfoAndActionButtonRow.tsx @@ -64,7 +64,7 @@ function UserInfoAndActionButtonRow({ shouldUseArrowIcon={false} /> )} - + ({ return ( - + {!!canSelectMultiple && ( onCheckboxPress?.(withdrawalIDItem as unknown as TItem)} isChecked={isSelectAllChecked} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20)]} disabled={!!isDisabled || withdrawalIDItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} isIndeterminate={isIndeterminate} + shouldStopMouseDownPropagation + style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle()]} /> )} {!isLargeScreenWidth && ( diff --git a/src/components/SelectionListWithSections/SearchTableHeader.tsx b/src/components/SelectionListWithSections/SearchTableHeader.tsx index 7d05263b54a09..bd2f43b543ec0 100644 --- a/src/components/SelectionListWithSections/SearchTableHeader.tsx +++ b/src/components/SelectionListWithSections/SearchTableHeader.tsx @@ -529,10 +529,10 @@ function SearchTableHeader({ shouldShowSorting={shouldShowSorting} sortBy={sortBy} sortOrder={sortOrder} - // In GroupBy views, disable flex expansion for Total columns so Expenses column gets more space - shouldRemoveTotalColumnFlex={!!groupBy && !isExpenseReportView} + // Disable flex expansion for Total columns in GroupBy views and Reports > Reports page + shouldRemoveTotalColumnFlex={(!!groupBy && !isExpenseReportView) || type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT} // Don't butt up against the 'select all' checkbox if present - containerStyles={canSelectMultiple && [styles.pl4]} + containerStyles={canSelectMultiple && [styles.pl3]} onSortPress={(columnName, order) => { if (columnName === CONST.SEARCH.TABLE_COLUMNS.COMMENTS) { return; diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts index 2bdd055f947b9..6273dbd260f6d 100644 --- a/src/components/SelectionListWithSections/types.ts +++ b/src/components/SelectionListWithSections/types.ts @@ -126,6 +126,9 @@ type CommonListItemProps = { /** Accessibility role for the list item (e.g. 'checkbox' for multi-select options so screen readers announce checked state) */ accessibilityRole?: Role; + + /** Whether this is the last item in the list (used for bottom border-radius styling on desktop) */ + isLastItem?: boolean; } & TRightHandSideComponent; type ListItemFocusEventHandler = (event: NativeSyntheticEvent) => void; diff --git a/src/components/Table/TableHeader.tsx b/src/components/Table/TableHeader.tsx index 093a9000ea642..6ca2815c309fc 100644 --- a/src/components/Table/TableHeader.tsx +++ b/src/components/Table/TableHeader.tsx @@ -50,6 +50,7 @@ type TableHeaderProps = ViewProps & { */ function TableHeader({style, shouldHideHeaderWhenEmptySearch = true, ...props}: TableHeaderProps) { const styles = useThemeStyles(); + const theme = useTheme(); const {columns, isEmptyResult} = useTableContext(); if (shouldHideHeaderWhenEmptySearch && isEmptyResult) { @@ -59,9 +60,11 @@ function TableHeader({style, shouldHideHea return ( ({column}: {colu accessible accessibilityLabel={column.label} accessibilityRole="button" + sentryLabel={`TableHeader-${column.key}`} style={[styles.flexRow, styles.alignItemsCenter, column.styling?.flex ? {flex: column.styling.flex} : styles.flex1, column.styling?.containerStyles]} onPress={() => toggleSorting(column.key)} > diff --git a/src/components/TransactionItemRow/DataCells/CategoryCell.tsx b/src/components/TransactionItemRow/DataCells/CategoryCell.tsx index 1eed41e4076b3..7263829d1016c 100644 --- a/src/components/TransactionItemRow/DataCells/CategoryCell.tsx +++ b/src/components/TransactionItemRow/DataCells/CategoryCell.tsx @@ -23,8 +23,8 @@ function CategoryCell({shouldUseNarrowLayout, shouldShowTooltip, transactionItem ); } diff --git a/src/components/TransactionItemRow/DataCells/MerchantCell.tsx b/src/components/TransactionItemRow/DataCells/MerchantCell.tsx index 7aec8d8fdfda4..00f59eaf95c10 100644 --- a/src/components/TransactionItemRow/DataCells/MerchantCell.tsx +++ b/src/components/TransactionItemRow/DataCells/MerchantCell.tsx @@ -27,8 +27,8 @@ function MerchantOrDescriptionCell({ ); } diff --git a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx index b833a4ec6d923..3d38388fefe1b 100644 --- a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx +++ b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx @@ -56,7 +56,7 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra return ( ); } diff --git a/src/components/TransactionItemRow/DataCells/TypeCell.tsx b/src/components/TransactionItemRow/DataCells/TypeCell.tsx index 03d9ff0cf7690..c93aebd28477b 100644 --- a/src/components/TransactionItemRow/DataCells/TypeCell.tsx +++ b/src/components/TransactionItemRow/DataCells/TypeCell.tsx @@ -83,8 +83,8 @@ function TypeCell({transactionItem, shouldUseNarrowLayout, shouldShowTooltip}: T diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index d56c367ad600e..59c90ef3767e9 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -780,7 +780,6 @@ function TransactionItemRow({ }} accessibilityLabel={CONST.ROLE.CHECKBOX} isChecked={isSelected} - style={styles.mr1} wrapperStyle={styles.justifyContentCenter} sentryLabel={checkboxSentryLabel} /> @@ -801,7 +800,7 @@ function TransactionItemRow({ onArrowRightPress?.()} - style={[styles.p3Half, styles.pl0half, styles.pr0half, styles.justifyContentCenter, styles.alignItemsEnd]} + style={[styles.pv2, styles.justifyContentCenter, styles.alignItemsEnd]} accessibilityRole={CONST.ROLE.BUTTON} accessibilityLabel={CONST.ROLE.BUTTON} sentryLabel={CONST.SENTRY_LABEL.TRANSACTION_ITEM_ROW.ARROW_RIGHT} diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index e74c18b428dc7..a2cb453b35204 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1492,18 +1492,10 @@ function shouldShowYear( if (exportedAction?.created && DateUtils.doesDateBelongToAPastYear(exportedAction.created)) { result.shouldShowYearExported = true; } - } else if (!checkOnlyReports && isReportActionEntry(key)) { - const item = data[key]; - for (const action of Object.values(item)) { - const date = action.created; - if (DateUtils.doesDateBelongToAPastYear(date)) { - result.shouldShowYearCreated = true; - } - } } else if (isReportEntry(key)) { const item = data[key]; - if (item.created && DateUtils.doesDateBelongToAPastYear(item.created)) { + if (checkOnlyReports && item.created && DateUtils.doesDateBelongToAPastYear(item.created)) { result.shouldShowYearCreated = true; } if (item.submitted && DateUtils.doesDateBelongToAPastYear(item.submitted)) { diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx index 1ea3f2d97bed8..26659d6bebb0e 100644 --- a/src/pages/domain/BaseDomainMembersPage.tsx +++ b/src/pages/domain/BaseDomainMembersPage.tsx @@ -274,7 +274,6 @@ function BaseDomainMembersPage({ shouldShowRightCaret style={{ containerStyle: styles.flex1, - listHeaderWrapperStyle: styles.baseListHeaderWrapperStyle, listItemTitleContainerStyles: shouldUseNarrowLayout ? undefined : styles.pr3, listItemErrorRowStyles: [styles.ph4, styles.pb2], contentContainerStyle: shouldShowEmptyPreFilterState ? [styles.flex1, styles.mh100] : undefined, diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index bc0c489336974..d7dd0486d2bd8 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -575,7 +575,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers // Show 4 columns only on wide screens for control policies if (isControlPolicyWithWideLayout) { const header = ( - + {translate('common.member')} diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index c197ae7882b5d..35cf6f887256c 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -326,6 +326,103 @@ function WorkspacesListPage() { ); }; + const navigateToWorkspace = (policyID: string) => { + if (shouldUseNarrowLayout) { + Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID)); + return; + } + Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(policyID)); + }; + + const navigateToDomain = ({domainAccountID, isAdmin}: {domainAccountID: number; isAdmin: boolean}) => { + if (!isAdmin) { + return Navigation.navigate(ROUTES.WORKSPACES_DOMAIN_ACCESS_RESTRICTED.getRoute(domainAccountID)); + } + Navigation.navigate(ROUTES.DOMAIN_INITIAL.getRoute(domainAccountID)); + }; + + const {policiesWithCardFeedErrors} = usePoliciesWithCardFeedErrors(); + + /** + * Add free policies (workspaces) to the list of menu items and returns the list of menu items + */ + const workspaces: WorkspaceItem[] = []; + if (!isEmptyObject(policies)) { + const reimbursementAccountBrickRoadIndicator = !isEmptyObject(reimbursementAccount?.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined; + + for (const policy of Object.values(policies)) { + if (!policy || !shouldShowPolicy(policy, true, session?.email)) { + continue; + } + + const receiptUberBrickRoadIndicator = getUberConnectionErrorDirectlyFromPolicy(policy as OnyxEntry) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined; + + let brickRoadIndicator: ValueOf | undefined; + if (isPolicyAdmin(policy, session?.email)) { + const indicator = reimbursementAccountBrickRoadIndicator ?? receiptUberBrickRoadIndicator; + + if (indicator) { + brickRoadIndicator = indicator; + } else if (policiesWithCardFeedErrors.find((p) => p.id === policy.id)) { + brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + } else if (shouldShowEmployeeListError(policy)) { + brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + } else { + brickRoadIndicator = getPolicyBrickRoadIndicatorStatus( + policy, + isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy), + ); + } + } + + if (policy?.isJoinRequestPending && policy?.policyDetailsForNonMembers) { + const policyInfo = Object.values(policy.policyDetailsForNonMembers).at(0) as PolicyDetailsForNonMembers; + const id = Object.keys(policy.policyDetailsForNonMembers).at(0); + workspaces.push({ + listItemType: 'workspace', + title: policyInfo.name, + icon: policyInfo?.avatar ? policyInfo.avatar : getDefaultWorkspaceAvatar(policy.name), + disabled: true, + ownerAccountID: policyInfo.ownerAccountID, + type: policyInfo.type, + iconType: policyInfo?.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, + iconFill: theme.textLight, + fallbackIcon: icons.FallbackWorkspaceAvatar, + policyID: id, + role: CONST.POLICY.ROLE.USER, + errors: undefined, + action: () => null, + dismissError: () => null, + isJoinRequestPending: true, + }); + } else { + workspaces.push({ + listItemType: 'workspace', + title: policy.name, + icon: policy.avatarURL ? policy.avatarURL : getDefaultWorkspaceAvatar(policy.name), + action: () => navigateToWorkspace(policy.id), + brickRoadIndicator, + pendingAction: policy.pendingAction, + errors: policy.errors, + dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), + disabled: policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, + iconFill: theme.textLight, + fallbackIcon: icons.FallbackWorkspaceAvatar, + policyID: policy.id, + ownerAccountID: policy.ownerAccountID, + role: policy.role, + type: policy.type, + employeeList: policy.employeeList, + }); + } + } + } + + const filterWorkspace = (workspace: WorkspaceItem, inputValue: string) => workspace.title.toLowerCase().includes(inputValue); + const sortWorkspace = (workspaceItems: WorkspaceItem[]) => workspaceItems.sort((a, b) => localeCompare(a.title, b.title)); + const [inputValue, setInputValue, filteredWorkspaces] = useSearchResults(workspaces, filterWorkspace, sortWorkspace); + /** * Gets the menu item for each workspace */ @@ -442,7 +539,7 @@ function WorkspacesListPage() { errorRowStyles={[styles.ph5, styles.mt3]} onClose={item.dismissError} errors={item.errors} - style={styles.mb2} + style={!isLessThanMediumScreen ? undefined : styles.mb1} shouldShowErrorMessages={item.policyID !== policyIDToDelete} shouldHideOnDelete={false} > @@ -474,6 +571,7 @@ function WorkspacesListPage() { isHovered={hovered} disabled={item.disabled} onPress={item.action} + isLastItem={index === filteredWorkspaces.length - 1} /> )} @@ -481,103 +579,6 @@ function WorkspacesListPage() { ); }; - const navigateToWorkspace = (policyID: string) => { - if (shouldUseNarrowLayout) { - Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID)); - return; - } - Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(policyID)); - }; - - const navigateToDomain = ({domainAccountID, isAdmin}: {domainAccountID: number; isAdmin: boolean}) => { - if (!isAdmin) { - return Navigation.navigate(ROUTES.WORKSPACES_DOMAIN_ACCESS_RESTRICTED.getRoute(domainAccountID)); - } - Navigation.navigate(ROUTES.DOMAIN_INITIAL.getRoute(domainAccountID)); - }; - - const {policiesWithCardFeedErrors} = usePoliciesWithCardFeedErrors(); - - /** - * Add free policies (workspaces) to the list of menu items and returns the list of menu items - */ - const workspaces: WorkspaceItem[] = []; - if (!isEmptyObject(policies)) { - const reimbursementAccountBrickRoadIndicator = !isEmptyObject(reimbursementAccount?.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined; - - for (const policy of Object.values(policies)) { - if (!policy || !shouldShowPolicy(policy, true, session?.email)) { - continue; - } - - const receiptUberBrickRoadIndicator = getUberConnectionErrorDirectlyFromPolicy(policy as OnyxEntry) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined; - - let brickRoadIndicator: ValueOf | undefined; - if (isPolicyAdmin(policy, session?.email)) { - const indicator = reimbursementAccountBrickRoadIndicator ?? receiptUberBrickRoadIndicator; - - if (indicator) { - brickRoadIndicator = indicator; - } else if (policiesWithCardFeedErrors.find((p) => p.id === policy.id)) { - brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; - } else if (shouldShowEmployeeListError(policy)) { - brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; - } else { - brickRoadIndicator = getPolicyBrickRoadIndicatorStatus( - policy, - isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy), - ); - } - } - - if (policy?.isJoinRequestPending && policy?.policyDetailsForNonMembers) { - const policyInfo = Object.values(policy.policyDetailsForNonMembers).at(0) as PolicyDetailsForNonMembers; - const id = Object.keys(policy.policyDetailsForNonMembers).at(0); - workspaces.push({ - listItemType: 'workspace', - title: policyInfo.name, - icon: policyInfo?.avatar ? policyInfo.avatar : getDefaultWorkspaceAvatar(policy.name), - disabled: true, - ownerAccountID: policyInfo.ownerAccountID, - type: policyInfo.type, - iconType: policyInfo?.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, - iconFill: theme.textLight, - fallbackIcon: icons.FallbackWorkspaceAvatar, - policyID: id, - role: CONST.POLICY.ROLE.USER, - errors: undefined, - action: () => null, - dismissError: () => null, - isJoinRequestPending: true, - }); - } else { - workspaces.push({ - listItemType: 'workspace', - title: policy.name, - icon: policy.avatarURL ? policy.avatarURL : getDefaultWorkspaceAvatar(policy.name), - action: () => navigateToWorkspace(policy.id), - brickRoadIndicator, - pendingAction: policy.pendingAction, - errors: policy.errors, - dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), - disabled: policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, - iconFill: theme.textLight, - fallbackIcon: icons.FallbackWorkspaceAvatar, - policyID: policy.id, - ownerAccountID: policy.ownerAccountID, - role: policy.role, - type: policy.type, - employeeList: policy.employeeList, - }); - } - } - } - - const filterWorkspace = (workspace: WorkspaceItem, inputValue: string) => workspace.title.toLowerCase().includes(inputValue); - const sortWorkspace = (workspaceItems: WorkspaceItem[]) => workspaceItems.sort((a, b) => localeCompare(a.title, b.title)); - const [inputValue, setInputValue, filteredWorkspaces] = useSearchResults(workspaces, filterWorkspace, sortWorkspace); - const domains = allDomains ? Object.values(allDomains).reduce((domainItems, domain) => { if (!domain || !domain.accountID || !domain.email) { @@ -628,7 +629,20 @@ function WorkspacesListPage() { /> )} {!isLessThanMediumScreen && filteredWorkspaces.length > 0 && ( - + ); } case 'domains-header': { + if (!isLessThanMediumScreen) { + return ( + + {translate('common.domains')} + + ); + } return ( {translate('common.domains')} diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index 2db4457710273..acb9372875213 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -91,6 +91,9 @@ type WorkspacesListRowProps = WithCurrentUserPersonalDetailsProps & { /** Callback when the row is pressed */ onPress?: () => void; + + /** Whether this is the last item in the list */ + isLastItem?: boolean; }; type BrickRoadIndicatorIconProps = { @@ -131,6 +134,7 @@ function WorkspacesListRow({ isHovered, disabled, onPress, + isLastItem, }: WorkspacesListRowProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -261,8 +265,20 @@ function WorkspacesListRow({ ); return ( - - + + + {translate('common.destination')} diff --git a/src/styles/index.ts b/src/styles/index.ts index d4ae898eb3ca0..992fce3db5435 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -374,9 +374,8 @@ const staticStyles = (theme: ThemeColors) => reportLayoutGroupHeader: { paddingHorizontal: 12, - marginTop: 16, - marginBottom: 8, - backgroundColor: theme.appBG, + paddingVertical: 8, + backgroundColor: theme.highlightBG, justifyContent: 'center', }, @@ -3482,18 +3481,26 @@ const staticStyles = (theme: ThemeColors) => }, searchListHeaderContainerStyle: { - width: '100%', flexDirection: 'row', alignItems: 'center', ...userSelect.userSelectNone, - paddingBottom: 12, - backgroundColor: theme.appBG, + minHeight: 40, + paddingTop: 8, + paddingBottom: 8, + backgroundColor: theme.highlightBG, justifyContent: 'flex-start', + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderBottomWidth: 1, + borderColor: theme.border, }, groupSearchListTableContainerStyle: { minHeight: variables.h28, paddingBottom: 0, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomWidth: 0, }, narrowSearchRouterInactiveStyle: { @@ -4744,7 +4751,7 @@ const staticStyles = (theme: ThemeColors) => justifyContent: 'space-between', overflow: 'hidden', flexDirection: 'row', - paddingVertical: 6, + paddingVertical: 4, }, searchQueryListItemStyle: { @@ -4756,7 +4763,6 @@ const staticStyles = (theme: ThemeColors) => }, listTableHeader: { - paddingVertical: 12, paddingHorizontal: 32, }, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 9ee72a3f13860..4882d08406615 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1800,11 +1800,13 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ let columnWidth; switch (columnName) { case CONST.SEARCH.TABLE_COLUMNS.COMMENTS: - case CONST.SEARCH.TABLE_COLUMNS.RECEIPT: columnWidth = {...getWidthStyle(variables.w36), ...styles.alignItemsCenter}; break; + case CONST.SEARCH.TABLE_COLUMNS.RECEIPT: + columnWidth = {...getWidthStyle(variables.w28), ...styles.alignItemsCenter}; + break; case CONST.SEARCH.TABLE_COLUMNS.AVATAR: - columnWidth = {...getWidthStyle(variables.w40), ...styles.alignItemsCenter}; + columnWidth = {...getWidthStyle(variables.w28), ...styles.alignItemsCenter}; break; case CONST.SEARCH.TABLE_COLUMNS.STATUS: columnWidth = {...getWidthStyle(variables.w80), ...styles.alignItemsCenter}; @@ -1857,7 +1859,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ columnWidth = {...getWidthStyle(isAmountColumnWide ? variables.w130 : variables.w96), ...(!shouldRemoveTotalColumnFlex && styles.flex1), ...styles.alignItemsEnd}; break; case CONST.SEARCH.TABLE_COLUMNS.TYPE: - columnWidth = {...getWidthStyle(variables.w20), ...styles.alignItemsCenter}; + columnWidth = {...getWidthStyle(variables.w16), ...styles.alignItemsCenter}; break; case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE: case CONST.SEARCH.TABLE_COLUMNS.BILLABLE: @@ -1867,7 +1869,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ columnWidth = {...getWidthStyle(variables.w92), ...styles.flex1}; break; case CONST.SEARCH.TABLE_COLUMNS.ACTION: - columnWidth = {...getWidthStyle(variables.w80), ...styles.alignItemsCenter}; + columnWidth = {...getWidthStyle(variables.w68), ...styles.alignItemsCenter}; break; case CONST.SEARCH.TABLE_COLUMNS.EXPORTED_TO: columnWidth = {...getWidthStyle(variables.w72), ...styles.alignItemsCenter}; diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 0510b832e12ed..9d7ad323010e4 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -120,6 +120,7 @@ export default { gutterWidth: 12, optionRowHeight: 64, optionRowHeightCompact: 52, + tableRowHeight: 56, sectionMenuItemHeight: 52, sectionMenuItemHeightCompact: 44, optionsListSectionHeaderHeight: getValueUsingPixelRatio(32, 38), @@ -352,6 +353,7 @@ export default { h20: 20, h28: 28, + h32: 32, h36: 36, h40: 40, h70: 70, @@ -366,6 +368,7 @@ export default { w44: 44, w46: 46, w52: 52, + w68: 68, w72: 72, w80: 80, w92: 92,