-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add support for groupBy week in Search #80684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 6c88f168..6fe752b8 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -638,6 +638,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: 'Doppelte Ausgabe',
newFeature: 'Neue Funktion',
month: 'Monat',
+ week: 'Woche',
},
supportalNoAccess: {
title: 'Nicht so schnell',
@@ -6956,6 +6957,7 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
[CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'Auszahlungs-ID',
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'Kategorie',
[CONST.SEARCH.GROUP_BY.MONTH]: 'Monat',
+ [CONST.SEARCH.GROUP_BY.WEEK]: 'Woche',
},
feed: 'Feed',
withdrawalType: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 7f08b053..d2f21ef9 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -640,6 +640,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: 'Note de frais en double',
newFeature: 'Nouvelle fonctionnalité',
month: 'Mois',
+ week: 'Semaine',
},
supportalNoAccess: {
title: 'Pas si vite',
@@ -6967,6 +6968,7 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
[CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID de retrait',
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'Catégorie',
[CONST.SEARCH.GROUP_BY.MONTH]: 'Mois',
+ [CONST.SEARCH.GROUP_BY.WEEK]: 'Semaine',
},
feed: 'Flux',
withdrawalType: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 7525885d..52b6b8ce 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -639,6 +639,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: 'Spesa duplicata',
newFeature: 'Nuova funzionalità',
month: 'Mese',
+ week: 'Settimana',
},
supportalNoAccess: {
title: 'Non così in fretta',
@@ -6945,6 +6946,7 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
[CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID prelievo', //_/\__/_/ \_,_/\__/\__/\_,_/
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'Categoria',
[CONST.SEARCH.GROUP_BY.MONTH]: 'Mese',
+ [CONST.SEARCH.GROUP_BY.WEEK]: 'Settimana',
},
feed: 'Feed',
withdrawalType: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 43f10ab5..789bba5b 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -638,6 +638,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: '重複した経費',
newFeature: '新機能',
month: '月',
+ week: '週',
},
supportalNoAccess: {
title: 'ちょっと待ってください',
@@ -6882,9 +6883,10 @@ ${reportName}
groupBy: {
[CONST.SEARCH.GROUP_BY.FROM]: '差出人',
[CONST.SEARCH.GROUP_BY.CARD]: 'カード',
- [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '出金ID',
+ [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '出金 ID',
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'カテゴリ',
[CONST.SEARCH.GROUP_BY.MONTH]: '月',
+ [CONST.SEARCH.GROUP_BY.WEEK]: '週',
},
feed: 'フィード',
withdrawalType: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 5289966a..900a0d20 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -639,6 +639,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: 'Dubbele uitgave',
newFeature: 'Nieuwe functie',
month: 'Maand',
+ week: 'Week',
},
supportalNoAccess: {
title: 'Niet zo snel',
@@ -6927,6 +6928,7 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
[CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'Opname-ID',
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'Categorie',
[CONST.SEARCH.GROUP_BY.MONTH]: 'Maand',
+ [CONST.SEARCH.GROUP_BY.WEEK]: 'Week',
},
feed: 'Feed',
withdrawalType: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index a3772656..307f5200 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -639,6 +639,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: 'Zduplikowany wydatek',
newFeature: 'Nowa funkcja',
month: 'Miesiąc',
+ week: 'Tydzień',
},
supportalNoAccess: {
title: 'Nie tak szybko',
@@ -6917,6 +6918,7 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
[CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID wypłaty',
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'Kategoria',
[CONST.SEARCH.GROUP_BY.MONTH]: 'Miesiąc',
+ [CONST.SEARCH.GROUP_BY.WEEK]: 'Tydzień',
},
feed: 'Kanał',
withdrawalType: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index ee7cd0e6..55737c17 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -638,6 +638,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: 'Despesa duplicada',
newFeature: 'Novo recurso',
month: 'Mês',
+ week: 'Semana',
},
supportalNoAccess: {
title: 'Não tão rápido',
@@ -6915,9 +6916,10 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
groupBy: {
[CONST.SEARCH.GROUP_BY.FROM]: 'De',
[CONST.SEARCH.GROUP_BY.CARD]: 'Cartão',
- [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID de retirada',
+ [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID de saque',
[CONST.SEARCH.GROUP_BY.CATEGORY]: 'Categoria',
[CONST.SEARCH.GROUP_BY.MONTH]: 'Mês',
+ [CONST.SEARCH.GROUP_BY.WEEK]: 'Semana',
},
feed: 'Feed',
withdrawalType: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index ee322f52..e70328a2 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -635,6 +635,7 @@ const translations: TranslationDeepObject<typeof en> = {
duplicateExpense: '重复报销',
newFeature: '新功能',
month: '月',
+ week: '周',
},
supportalNoAccess: {
title: '先别急',
@@ -6760,11 +6761,13 @@ ${reportName}
reimbursable: '可报销',
purchaseCurrency: '购买货币',
groupBy: {
- [CONST.SEARCH.GROUP_BY.FROM]: '来自',
+ [CONST.SEARCH.GROUP_BY.FROM]: '发件人',
[CONST.SEARCH.GROUP_BY.CARD]: '卡',
- [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '提现 ID',
+ //_/\__/_/ \_,_/\__/\__/\_,_/
+ [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '提现编号',
[CONST.SEARCH.GROUP_BY.CATEGORY]: '类别',
[CONST.SEARCH.GROUP_BY.MONTH]: '月',
+ [CONST.SEARCH.GROUP_BY.WEEK]: '周',
},
feed: '动态',
withdrawalType: {
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
@shubham1206agra Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
🚧 @cristipaval has triggered a test Expensify/App build. You can view the workflow run here. |
| if (isGroupEntry(key)) { | ||
| const weekGroup = data[key]; | ||
| // Check if this is a week group by checking for week property | ||
| if (!('week' in weekGroup)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ PERF-13 (docs)
DateUtils.getWeekDateRange(weekGroup.week) is called twice in the same loop iteration - once inside the if (queryJSON && weekGroup.week) block and again immediately after. The second call produces the same result as the first since weekGroup.week hasn't changed.
Move the first call outside the conditional and reuse the result:
const {start: weekStart, end: weekEnd} = DateUtils.getWeekDateRange(weekGroup.week);
let transactionsQueryJSON: SearchQueryJSON | undefined;
if (queryJSON && weekGroup.week) {
const newFlatFilters = queryJSON.flatFilters.filter((filter) => filter.key \!== CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE);
newFlatFilters.push({
key: CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
filters: [
{operator: CONST.SEARCH.SYNTAX_OPERATORS.GREATER_THAN_OR_EQUAL_TO, value: weekStart},
{operator: CONST.SEARCH.SYNTAX_OPERATORS.LOWER_THAN_OR_EQUAL_TO, value: weekEnd},
],
});
const newQueryJSON: SearchQueryJSON = {...queryJSON, groupBy: undefined, flatFilters: newFlatFilters};
const newQuery = buildSearchQueryString(newQueryJSON);
transactionsQueryJSON = buildSearchQueryJSON(newQuery);
}
const formattedWeek = DateUtils.getFormattedDateRangeForSearch(weekStart, weekEnd);Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
| if (isTransactionItem && item?.reportAction?.childReportID) { | ||
| const isFromSelfDM = item.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; | ||
| const isFromOneTransactionReport = isOneTransactionReport(item.report); | ||
| if (isTransactionWeekGroupListItemType(item)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ CONSISTENCY-3 (docs)
This logic for creating date range filters from a week group is duplicated in src/libs/SearchUIUtils.ts in the getWeekSections function (lines 2337-2350). The same pattern of filtering out DATE filters, calling getWeekDateRange, and building a new query appears in both places.
Consider extracting this into a shared utility function:
// In a shared utilities file or SearchUtils
function buildWeekDateRangeQuery(
weekStartDate: string,
baseQueryJSON: SearchQueryJSON
): SearchQueryJSON | undefined {
const {start: weekStart, end: weekEnd} = DateUtils.getWeekDateRange(weekStartDate);
const newFlatFilters = baseQueryJSON.flatFilters.filter(
(filter) => filter.key \!== CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE
);
newFlatFilters.push({
key: CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
filters: [
{operator: CONST.SEARCH.SYNTAX_OPERATORS.GREATER_THAN_OR_EQUAL_TO, value: weekStart},
{operator: CONST.SEARCH.SYNTAX_OPERATORS.LOWER_THAN_OR_EQUAL_TO, value: weekEnd},
],
});
const newQueryJSON: SearchQueryJSON = {...baseQueryJSON, groupBy: undefined, flatFilters: newFlatFilters};
const newQuery = buildSearchQueryString(newQueryJSON);
return buildSearchQueryJSON(newQuery);
}
// Then use it here:
const newQueryJSONWithHash = buildWeekDateRangeQuery(weekGroupItem.week, queryJSON);Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
Looks pretty good from a design standpoint 👍 |
|
@cristipaval Fix the conflicts please |
|
Thanks for reviewing, @shawnborton! 🙏 Conflicts are resolved, @shubham1206agra. |
| merchant: "asc", | ||
| month: "desc" | ||
| month: "desc", | ||
| week: "desc", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| week: "desc", | |
| week: "desc" |
Minor change :)
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK]: ( | ||
| <View | ||
| key={CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK} | ||
| style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cristipaval Please refactor to use the new component for DRY
| [CONST.SEARCH.GROUP_BY.FROM]: 'De', | ||
| [CONST.SEARCH.GROUP_BY.CARD]: 'Cartão', | ||
| [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID da retirada', | ||
| [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID da saque', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cristipaval Do we need this change?
| [CONST.SEARCH.GROUP_BY.FROM]: '来自', | ||
| [CONST.SEARCH.GROUP_BY.FROM]: '发件人', | ||
| [CONST.SEARCH.GROUP_BY.CARD]: '卡片', | ||
| [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '提现 ID', | ||
| //_/\__/_/ \_,_/\__/\__/\_,_/ | ||
| [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '提现编号', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cristipaval Do we need these changes?
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES]: 'count' as const, | ||
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: 'total' as const, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES]: 'count' as const, | |
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: 'total' as const, | |
| ...transactionGroupBaseSortingProperties, |
Explanation of Change
Fixed Issues
$ #80396
PROPOSAL:
Tests
Tests
Prerequisites:
Test 1: Access Group by Week via Advanced Filters
Test 2: Verify Week Grouping Display (Large Screen)
groupBy:weekfilterTest 3: Verify Week Grouping Display (Small Screen)
groupBy:weekfilterTest 4: Verify Week Grouping Across Multiple Weeks
groupBy:weekTest 5: Verify Week Expand/Collapse
groupBy:weekappliedTest 6: Verify Sorting
groupBy:weekTest 7: Verify Multi-select Checkbox
groupBy:weekTest 8: Verify Week Grouping Across Year Boundaries
groupBy:weekTest 9: Verify Sunday Transaction Grouping (Critical Edge Case)
groupBy:weekTest 10: Verify Clicking Week Group Filters Transactions
groupBy:weekgroupByis removed from the queryOffline tests
QA Steps
Same as steps.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-01-27.at.17.34.11.mov