Skip to content

Commit eb7deee

Browse files
authored
Merge pull request Expensify#80833 from Expensify/revert-80824-revert-80684-cristi_support-groupByWeek
Add support for groupBy week in Search - v2
2 parents 9d4a0aa + f6ea92f commit eb7deee

29 files changed

Lines changed: 1244 additions & 283 deletions

src/CONST/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6897,6 +6897,7 @@ const CONST = {
68976897
MERCHANT: 'merchant',
68986898
TAG: 'tag',
68996899
MONTH: 'month',
6900+
WEEK: 'week',
69006901
},
69016902
get TYPE_CUSTOM_COLUMNS() {
69026903
return {
@@ -6993,6 +6994,11 @@ const CONST = {
69936994
EXPENSES: this.TABLE_COLUMNS.GROUP_EXPENSES,
69946995
TOTAL: this.TABLE_COLUMNS.GROUP_TOTAL,
69956996
},
6997+
WEEK: {
6998+
WEEK: this.TABLE_COLUMNS.GROUP_WEEK,
6999+
EXPENSES: this.TABLE_COLUMNS.GROUP_EXPENSES,
7000+
TOTAL: this.TABLE_COLUMNS.GROUP_TOTAL,
7001+
},
69967002
};
69977003
},
69987004
get TYPE_DEFAULT_COLUMNS() {
@@ -7038,6 +7044,7 @@ const CONST = {
70387044
MERCHANT: [this.TABLE_COLUMNS.GROUP_MERCHANT, this.TABLE_COLUMNS.GROUP_EXPENSES, this.TABLE_COLUMNS.GROUP_TOTAL],
70397045
TAG: [this.TABLE_COLUMNS.GROUP_TAG, this.TABLE_COLUMNS.GROUP_EXPENSES, this.TABLE_COLUMNS.GROUP_TOTAL],
70407046
MONTH: [this.TABLE_COLUMNS.GROUP_MONTH, this.TABLE_COLUMNS.GROUP_EXPENSES, this.TABLE_COLUMNS.GROUP_TOTAL],
7047+
WEEK: [this.TABLE_COLUMNS.GROUP_WEEK, this.TABLE_COLUMNS.GROUP_EXPENSES, this.TABLE_COLUMNS.GROUP_TOTAL],
70417048
};
70427049
},
70437050
BOOLEAN: {
@@ -7137,6 +7144,7 @@ const CONST = {
71377144
GROUP_MERCHANT: 'groupMerchant',
71387145
GROUP_TAG: 'groupTag',
71397146
GROUP_MONTH: 'groupmonth',
7147+
GROUP_WEEK: 'groupweek',
71407148
},
71417149
SYNTAX_OPERATORS: {
71427150
AND: 'and',
@@ -7332,6 +7340,8 @@ const CONST = {
73327340
[this.TABLE_COLUMNS.GROUP_CATEGORY]: 'group-category',
73337341
[this.TABLE_COLUMNS.GROUP_MERCHANT]: 'group-merchant',
73347342
[this.TABLE_COLUMNS.GROUP_TAG]: 'group-tag',
7343+
[this.TABLE_COLUMNS.GROUP_MONTH]: 'group-month',
7344+
[this.TABLE_COLUMNS.GROUP_WEEK]: 'group-week',
73357345
};
73367346
},
73377347
NOT_MODIFIER: 'Not',

src/components/Search/SearchList/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type {
3030
TransactionListItemType,
3131
TransactionMerchantGroupListItemType,
3232
TransactionMonthGroupListItemType,
33+
TransactionWeekGroupListItemType,
3334
} from '@components/SelectionListWithSections/types';
3435
import Text from '@components/Text';
3536
import useKeyboardState from '@hooks/useKeyboardState';
@@ -167,6 +168,13 @@ function isTransactionMatchWithGroupItem(transaction: Transaction, groupItem: Se
167168
const transactionDateString = transaction.modifiedCreated ?? transaction.created ?? '';
168169
return DateUtils.isDateStringInMonth(transactionDateString, monthGroup.year, monthGroup.month);
169170
}
171+
if (groupBy === CONST.SEARCH.GROUP_BY.WEEK) {
172+
const weekGroup = groupItem as TransactionWeekGroupListItemType;
173+
const transactionDateString = transaction.modifiedCreated ?? transaction.created ?? '';
174+
const datePart = transactionDateString.substring(0, 10);
175+
const {start: weekStart, end: weekEnd} = DateUtils.getWeekDateRange(weekGroup.week);
176+
return datePart >= weekStart && datePart <= weekEnd;
177+
}
170178
return false;
171179
}
172180

