Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8d1888c
Add workspace.copyPolicySettings.error translation key
fedirjh May 7, 2026
9424295
Add CopyPolicySettings action file
fedirjh May 7, 2026
a48557e
Add unit tests for buildCopyPolicySettingsData
fedirjh May 7, 2026
87c6860
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 7, 2026
861c2b2
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 7, 2026
486f960
Use named type import in CopyPolicySettings
fedirjh May 7, 2026
b9e8d88
Use named imports in CopyPolicySettings action file
fedirjh May 7, 2026
4f46baf
Replace test custom unit IDs with hex digits to satisfy cspell
fedirjh May 7, 2026
fe3643f
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 7, 2026
51fdba8
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 8, 2026
0f3a8ae
Add documentation for sourcePolicyID in CopyPolicySettingsParams
fedirjh May 8, 2026
0fd5e18
Add workspace.copySettings translation block
fedirjh May 8, 2026
585f827
Add Copy settings entry to workspace three-dot menu
fedirjh May 8, 2026
5b03b95
Implement Copy settings select-workspaces page
fedirjh May 8, 2026
8e76652
Address review: swap Duplicate/Copy menu icons
fedirjh May 8, 2026
ff4acc4
Address review: align select-all checkbox with row checkboxes
fedirjh May 8, 2026
b1184c8
Address review: use SelectionList built-in text input for search
fedirjh May 8, 2026
6f4b833
Make SelectionList ListHeader respect selectionButtonPosition
fedirjh May 8, 2026
5bbf0a1
Address review: built-in select-all, avatars, local selection state
fedirjh May 8, 2026
ebdd219
Drop unused selectAll translation key and sentry label
fedirjh May 8, 2026
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/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ function BaseSelectionList<TItem extends ListItem>({
selectAllTextStyle={style?.listHeaderSelectAllTextStyle}
shouldShowSelectAllButton={!!onSelectAll}
shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow}
selectionButtonPosition={selectionButtonPosition}
/>
);

Expand Down
69 changes: 45 additions & 24 deletions src/components/SelectionList/components/ListHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
import {View} from 'react-native';
import type {ValueOf} from 'type-fest';
import Checkbox from '@components/Checkbox';
import {PressableWithFeedback} from '@components/Pressable';
import type {DataDetailsType, ListItem} from '@components/SelectionList/types';
Expand Down Expand Up @@ -33,6 +34,9 @@ type ListHeaderProps<TItem extends ListItem> = {

/** Whether to prevent default focus when selecting rows */
shouldPreventDefaultFocusOnSelectRow?: boolean;

/** Side on which the select-all checkbox should be rendered, to align with the per-row checkboxes */
selectionButtonPosition?: ValueOf<typeof CONST.SELECTION_BUTTON_POSITION>;
};

