MM-61032: Add default_team_id to accept invite flow (#28841)
* add default_team_id to accept invite api * add team selector to accept invite flow UI * e2e * lint/i18n
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5a923e0b94
Коммит
fcded9559c
@@ -156,6 +156,14 @@ export const ModalFieldsetWrapper = styled.div`
|
||||
background: none !important;
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.Input_container {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.DropdownInput.Input_container {
|
||||
margin-top: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ModalLegend = styled.legend`
|
||||
|
||||
@@ -116,7 +116,7 @@ export const useRemoteClusterAcceptInvite = () => {
|
||||
modalId: ModalIdentifiers.SECURE_CONNECTION_ACCEPT_INVITE,
|
||||
dialogType: SecureConnectionAcceptInviteModal,
|
||||
dialogProps: {
|
||||
onConfirm: async (acceptInvite: PartialExcept<RemoteClusterAcceptInvite, 'display_name' | 'invite' | 'password'>) => {
|
||||
onConfirm: async (acceptInvite: PartialExcept<RemoteClusterAcceptInvite, 'display_name' | 'default_team_id' | 'invite' | 'password'>) => {
|
||||
try {
|
||||
setSaving(true);
|
||||
const rc = await Client4.acceptInviteRemoteCluster({
|
||||
|
||||
@@ -13,12 +13,13 @@ import LoadingScreen from 'components/loading_screen';
|
||||
import Input from 'components/widgets/inputs/input/input';
|
||||
|
||||
import {ModalFieldset, ModalParagraph} from '../controls';
|
||||
import {isErrorState, isPendingState} from '../utils';
|
||||
import TeamSelector from '../team_selector';
|
||||
import {isErrorState, isPendingState, useTeamOptions} from '../utils';
|
||||
|
||||
type Props = {
|
||||
creating?: boolean;
|
||||
password?: string;
|
||||
onConfirm: (accept: PartialExcept<RemoteClusterAcceptInvite, 'display_name' | 'invite' | 'password'>) => Promise<RemoteCluster>;
|
||||
onConfirm: (accept: PartialExcept<RemoteClusterAcceptInvite, 'display_name' | 'default_team_id' | 'invite' | 'password'>) => Promise<RemoteCluster>;
|
||||
onCancel?: () => void;
|
||||
onExited: () => void;
|
||||
onHide: () => void;
|
||||
@@ -34,12 +35,16 @@ function SecureConnectionAcceptInviteModal({
|
||||
}: Props) {
|
||||
const {formatMessage} = useIntl();
|
||||
const [displayName, setDisplayName] = useState('');
|
||||
const [defaultTeamId, setDefaultTeamId] = useState('');
|
||||
const [inviteCode, setInviteCode] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [saving, setSaving] = useState<boolean | ClientError>(false);
|
||||
|
||||
const teamsById = useTeamOptions();
|
||||
|
||||
const need = {
|
||||
displayName: !displayName,
|
||||
defaultTeamId: !defaultTeamId,
|
||||
inviteCode: !inviteCode,
|
||||
password: !password,
|
||||
};
|
||||
@@ -50,7 +55,12 @@ function SecureConnectionAcceptInviteModal({
|
||||
setSaving(true);
|
||||
|
||||
try {
|
||||
await onConfirm({display_name: displayName, invite: inviteCode, password});
|
||||
await onConfirm({
|
||||
display_name: displayName,
|
||||
default_team_id: defaultTeamId,
|
||||
invite: inviteCode,
|
||||
password,
|
||||
});
|
||||
setSaving(false);
|
||||
onHide();
|
||||
} catch (err) {
|
||||
@@ -90,6 +100,7 @@ function SecureConnectionAcceptInviteModal({
|
||||
modalHeaderText={title}
|
||||
onExited={onExited}
|
||||
compassDesign={true}
|
||||
bodyOverflowVisible={true}
|
||||
autoCloseOnConfirmButton={false}
|
||||
errorText={isErrorState(saving) && (
|
||||
<FormattedMessage
|
||||
@@ -120,6 +131,23 @@ function SecureConnectionAcceptInviteModal({
|
||||
onChange={handleDisplayNameChange}
|
||||
data-testid='display-name'
|
||||
/>
|
||||
|
||||
<FormattedMessage
|
||||
id={'admin.secure_connections.accept_invite.select_team'}
|
||||
defaultMessage={'Please select the destination team where channels will be placed.'}
|
||||
tagName={ModalParagraph}
|
||||
/>
|
||||
<TeamSelector
|
||||
testId='destination-team-input'
|
||||
value={defaultTeamId}
|
||||
teamsById={teamsById}
|
||||
onChange={setDefaultTeamId}
|
||||
legend={formatMessage({
|
||||
id: 'admin.secure_connections.accept_invite.select_team.legend',
|
||||
defaultMessage: 'Select a team',
|
||||
})}
|
||||
/>
|
||||
|
||||
<FormattedMessage
|
||||
id={'admin.secure_connections.accept_invite.prompt_invite_password'}
|
||||
defaultMessage={'Enter the encrypted invitation code shared to you by the admin of the server you are connecting with.'}
|
||||
|
||||
@@ -12,11 +12,8 @@ import styled from 'styled-components';
|
||||
|
||||
import {GlobeIcon, LockIcon, PlusIcon, ArchiveOutlineIcon} from '@mattermost/compass-icons/components';
|
||||
import {isRemoteClusterPatch, type RemoteCluster} from '@mattermost/types/remote_clusters';
|
||||
import type {Team} from '@mattermost/types/teams';
|
||||
import type {IDMappedObjects} from '@mattermost/types/utilities';
|
||||
|
||||
import {getChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getActiveTeamsList} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {setNavigationBlocked} from 'actions/admin_actions';
|
||||
|
||||
@@ -47,7 +44,7 @@ import {
|
||||
import {useRemoteClusterCreate, useSharedChannelsAdd, useSharedChannelsRemove} from './modals/modal_utils';
|
||||
import TeamSelector from './team_selector';
|
||||
import type {SharedChannelRemoteRow} from './utils';
|
||||
import {getEditLocation, isConfirmed, isErrorState, isPendingState, useRemoteClusterEdit, useSharedChannelRemoteRows} from './utils';
|
||||
import {getEditLocation, isConfirmed, isErrorState, isPendingState, useRemoteClusterEdit, useSharedChannelRemoteRows, useTeamOptions} from './utils';
|
||||
|
||||
import {AdminConsoleListTable} from '../list_table';
|
||||
import SaveChangesPanel from '../team_channel_settings/save_changes_panel';
|
||||
@@ -73,6 +70,8 @@ export default function SecureConnectionDetail(props: Props) {
|
||||
|
||||
const {promptCreate, saving: creating} = useRemoteClusterCreate();
|
||||
|
||||
const teamsById = useTeamOptions();
|
||||
|
||||
useEffect(() => {
|
||||
// keep history cache up to date
|
||||
history.replace({...location, state: currentRemoteCluster});
|
||||
@@ -87,8 +86,6 @@ export default function SecureConnectionDetail(props: Props) {
|
||||
applyPatch({display_name: value});
|
||||
};
|
||||
|
||||
const teams = useSelector(getActiveTeamsList);
|
||||
const teamsById = useMemo(() => teams.reduce<IDMappedObjects<Team>>((teams, team) => ({...teams, [team.id]: team}), {}), [teams]);
|
||||
const handleTeamChange = (teamId: string) => {
|
||||
applyPatch({default_team_id: teamId});
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ export type Props = {
|
||||
teamsById: IDMappedObjects<Team>;
|
||||
onChange: (teamId: string) => void;
|
||||
testId: string;
|
||||
legend?: string;
|
||||
}
|
||||
|
||||
const TeamSelector = (props: Props): JSX.Element => {
|
||||
@@ -40,6 +41,7 @@ const TeamSelector = (props: Props): JSX.Element => {
|
||||
value={value ? {label: value.display_name, value: value.id} : undefined}
|
||||
options={teamValues}
|
||||
name='team_selector'
|
||||
legend={props.legend}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
import type {LocationDescriptor} from 'history';
|
||||
import {DateTime, Interval} from 'luxon';
|
||||
import {useCallback, useEffect, useState} from 'react';
|
||||
import {useDispatch} from 'react-redux';
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import type {ClientError} from '@mattermost/client';
|
||||
import type {Channel} from '@mattermost/types/channels';
|
||||
@@ -17,7 +17,7 @@ import {ChannelTypes} from 'mattermost-redux/action_types';
|
||||
import {getChannel as fetchChannel} from 'mattermost-redux/actions/channels';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {getChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getActiveTeamsList, getTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import type {ActionFuncAsync} from 'mattermost-redux/types/actions';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
@@ -228,6 +228,12 @@ export const useSharedChannelRemoteRows = (remoteId: string, opts: {filter: 'hom
|
||||
return [sharedChannelRemotes, {loading, error, fetch}] as const;
|
||||
};
|
||||
|
||||
export const useTeamOptions = () => {
|
||||
const teams = useSelector(getActiveTeamsList);
|
||||
const teamsById = useMemo(() => teams.reduce<IDMappedObjects<Team>>((teams, team) => ({...teams, [team.id]: team}), {}), [teams]);
|
||||
return teamsById;
|
||||
};
|
||||
|
||||
export const getEditLocation = (rc: RemoteCluster): LocationDescriptor<RemoteCluster> => {
|
||||
return {pathname: `/admin_console/environment/secure_connections/${rc.remote_id}`, state: rc};
|
||||
};
|
||||
|
||||
@@ -2222,6 +2222,8 @@
|
||||
"admin.secure_connections.accept_invite.prompt": "Accept a secure connection from another server",
|
||||
"admin.secure_connections.accept_invite.prompt_invite_password": "Enter the encrypted invitation code shared to you by the admin of the server you are connecting with.",
|
||||
"admin.secure_connections.accept_invite.saving_changes_error": "There was an error while accepting the invite.",
|
||||
"admin.secure_connections.accept_invite.select_team": "Please select the destination team where channels will be placed.",
|
||||
"admin.secure_connections.accept_invite.select_team.legend": "Select a team",
|
||||
"admin.secure_connections.accept_invite.share_title": "Accept a connection invite",
|
||||
"admin.secure_connections.confirm.delete.button": "Yes, delete",
|
||||
"admin.secure_connections.confirm.delete.text": "Are you sure you want to delete the secure connection <strong>{displayName}</strong>?",
|
||||
|
||||
@@ -11,6 +11,7 @@ export type RemoteClusterInvite = {
|
||||
export type RemoteClusterAcceptInvite = {
|
||||
name: string;
|
||||
display_name: string;
|
||||
default_team_id: string;
|
||||
invite: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user