Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8372,6 +8372,7 @@ const CONST = {
CHOOSE_SPEND_RULE: 'WorkspaceExpensifyCard-ChooseSpendRule',
},
PER_DIEM: {
ROW: 'WorkspacePerDiem-Row',
MORE_DROPDOWN: 'WorkspacePerDiem-MoreDropdown',
BULK_ACTIONS_DROPDOWN: 'WorkspacePerDiem-BulkActionsDropdown',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React from 'react';
import {View} from 'react-native';
import Icon from '@components/Icon';
import Table from '@components/Table';
import TextWithTooltip from '@components/TextWithTooltip';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {PerDiemTableRowData} from '.';

type WorkspacePerDiemTableRowProps = {
/** Data about the per diem subrate */
item: PerDiemTableRowData;

/** The index of the row relative to all other rows */
rowIndex: number;

/** Whether to use narrow table row layout */
shouldUseNarrowTableLayout: boolean;
};

function WorkspacePerDiemTableRow({item, rowIndex, shouldUseNarrowTableLayout}: WorkspacePerDiemTableRowProps) {
const theme = useTheme();
const styles = useThemeStyles();
const icons = useMemoizedLazyExpensifyIcons(['ArrowRight']);

const accessibilityLabel = [item.destination, item.subRateName, item.formattedAmount].filter(Boolean).join(', ');

return (
<Table.Row
interactive
rowIndex={rowIndex}
disabled={item.disabled}
accessibilityLabel={accessibilityLabel}
sentryLabel={CONST.SENTRY_LABEL.WORKSPACE.PER_DIEM.ROW}
offlineWithFeedback={{
pendingAction: item.pendingAction,
shouldHideOnDelete: false,
}}
onPress={item.action}
>
{({hovered}) => (
<>
{shouldUseNarrowTableLayout && (
<View style={[styles.flex1, styles.justifyContentCenter]}>
<TextWithTooltip
shouldShowTooltip
text={item.destination}
style={[styles.optionDisplayName, styles.pre]}
/>
<TextWithTooltip
shouldShowTooltip
text={[item.subRateName, item.formattedAmount].filter(Boolean).join(` ${CONST.DOT_SEPARATOR} `)}
numberOfLines={1}
style={[styles.textLabelSupporting, styles.lh16, styles.pre, styles.mt1]}
/>
</View>
)}

{!shouldUseNarrowTableLayout && (
<View style={[styles.flex1]}>
<TextWithTooltip
shouldShowTooltip
numberOfLines={1}
text={item.destination}
style={[styles.lh16, styles.optionDisplayName, styles.pre]}
/>
</View>
)}

{!shouldUseNarrowTableLayout && (
<View style={[styles.flex1]}>
<TextWithTooltip
shouldShowTooltip
numberOfLines={1}
text={item.subRateName}
style={[styles.lh16, styles.optionDisplayName, styles.pre]}
/>
</View>
)}

{!shouldUseNarrowTableLayout && (
<View style={[styles.flex1, styles.alignItemsEnd]}>
<TextWithTooltip
shouldShowTooltip
numberOfLines={1}
text={item.formattedAmount}
style={[styles.lh16, styles.optionDisplayName, styles.pre]}
/>
</View>
)}

<Icon
src={icons.ArrowRight}
fill={theme.icon}
additionalStyles={[styles.justifyContentCenter, styles.alignItemsCenter, (!hovered || item.disabled) && styles.opacitySemiTransparent]}
width={variables.iconSizeNormal}
height={variables.iconSizeNormal}
/>
</>
)}
</Table.Row>
);
}

export default WorkspacePerDiemTableRow;
138 changes: 138 additions & 0 deletions src/components/Tables/WorkspacePerDiemTable/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import type {ListRenderItemInfo} from '@shopify/flash-list';
import React from 'react';
import type {CompareItemsCallback, IsItemInSearchCallback, TableColumn, TableData} from '@components/Table';
import Table from '@components/Table';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import tokenizedSearch from '@libs/tokenizedSearch';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import WorkspacePerDiemTableRow from './WorkspacePerDiemTableRow';

type PerDiemTableColumnKey = 'destination' | 'subrate' | 'amount' | 'actions';

type PerDiemTableRowData = TableData & {
subRateID: string;
rateID: string;
destination: string;
subRateName: string;
rate: number;
formattedAmount: string;
disabled?: boolean;
pendingAction?: OnyxCommon.PendingAction;
action: () => void;
};

type WorkspacePerDiemTableProps = {
perDiemData: PerDiemTableRowData[];
selectionEnabled: boolean;
selectedKeys: string[];
onRowSelectionChange: (selectedRowKeys: string[]) => void;
EmptyStateComponent: React.ReactElement;
};

export default function WorkspacePerDiemTable({perDiemData, selectionEnabled, selectedKeys, onRowSelectionChange, EmptyStateComponent}: WorkspacePerDiemTableProps) {
const styles = useThemeStyles();
const {translate, localeCompare} = useLocalize();
const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout();
const shouldUseNarrowTableLayout = shouldUseNarrowLayout || isMediumScreenWidth;

const columns: Array<TableColumn<PerDiemTableColumnKey>> = [
{key: 'destination', label: translate('common.destination'), sortable: true},
{key: 'subrate', label: translate('common.subrate'), sortable: true},
{
key: 'amount',
label: translate('workspace.perDiem.amount'),
sortable: true,
styling: {
containerStyles: [styles.justifyContentEnd],
},
},
{key: 'actions', label: '', sortable: false, width: variables.tableCaretColumnWidth},
];

const compareItems: CompareItemsCallback<PerDiemTableRowData, PerDiemTableColumnKey> = (item1, item2, activeSorting) => {
const orderMultiplier = activeSorting.order === 'asc' ? 1 : -1;
const destinationComparison = localeCompare(item1.destination, item2.destination) * orderMultiplier;
const subRateNameComparison = localeCompare(item1.subRateName, item2.subRateName) * orderMultiplier;
const amountComparison = (item1.rate - item2.rate) * orderMultiplier;

if (activeSorting.columnKey === 'destination') {
if (destinationComparison !== 0) {
return destinationComparison;
}
if (subRateNameComparison !== 0) {
return subRateNameComparison;
}
return amountComparison;
}

if (activeSorting.columnKey === 'subrate') {
if (subRateNameComparison !== 0) {
return subRateNameComparison;
}
if (destinationComparison !== 0) {
return destinationComparison;
}
return amountComparison;
}

if (activeSorting.columnKey === 'amount') {
if (amountComparison !== 0) {
return amountComparison;
}
if (destinationComparison !== 0) {
return destinationComparison;
}
return subRateNameComparison;
}

return 0;
};

const isItemInSearch: IsItemInSearchCallback<PerDiemTableRowData> = (item, searchString) => {
const matchingItems = tokenizedSearch([item], searchString, (option) => [option.destination, option.subRateName]);
return matchingItems.length > 0;
};

const renderItem = ({item, index}: ListRenderItemInfo<PerDiemTableRowData>) => (
<WorkspacePerDiemTableRow
item={item}
rowIndex={index}
shouldUseNarrowTableLayout={shouldUseNarrowTableLayout}
/>
);

const isEmpty = perDiemData.length === 0;
const shouldShowSearchBar = perDiemData.length >= CONST.STANDARD_LIST_ITEM_LIMIT;

return (
<Table
data={perDiemData}
columns={columns}
selectionEnabled={selectionEnabled}
selectedKeys={selectedKeys}
onRowSelectionChange={onRowSelectionChange}
renderItem={renderItem}
keyExtractor={(item) => item.keyForList}
compareItems={compareItems}
isItemInSearch={isItemInSearch}
initialSortColumn="destination"
narrowLayoutSortColumn="destination"
title={translate('common.perDiem')}
>
{isEmpty && EmptyStateComponent}
{!isEmpty && (
<>
{shouldShowSearchBar && <Table.SearchBar label={translate('workspace.perDiem.findPerDiemRate')} />}
<Table.Header />
<Table.Body />
</>
)}
</Table>
);
}

export type {PerDiemTableRowData, PerDiemTableColumnKey};
2 changes: 2 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2779,6 +2779,8 @@ type WorkspaceSplitNavigatorParamList = {
};
[SCREENS.WORKSPACE.PER_DIEM]: {
policyID: string;
// eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md
backTo?: Routes;
};
[SCREENS.WORKSPACE.WORKFLOWS]: {
policyID: string;
Expand Down
Loading
Loading