function ListHeader<TItem extends ListItem>({
Expand All @@ -44,6 +48,7 @@ function ListHeader<TItem extends ListItem>({
selectAllTextStyle,
shouldShowSelectAllButton,
shouldPreventDefaultFocusOnSelectRow,
selectionButtonPosition = CONST.SELECTION_BUTTON_POSITION.LEFT,
}: ListHeaderProps<TItem>) {
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand All @@ -61,35 +66,51 @@ function ListHeader<TItem extends ListItem>({
e.preventDefault();
};

const checkbox = (
<Checkbox
testID="selection-list-select-all-checkbox"
accessibilityLabel={translate('accessibilityHints.selectAllItems')}
isChecked={dataDetails.allSelected}
isIndeterminate={dataDetails.someSelected}
onPress={onSelectAll}
disabled={allDisabled}
/>
);

const label = !customListHeader && (
<PressableWithFeedback
style={[styles.userSelectNone, styles.flexRow, styles.alignItemsCenter]}
onPress={onSelectAll}
accessibilityLabel={translate('accessibilityHints.selectAllItems')}
sentryLabel={CONST.SENTRY_LABEL.SELECTION_LIST.LIST_HEADER_SELECT_ALL}
accessibilityRole="button"
accessibilityState={{checked: dataDetails.allSelected, disabled: allDisabled}}
disabled={allDisabled}
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
onMouseDown={handleMouseDown}
>
<Text style={[styles.textStrong, styles.ph3, selectAllTextStyle]}>{translate('workspace.people.selectAll')}</Text>
</PressableWithFeedback>
);

const isCheckboxOnRight = selectionButtonPosition === CONST.SELECTION_BUTTON_POSITION.RIGHT;

return (
<View
style={[styles.userSelectNone, styles.peopleRow, styles.ph5, styles.pb3, headerStyle, styles.selectionListStickyHeader]}
accessibilityRole={CONST.ROLE.HEADER}
>
<View style={[styles.flexRow, styles.alignItemsCenter]}>
<Checkbox
testID="selection-list-select-all-checkbox"
accessibilityLabel={translate('accessibilityHints.selectAllItems')}
isChecked={dataDetails.allSelected}
isIndeterminate={dataDetails.someSelected}
onPress={onSelectAll}
disabled={allDisabled}
/>

{!customListHeader && (
<PressableWithFeedback
style={[styles.userSelectNone, styles.flexRow, styles.alignItemsCenter]}
onPress={onSelectAll}
accessibilityLabel={translate('accessibilityHints.selectAllItems')}
sentryLabel={CONST.SENTRY_LABEL.SELECTION_LIST.LIST_HEADER_SELECT_ALL}
accessibilityRole="button"
accessibilityState={{checked: dataDetails.allSelected, disabled: allDisabled}}
disabled={allDisabled}
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
onMouseDown={handleMouseDown}
>
<Text style={[styles.textStrong, styles.ph3, selectAllTextStyle]}>{translate('workspace.people.selectAll')}</Text>
</PressableWithFeedback>
<View style={[styles.flexRow, styles.alignItemsCenter, isCheckboxOnRight && styles.flex1, isCheckboxOnRight && styles.justifyContentBetween]}>
{isCheckboxOnRight ? (
<>
{label}
{checkbox}
</>
) : (
<>
{checkbox}
{label}
</>
)}
</View>
{customListHeader}
Expand Down
9 changes: 9 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5964,6 +5964,15 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
`Sie sind dabei, ${newWorkspaceName ?? ''} mit ${totalMembers ?? 0} Mitgliedern aus dem ursprünglichen Workspace zu erstellen und zu teilen.`,
error: 'Beim Duplizieren deines neuen Workspace ist ein Fehler aufgetreten. Bitte versuche es erneut.',
},
copyPolicySettings: {
error: 'Beim Kopieren der Arbeitsbereich-Einstellungen ist ein Fehler aufgetreten. Bitte versuche es erneut.',
},
copySettings: {
title: 'Einstellungen kopieren',
selectWorkspaces: 'Arbeitsbereiche auswählen',
whichWorkspaces: 'In welche Arbeitsbereiche möchtest du diese Einstellungen kopieren?',
searchPlaceholder: 'Arbeitsbereiche suchen',
},
emptyWorkspace: {
title: 'Du hast keine Arbeitsbereiche',
subtitle: 'Belege erfassen, Auslagen erstatten, Reisen verwalten, Rechnungen versenden und mehr.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5977,6 +5977,15 @@ const translations = {
`You’re about to create and share ${newWorkspaceName ?? ''} with ${totalMembers ?? 0} members from the original workspace.`,
error: 'An error occurred while duplicating your new workspace. Please try again.',
},
copyPolicySettings: {
error: 'An error occurred while copying workspace settings. Please try again.',
},
copySettings: {
title: 'Copy settings',
selectWorkspaces: 'Select workspaces',
whichWorkspaces: 'Which workspaces do you want to copy these settings to?',
searchPlaceholder: 'Search workspaces',
},
emptyWorkspace: {
title: 'No workspaces yet',
subtitle: 'Create a workspace to manage your expenses, reimbursements, and company cards.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5841,6 +5841,15 @@ ${amount} para ${merchant} - ${date}`,
`Estás a punto de crear y compartir ${newWorkspaceName ?? ''} con ${totalMembers ?? 0} miembros del espacio de trabajo original.`,
error: 'Se produjo un error al duplicar tu nuevo espacio de trabajo. Inténtalo de nuevo.',
},
copyPolicySettings: {
error: 'Se produjo un error al copiar la configuración del espacio de trabajo. Inténtalo de nuevo.',
},
copySettings: {
title: 'Copiar configuración',
selectWorkspaces: 'Selecciona espacios de trabajo',
whichWorkspaces: '¿A qué espacios de trabajo quieres copiar esta configuración?',
searchPlaceholder: 'Buscar espacios de trabajo',
},
emptyWorkspace: {
title: 'Aún no hay espacios de trabajo',
subtitle: 'Crea un espacio de trabajo para gestionar tus gastos, reembolsos y tarjetas de empresa.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5984,6 +5984,15 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
`Vous êtes sur le point de créer et de partager ${newWorkspaceName ?? ''} avec ${totalMembers ?? 0} membres de l’espace de travail d’origine.`,
error: 'Une erreur s’est produite lors de la duplication de votre nouvel espace de travail. Veuillez réessayer.',
},
copyPolicySettings: {
error: 'Une erreur s’est produite lors de la copie des paramètres de l’espace de travail. Veuillez réessayer.',
},
copySettings: {
title: 'Copier les paramètres',
selectWorkspaces: 'Sélectionner des espaces de travail',
whichWorkspaces: 'Vers quels espaces de travail voulez-vous copier ces paramètres?',
searchPlaceholder: 'Rechercher des espaces de travail',
},
emptyWorkspace: {
title: 'Vous n’avez aucun espace de travail',
subtitle: 'Suivez les reçus, remboursez les dépenses, gérez les voyages, envoyez des factures, et plus encore.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5954,6 +5954,15 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
`Stai per creare e condividere ${newWorkspaceName ?? ''} con ${totalMembers ?? 0} membri dello spazio di lavoro originale.`,
error: 'Si è verificato un errore durante la duplicazione del tuo nuovo workspace. Riprova.',
},
copyPolicySettings: {
error: 'Si è verificato un errore durante la copia delle impostazioni del workspace. Riprova.',
},
copySettings: {
title: 'Copia impostazioni',
selectWorkspaces: 'Seleziona spazi di lavoro',
whichWorkspaces: 'In quali spazi di lavoro vuoi copiare queste impostazioni?',
searchPlaceholder: 'Cerca spazi di lavoro',
},
emptyWorkspace: {
title: 'Non hai nessuna area di lavoro',
subtitle: 'Tieni traccia delle ricevute, rimborsa le spese, gestisci i viaggi, invia le fatture e altro ancora.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5887,6 +5887,15 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
`元のワークスペースから ${totalMembers ?? 0} 人のメンバーと一緒に、${newWorkspaceName ?? ''} を作成して共有しようとしています。`,
error: '新しいワークスペースの複製中にエラーが発生しました。もう一度お試しください。',
},
copyPolicySettings: {
error: 'ワークスペース設定のコピー中にエラーが発生しました。もう一度お試しください。',
},
copySettings: {
title: '設定をコピー',
selectWorkspaces: 'ワークスペースを選択',
whichWorkspaces: 'どのワークスペースに設定をコピーしますか?',
searchPlaceholder: 'ワークスペースを検索',
},
emptyWorkspace: {
title: 'ワークスペースがありません',
subtitle: '領収書を管理し、経費を精算し、出張を管理し、請求書を送信するなど、さまざまなことができます。',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5934,6 +5934,15 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
`Je staat op het punt ${newWorkspaceName ?? ''} te maken en te delen met ${totalMembers ?? 0} leden van de oorspronkelijke werkruimte.`,
error: 'Er is een fout opgetreden bij het dupliceren van je nieuwe werkruimte. Probeer het opnieuw.',
},
copyPolicySettings: {
error: 'Er is een fout opgetreden bij het kopiëren van de werkruimte-instellingen. Probeer het opnieuw.',
},
copySettings: {
title: 'Instellingen kopiëren',
selectWorkspaces: 'Werkruimtes selecteren',
whichWorkspaces: 'Naar welke werkruimtes wil je deze instellingen kopiëren?',
searchPlaceholder: 'Werkruimtes zoeken',
},
emptyWorkspace: {
title: 'Je hebt geen werkruimtes',
subtitle: 'Volg bonnen, vergoed uitgaven, beheer reizen, verstuur facturen en meer.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5928,6 +5928,15 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
`Za chwilę utworzysz i udostępnisz ${newWorkspaceName ?? ''} ${totalMembers ?? 0} członkom oryginalnego obszaru roboczego.`,
error: 'Wystąpił błąd podczas duplikowania Twojego nowego obszaru roboczego. Spróbuj ponownie.',
},
copyPolicySettings: {
error: 'Wystąpił błąd podczas kopiowania ustawień przestrzeni roboczej. Spróbuj ponownie.',
},
copySettings: {
title: 'Kopiuj ustawienia',
selectWorkspaces: 'Wybierz przestrzenie robocze',
whichWorkspaces: 'Do których przestrzeni roboczych chcesz skopiować te ustawienia?',
searchPlaceholder: 'Wyszukaj przestrzenie robocze',
},
emptyWorkspace: {
title: 'Nie masz żadnych przestrzeni roboczych',
subtitle: 'Śledź paragony, rozliczaj wydatki, zarządzaj podróżami, wysyłaj faktury i wiele więcej.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5934,6 +5934,15 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
`Você está prestes to criar e compartilhar ${newWorkspaceName ?? ''} com ${totalMembers ?? 0} membros do workspace original.`,
error: 'Ocorreu um erro ao duplicar seu novo espaço de trabalho. Tente novamente.',
},
copyPolicySettings: {
error: 'Ocorreu um erro ao copiar as configurações do espaço de trabalho. Tente novamente.',
},
copySettings: {
title: 'Copiar configurações',
selectWorkspaces: 'Selecionar espaços de trabalho',
whichWorkspaces: 'Para quais espaços de trabalho você quer copiar essas configurações?',
searchPlaceholder: 'Pesquisar espaços de trabalho',
},
emptyWorkspace: {
title: 'Você não tem nenhum workspace',
subtitle: 'Controle recibos, reembolse despesas, gerencie viagens, envie faturas e muito mais.',
Expand Down
9 changes: 9 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5789,6 +5789,15 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
`您即将创建并共享 ${newWorkspaceName ?? ''},其中包含来自原始工作区的 ${totalMembers ?? 0} 位成员。`,
error: '复制您的新工作区时发生错误。请重试。',
},
copyPolicySettings: {
error: '复制工作区设置时发生错误。请重试。',
},
copySettings: {
title: '复制设置',
selectWorkspaces: '选择工作区',
whichWorkspaces: '您想将这些设置复制到哪些工作区?',
searchPlaceholder: '搜索工作区',
},
emptyWorkspace: {
title: '你还没有工作区',
subtitle: '跟踪收据、报销费用、管理差旅、发送发票等。',
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CopyPolicySettingsParams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
type CopyPolicySettingsParams = {
/** Source policy ID we're copying settings from */
sourcePolicyID: string;

/** CSV list of target policy IDs to copy settings into */
Expand Down
Loading
Loading