src/components/Search/index.tsx

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {isSplitAction} from '@libs/ReportSecondaryActionUtils';
4141
import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils';
4242
import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils';
4343
import {
44+
adjustTimeRangeToDateFilters,
4445
createAndOpenSearchTransactionThread,
4546
getColumnsToShow,
4647
getListItem,
@@ -60,6 +61,7 @@ import {
6061
isTransactionMerchantGroupListItemType,
6162
isTransactionMonthGroupListItemType,
6263
isTransactionTagGroupListItemType,
64+
isTransactionWeekGroupListItemType,
6365
isTransactionWithdrawalIDGroupListItemType,
6466
shouldShowEmptyState,
6567
shouldShowYear as shouldShowYearUtil,
@@ -547,7 +549,7 @@ function Search({
547549

548550
// For expense reports: when ANY transaction is selected, we want ALL transactions in the report selected.
549551
// This ensures report-level selection persists when new transactions are added.
550-
const hasAnySelected = isExpenseReportType && transactionGroup.transactions.some((transaction) => transaction.transactionID in selectedTransactions);
552+
const hasAnySelected = isExpenseReportType && transactionGroup.transactions.some((transaction: TransactionListItemType) => transaction.transactionID in selectedTransactions);
551553

552554
for (const transactionItem of transactionGroup.transactions) {
553555
const isSelected = transactionItem.transactionID in selectedTransactions;
@@ -907,13 +909,46 @@ function Search({
907909
return;
908910
}
909911

910-
let reportID = item.reportID;
911-
if (isTransactionItem && item?.reportAction?.childReportID) {
912-
const isFromSelfDM = item.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;
913-
const isFromOneTransactionReport = isOneTransactionReport(item.report);
912+
if (isTransactionWeekGroupListItemType(item)) {
913+
if (!item.week) {
914+
return;
915+
}
916+
// Extract the existing date filter to check for year-to-date or other date limits
917+
const existingDateFilter = queryJSON.flatFilters.find((filter) => filter.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE);
918+
const {start: weekStart, end: weekEnd} = adjustTimeRangeToDateFilters(DateUtils.getWeekDateRange(item.week), existingDateFilter);
919+
const newFlatFilters = queryJSON.flatFilters.filter((filter) => filter.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE);
920+
newFlatFilters.push({
921+
key: CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
922+
filters: [
923+
{operator: CONST.SEARCH.SYNTAX_OPERATORS.GREATER_THAN_OR_EQUAL_TO, value: weekStart},
924+
{operator: CONST.SEARCH.SYNTAX_OPERATORS.LOWER_THAN_OR_EQUAL_TO, value: weekEnd},
925+
],
926+
});
927+
const newQueryJSON: SearchQueryJSON = {...queryJSON, groupBy: undefined, flatFilters: newFlatFilters};
928+
const newQuery = buildSearchQueryString(newQueryJSON);
929+
const newQueryJSONWithHash = buildSearchQueryJSON(newQuery);
930+
if (!newQueryJSONWithHash) {
931+
return;
932+
}
933+
handleSearch({queryJSON: newQueryJSONWithHash, searchKey, offset: 0, shouldCalculateTotals: false, isLoading: false});
934+
return;
935+
}
936+
937+
// After handling all group types, item should be TransactionListItemType, ReportActionListItemType, or TransactionGroupListItemType
938+
if (!isTransactionItem && !isReportActionListItemType(item) && !isTransactionGroupListItemType(item)) {
939+
return;
940+
}
941+
942+
const transactionItem = item as TransactionListItemType;
943+
const reportActionItem = item as ReportActionListItemType;
944+
945+
let reportID = transactionItem.reportID ?? reportActionItem.reportID;
946+
if (isTransactionItem && transactionItem?.reportAction?.childReportID) {
947+
const isFromSelfDM = transactionItem.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;
948+
const isFromOneTransactionReport = isOneTransactionReport(transactionItem.report);
914949

915950
if (isFromSelfDM || !isFromOneTransactionReport) {
916-
reportID = item?.reportAction?.childReportID;
951+
reportID = transactionItem?.reportAction?.childReportID;
917952
}
918953
}
919954

@@ -929,16 +964,17 @@ function Search({
929964
});
930965

931966
if (isTransactionGroupListItemType(item)) {
932-
const firstTransaction = item.transactions.at(0);
933-
if (item.isOneTransactionReport && firstTransaction && transactionPreviewData) {
967+
const groupItem = item as TransactionGroupListItemType;
968+
const firstTransaction = groupItem.transactions.at(0);
969+
if (groupItem.isOneTransactionReport && firstTransaction && transactionPreviewData) {
934970
if (!firstTransaction?.reportAction?.childReportID) {
935971
createAndOpenSearchTransactionThread(firstTransaction, backTo, firstTransaction?.reportAction?.childReportID, transactionPreviewData, false);
936972
} else {
937973
setOptimisticDataForTransactionThreadPreview(firstTransaction, transactionPreviewData, firstTransaction?.reportAction?.childReportID);
938974
}
939975
}
940976

941-
if (item.transactions.length > 1) {
977+
if (groupItem.transactions.length > 1) {
942978
markReportIDAsMultiTransactionExpense(reportID);
943979
} else {
944980
unmarkReportIDAsMultiTransactionExpense(reportID);
@@ -949,15 +985,15 @@ function Search({
949985
}
950986

951987
if (isReportActionListItemType(item)) {
952-
const reportActionID = item.reportActionID;
988+
const reportActionID = reportActionItem.reportActionID;
953989
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo}));
954990
return;
955991
}
956992

957993
markReportIDAsExpense(reportID);
958994

959995
if (isTransactionItem && transactionPreviewData) {
960-
setOptimisticDataForTransactionThreadPreview(item, transactionPreviewData, item?.reportAction?.childReportID);
996+
setOptimisticDataForTransactionThreadPreview(transactionItem, transactionPreviewData, transactionItem?.reportAction?.childReportID);
961997
}
962998

963999
requestAnimationFrame(() => Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, backTo})));

src/components/Search/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ type SearchCustomColumnIds =
139139
| ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.CATEGORY>
140140
| ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.MERCHANT>
141141
| ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.TAG>
142-
| ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.MONTH>;
142+
| ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.MONTH>
143+
| ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.WEEK>;
143144

144145
type SearchContextData = {
145146
currentSearchHash: number;

src/components/SelectionListWithSections/Search/BaseListItemHeader.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ type GroupColumnKey =
3030
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_CATEGORY
3131
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT
3232
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_TAG
33-
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MONTH;
33+
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MONTH
34+
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK;
3435

3536
/** Supported column style keys for sizing */
3637
type ColumnStyleKey =
3738
| typeof CONST.SEARCH.TABLE_COLUMNS.CATEGORY
3839
| typeof CONST.SEARCH.TABLE_COLUMNS.MERCHANT
3940
| typeof CONST.SEARCH.TABLE_COLUMNS.TAG
40-
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MONTH;
41+
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MONTH
42+
| typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK;
4143

4244
type BaseListItemHeaderProps<TItem extends ListItem> = {
4345
/** The group item being rendered */

src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {
2222
TransactionMonthGroupListItemType,
2323
TransactionReportGroupListItemType,
2424
TransactionTagGroupListItemType,
25+
TransactionWeekGroupListItemType,
2526
TransactionWithdrawalIDGroupListItemType,
2627
} from '@components/SelectionListWithSections/types';
2728
import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle';
@@ -50,6 +51,7 @@ import MonthListItemHeader from './MonthListItemHeader';
5051
import ReportListItemHeader from './ReportListItemHeader';
5152
import TagListItemHeader from './TagListItemHeader';
5253
import TransactionGroupListExpandedItem from './TransactionGroupListExpanded';
54+
import WeekListItemHeader from './WeekListItemHeader';
5355
import WithdrawalIDListItemHeader from './WithdrawalIDListItemHeader';
5456

5557
function TransactionGroupListItem<TItem extends ListItem>({
@@ -340,6 +342,19 @@ function TransactionGroupListItem<TItem extends ListItem>({
340342
isExpanded={isExpanded}
341343
/>
342344
),
345+
[CONST.SEARCH.GROUP_BY.WEEK]: (
346+
<WeekListItemHeader
347+
week={groupItem as TransactionWeekGroupListItemType}
348+
onCheckboxPress={onCheckboxPress}
349+
isDisabled={isDisabledOrEmpty}
350+
columns={columns}
351+
canSelectMultiple={canSelectMultiple}
352+
isSelectAllChecked={isSelectAllChecked}
353+
isIndeterminate={isIndeterminate}
354+
onDownArrowClick={onExpandIconPress}
355+
isExpanded={isExpanded}
356+
/>
357+
),
343358
};
344359

345360
if (searchType === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
import type {ListItem, TransactionWeekGroupListItemType} from '@components/SelectionListWithSections/types';
3+
import CONST from '@src/CONST';
4+
import type {BaseListItemHeaderProps} from './BaseListItemHeader';
5+
import BaseListItemHeader from './BaseListItemHeader';
6+
7+
type WeekListItemHeaderProps<TItem extends ListItem> = Omit<BaseListItemHeaderProps<TItem>, 'item' | 'displayName' | 'groupColumnKey' | 'columnStyleKey'> & {
8+
/** The week group currently being looked at */
9+
week: TransactionWeekGroupListItemType;
10+
};
11+
12+
function WeekListItemHeader<TItem extends ListItem>({
13+
week: weekItem,
14+
onCheckboxPress,
15+
isDisabled,
16+
canSelectMultiple,
17+
isSelectAllChecked,
18+
isIndeterminate,
19+
isExpanded,
20+
onDownArrowClick,
21+
columns,
22+
}: WeekListItemHeaderProps<TItem>) {
23+
const weekName = weekItem.formattedWeek;
24+
25+
return (
26+
<BaseListItemHeader
27+
item={weekItem}
28+
displayName={weekName}
29+
groupColumnKey={CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK}
30+
columnStyleKey={CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK}
31+
onCheckboxPress={onCheckboxPress}
32+
isDisabled={isDisabled}
33+
canSelectMultiple={canSelectMultiple}
34+
isSelectAllChecked={isSelectAllChecked}
35+
isIndeterminate={isIndeterminate}
36+
isExpanded={isExpanded}
37+
onDownArrowClick={onDownArrowClick}
38+
columns={columns}
39+
/>
40+
);
41+
}
42+
43+
export default WeekListItemHeader;

src/components/SelectionListWithSections/SearchTableHeader.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,24 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI
409409
isColumnSortable: true,
410410
},
411411
];
412+
case CONST.SEARCH.GROUP_BY.WEEK:
413+
return [
414+
{
415+
columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK,
416+
translationKey: 'common.week',
417+
isColumnSortable: true,
418+
},
419+
{
420+
columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES,
421+
translationKey: 'common.expenses',
422+
isColumnSortable: true,
423+
},
424+
{
425+
columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL,
426+
translationKey: 'common.total',
427+
isColumnSortable: true,
428+
},
429+
];
412430
default:
413431
return [];
414432
}

src/components/SelectionListWithSections/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type {
3838
SearchTagGroup,
3939
SearchTask,
4040
SearchTransactionAction,
41+
SearchWeekGroup,
4142
SearchWithdrawalIDGroup,
4243
} from '@src/types/onyx/SearchResults';
4344
import type {ReceiptErrors} from '@src/types/onyx/Transaction';
@@ -526,6 +527,11 @@ type TransactionTagGroupListItemType = TransactionGroupListItemType & {groupedBy
526527
formattedTag?: string;
527528
};
528529

530+
type TransactionWeekGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.WEEK} & SearchWeekGroup & {
531+
/** Final and formatted "week" value used for displaying */
532+
formattedWeek: string;
533+
};
534+
529535
type ListItemProps<TItem extends ListItem> = CommonListItemProps<TItem> & {
530536
/** The section list item */
531537
item: TItem;
@@ -1171,6 +1177,7 @@ export type {
11711177
TransactionCategoryGroupListItemType,
11721178
TransactionMerchantGroupListItemType,
11731179
TransactionTagGroupListItemType,
1180+
TransactionWeekGroupListItemType,
11741181
Section,
11751182
SectionListDataType,
11761183
SectionWithIndexOffset,

src/languages/de.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ const translations: TranslationDeepObject<typeof en> = {
641641
newFeature: 'Neue Funktion',
642642
month: 'Monat',
643643
home: 'Startseite',
644+
week: 'Woche',
644645
},
645646
supportalNoAccess: {
646647
title: 'Nicht so schnell',
@@ -7013,6 +7014,7 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
70137014
[CONST.SEARCH.GROUP_BY.MERCHANT]: 'Händler',
70147015
[CONST.SEARCH.GROUP_BY.TAG]: 'Tag',
70157016
[CONST.SEARCH.GROUP_BY.MONTH]: 'Monat',
7017+
[CONST.SEARCH.GROUP_BY.WEEK]: 'Woche',
70167018
},
70177019
feed: 'Feed',
70187020
withdrawalType: {

0 commit comments

Comments
 (0)