Skip to content

Commit d73ad24

Browse files
authored
Merge pull request #81973 from software-mansion-labs/feat/groups-move-members
[Domain Control] [Release 4] [FE] Create MoveUsersBetweenGroupsPage
2 parents b40475a + d221d96 commit d73ad24

32 files changed

Lines changed: 708 additions & 42 deletions

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9562,6 +9562,7 @@ const CONST = {
95629562
},
95639563
BULK_ACTION_TYPES: {
95649564
CLOSE_ACCOUNT: 'closeAccount',
9565+
MOVE_TO_GROUP: 'moveToGroup',
95659566
},
95669567
},
95679568
},

src/ONYXKEYS.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,9 @@ const ONYXKEYS = {
426426
/** A map of the user's security group IDs they belong to in specific domains */
427427
MY_DOMAIN_SECURITY_GROUPS: 'myDomainSecurityGroups',
428428

429+
/** Selected domain member account IDs for the move-to-group operation */
430+
DOMAIN_MEMBERS_SELECTED_FOR_MOVE: 'domainMembersSelectedForMove',
431+
429432
// The theme setting set by the user in preferences.
430433
// This can be either "light", "dark", "system", "light-contrast", "dark-contrast" or "system-contrast"
431434
PREFERRED_THEME: 'nvp_preferredTheme',
@@ -1430,6 +1433,7 @@ type OnyxValuesMapping = {
14301433
[ONYXKEYS.IS_BETA]: boolean;
14311434
[ONYXKEYS.IS_CHECKING_PUBLIC_ROOM]: boolean;
14321435
[ONYXKEYS.MY_DOMAIN_SECURITY_GROUPS]: Record<string, string>;
1436+
[ONYXKEYS.DOMAIN_MEMBERS_SELECTED_FOR_MOVE]: string[];
14331437
[ONYXKEYS.VERIFY_3DS_SUBSCRIPTION]: string;
14341438
[ONYXKEYS.PREFERRED_THEME]: ValueOf<typeof CONST.THEME>;
14351439
[ONYXKEYS.MAPBOX_ACCESS_TOKEN]: OnyxTypes.MapboxAccessToken;

src/ROUTES.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4087,6 +4087,10 @@ const ROUTES = {
40874087
route: 'domain/:domainAccountID/members/:accountID/reset-two-factor-auth',
40884088
getRoute: (domainAccountID: number, accountID: number) => `domain/${domainAccountID}/members/${accountID}/reset-two-factor-auth` as const,
40894089
},
4090+
DOMAIN_MEMBERS_MOVE_TO_GROUP: {
4091+
route: 'domain/:domainAccountID/members/move',
4092+
getRoute: (domainAccountID: number) => `domain/${domainAccountID}/members/move` as const,
4093+
},
40904094

40914095
MULTIFACTOR_AUTHENTICATION_MAGIC_CODE: `multifactor-authentication/magic-code`,
40924096
MULTIFACTOR_AUTHENTICATION_BIOMETRICS_TEST: 'multifactor-authentication/scenario/biometrics-test',

src/SCREENS.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ const SCREENS = {
10031003
MEMBER_RESET_TWO_FACTOR_AUTH: 'Member_Reset_Two_Factor_Auth',
10041004
MEMBER_FORCE_TWO_FACTOR_AUTH: 'Member_Force_Two_Factor_Auth',
10051005
MEMBER_LOCK_ACCOUNT: 'Member_Lock_Account',
1006+
MEMBERS_MOVE_TO_GROUP: 'Members_Move_To_Group',
10061007
},
10071008
MULTIFACTOR_AUTHENTICATION: {
10081009
MAGIC_CODE: 'Multifactor_Authentication_Magic_Code',

src/components/Search/FilterDropdowns/SingleSelectPopup.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useCallback, useMemo, useState} from 'react';
1+
import React, {Activity, useCallback, useMemo, useState} from 'react';
22
import {View} from 'react-native';
33
import type {StyleProp, ViewStyle} from 'react-native';
44
import SelectionList from '@components/SelectionList';
@@ -46,6 +46,9 @@ type SingleSelectPopupProps<T> = {
4646

4747
/** Custom styles for the SelectionList */
4848
selectionListStyle?: SelectionListStyle;
49+
50+
/** Whether SelectionList of popup should stay mounted when popup is not visible. */
51+
shouldShowList?: boolean;
4952
};
5053

5154
function SingleSelectPopup<T extends string>({
@@ -59,6 +62,7 @@ function SingleSelectPopup<T extends string>({
5962
defaultValue,
6063
style,
6164
selectionListStyle,
65+
shouldShowList = true,
6266
}: SingleSelectPopupProps<T>) {
6367
const {translate} = useLocalize();
6468
const styles = useThemeStyles();
@@ -139,17 +143,19 @@ function SingleSelectPopup<T extends string>({
139143
style={style}
140144
>
141145
<View style={[styles.getSelectionListPopoverHeight(options.length || 1, windowHeight, isSearchable ?? false, isInLandscapeMode, shouldShowLabel)]}>
142-
<SelectionList
143-
data={options}
144-
shouldSingleExecuteRowSelect
145-
ListItem={SingleSelectListItem}
146-
onSelectRow={updateSelectedItem}
147-
textInputOptions={textInputOptions}
148-
style={{contentContainerStyle: [styles.pb0], ...selectionListStyle}}
149-
shouldUpdateFocusedIndex={isSearchable}
150-
initiallyFocusedItemKey={isSearchable ? value?.value : undefined}
151-
shouldShowLoadingPlaceholder={!noResultsFound}
152-
/>
146+
<Activity mode={shouldShowList ? 'visible' : 'hidden'}>
147+
<SelectionList
148+
data={options}
149+
shouldSingleExecuteRowSelect
150+
ListItem={SingleSelectListItem}
151+
onSelectRow={updateSelectedItem}
152+
textInputOptions={textInputOptions}
153+
style={{contentContainerStyle: [styles.pb0], ...selectionListStyle}}
154+
shouldUpdateFocusedIndex={isSearchable}
155+
initiallyFocusedItemKey={isSearchable ? value?.value : undefined}
156+
shouldShowLoadingPlaceholder={!noResultsFound}
157+
/>
158+
</Activity>
153159
</View>
154160
</BasePopup>
155161
);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {useEffect} from 'react';
2+
import ONYXKEYS from '@src/ONYXKEYS';
3+
import useOnyx from './useOnyx';
4+
import usePrevious from './usePrevious';
5+
6+
/**
7+
* Clears local member selection after move flow completion by reacting to a
8+
* transition of `DOMAIN_MEMBERS_SELECTED_FOR_MOVE` from non-empty to empty.
9+
*/
10+
function useClearSelectedDomainMembersOnMoveComplete(clearSelectedMembers: () => void) {
11+
const [selectedMemberAccountIDs] = useOnyx(ONYXKEYS.DOMAIN_MEMBERS_SELECTED_FOR_MOVE, {initWithStoredValues: false});
12+
const prevSelectedMemberAccountIDs = usePrevious(selectedMemberAccountIDs);
13+
const selectedCount = selectedMemberAccountIDs?.length ?? 0;
14+
const previousSelectedCount = prevSelectedMemberAccountIDs?.length ?? 0;
15+
16+
useEffect(() => {
17+
const hadSelectionBefore = previousSelectedCount > 0;
18+
const hasNoSelectionNow = selectedCount === 0;
19+
20+
if (!hadSelectionBefore || !hasNoSelectionNow) {
21+
return;
22+
}
23+
24+
clearSelectedMembers();
25+
}, [selectedCount, previousSelectedCount, clearSelectedMembers]);
26+
}
27+
28+
export default useClearSelectedDomainMembersOnMoveComplete;

src/languages/de.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9060,12 +9060,15 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
90609060
removeMember: 'Dieser Benutzer kann nicht entfernt werden. Bitte versuche es erneut.',
90619061
addMember: 'Dieses Mitglied kann nicht hinzugefügt werden. Bitte versuche es erneut.',
90629062
vacationDelegate: 'Dieser Benutzer kann nicht als Urlaubsvertretung festgelegt werden. Bitte versuche es erneut.',
9063+
moveMember: 'Dieses Mitglied kann nicht verschoben werden. Bitte versuchen Sie es erneut.',
90639064
},
90649065
reportSuspiciousActivityPrompt: (email: string) =>
90659066
`Bist du sicher? Dadurch wird das Konto von <strong>${email}</strong> gesperrt. <br /><br /> Unser Team wird das Konto anschließend überprüfen und unbefugten Zugriff entfernen. Um den Zugriff wiederherzustellen, muss die Person mit Concierge zusammenarbeiten.`,
90669067
reportSuspiciousActivityConfirmationPrompt: 'Wir überprüfen das Konto, um sicherzustellen, dass es sicher entsperrt werden kann, und melden uns bei Fragen über Concierge.',
90679068
cannotSetVacationDelegateForMember: (email: string) => `Du kannst keine Urlaubsvertretung für ${email} festlegen, weil sie derzeit die Vertretung für folgende Mitglieder sind:`,
90689069
emptyMembers: {title: 'Keine Mitglieder in dieser Gruppe', subtitle: 'Fügen Sie ein Mitglied hinzu oder versuchen Sie, den Filter oben zu ändern.'},
9070+
moveToGroup: 'In Gruppe verschieben',
9071+
chooseWhereToMove: ({count}: {count: number}) => `Wählen Sie aus, wohin Sie ${count} ${count === 1 ? 'Mitglied' : 'Mitglieder'} verschieben möchten.`,
90699072
},
90709073
common: {
90719074
settings: 'Einstellungen',

src/languages/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9041,9 +9041,12 @@ const translations = {
90419041
one: 'Close account',
90429042
other: 'Close accounts',
90439043
}),
9044+
moveToGroup: 'Move to group',
9045+
chooseWhereToMove: ({count}: {count: number}) => `Choose where to move ${count} ${count === 1 ? 'member' : 'members'}.`,
90449046
error: {
90459047
addMember: 'Unable to add this member. Please try again.',
90469048
removeMember: 'Unable to remove this user. Please try again.',
9049+
moveMember: 'Unable to move this member. Please try again.',
90479050
vacationDelegate: 'Unable to set this user as a vacation delegate. Please try again.',
90489051
},
90499052
cannotSetVacationDelegateForMember: (email: string) => `You can't set a vacation delegate for ${email} because they're currently the delegate for the following members:`,

src/languages/es.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9279,6 +9279,7 @@ ${amount} para ${merchant} - ${date}`,
92799279
removeMember: 'No se pudo eliminar a este usuario. Por favor, inténtalo de nuevo.',
92809280
addMember: 'No se pudo añadir este miembro. Por favor, inténtalo de nuevo.',
92819281
vacationDelegate: 'No se pudo establecer a este usuario como delegado de vacaciones. Por favor, inténtalo de nuevo.',
9282+
moveMember: 'No se pudo mover este miembro. Por favor, inténtalo de nuevo.',
92829283
},
92839284
cannotSetVacationDelegateForMember: (email: string) =>
92849285
`No puedes establecer un delegado de vacaciones para ${email} porque actualmente es el delegado de los siguientes miembros:`,
@@ -9287,6 +9288,8 @@ ${amount} para ${merchant} - ${date}`,
92879288
`¿Estás seguro? Esto bloqueará la cuenta de <strong>${email}</strong>. <br /><br /> Nuestro equipo revisará la cuenta y eliminará cualquier acceso no autorizado. Para recuperar el acceso, deberá comunicarse con Concierge.`,
92889289
reportSuspiciousActivityConfirmationPrompt:
92899290
'Revisaremos la cuenta para verificar que sea seguro desbloquearla y nos comunicaremos a través de Concierge si tenemos alguna pregunta.',
9291+
moveToGroup: 'Mover al grupo',
9292+
chooseWhereToMove: ({count}: {count: number}) => `Elige a dónde mover ${count} ${count === 1 ? 'miembro' : 'miembros'}.`,
92909293
},
92919294
common: {
92929295
settings: 'Configuración',

src/languages/fr.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9081,6 +9081,7 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`,
90819081
removeMember: 'Impossible de supprimer cet utilisateur. Veuillez réessayer.',
90829082
addMember: 'Impossible d’ajouter ce membre. Veuillez réessayer.',
90839083
vacationDelegate: 'Impossible de définir cet utilisateur comme délégué de vacances. Veuillez réessayer.',
9084+
moveMember: 'Impossible de déplacer ce membre. Veuillez réessayer.',
90849085
},
90859086
cannotSetVacationDelegateForMember: (email: string) =>
90869087
`Vous ne pouvez pas définir un délégué de vacances pour ${email}, car cette personne est actuellement le délégué des membres suivants :`,
@@ -9089,6 +9090,8 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`,
90899090
reportSuspiciousActivityConfirmationPrompt:
90909091
'Nous examinerons le compte pour vérifier qu’il est sûr de le déverrouiller et nous vous contacterons via Concierge si nous avons des questions.',
90919092
emptyMembers: {title: 'Aucun membre dans ce groupe', subtitle: 'Ajoutez un membre ou essayez de modifier le filtre ci-dessus.'},
9093+
moveToGroup: 'Déplacer vers le groupe',
9094+
chooseWhereToMove: ({count}: {count: number}) => `Choisissez où déplacer ${count} ${count === 1 ? 'membre' : 'membres'}.`,
90929095
},
90939096
common: {
90949097
settings: 'Paramètres',

0 commit comments

Comments
 (0)