Initial Commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState, useEffect, ChangeEvent, useCallback} from 'react';
|
||||
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
import {documentationLinks} from 'constants/links';
|
||||
|
||||
import {NewTabLink} from 'components/new-tab-link';
|
||||
import Input from 'components/forms/Input';
|
||||
import {CopyIconButton} from 'components/buttons/copy-icon-button';
|
||||
import {DialogForm, Error} from 'components/modal/modal-form-response/style';
|
||||
|
||||
const DeleteChannelForm = ({setIsValid, alias}: {setIsValid: (isValid: boolean) => void; alias: string}): React.JSX.Element => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [enteredAlias, setEnteredAlias] = useState('');
|
||||
|
||||
const validate = useCallback((): boolean => {
|
||||
if (!enteredAlias) {
|
||||
setError('Please enter a channel alias');
|
||||
setIsValid(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enteredAlias !== alias) {
|
||||
setError('Entered alias does not match the channels alias');
|
||||
setIsValid(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setIsValid(true);
|
||||
|
||||
return true;
|
||||
}, [enteredAlias, alias, setIsValid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (enteredAlias.length) {
|
||||
validate();
|
||||
}
|
||||
}, [enteredAlias, validate]);
|
||||
|
||||
const handleEnteredAlias = (event: ChangeEvent<HTMLInputElement>): void => {
|
||||
if (error) {
|
||||
setError(null);
|
||||
}
|
||||
|
||||
setEnteredAlias(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogForm>
|
||||
<h3 className="testId-deleteChannelForm">
|
||||
Delete Channel <NewTabLink link={documentationLinks.deleteChannel} icon={faQuestionCircle} iconColor="black" />
|
||||
</h3>
|
||||
<p>To delete channel, please enter the channel alias:</p>
|
||||
<strong>
|
||||
<CopyIconButton text={alias} quoted />
|
||||
</strong>
|
||||
<Input name="alias" error={!!error} onChange={handleEnteredAlias} value={enteredAlias} />
|
||||
{error && <Error className="error-text testId-displayMessage">{error}</Error>}
|
||||
</DialogForm>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteChannelForm;
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState} from 'react';
|
||||
import {useDispatch} from 'react-redux';
|
||||
|
||||
import LoggerFactory from 'services/logger/LoggerFactory';
|
||||
import {deleteChannel} from 'services/Channel.service';
|
||||
import {listChannels} from 'store/action/channels';
|
||||
|
||||
import {transformToPortalError} from 'utility/error-handler';
|
||||
import {deleteChannelErrorMessages} from 'constants/error-messages';
|
||||
|
||||
import {MultiStepModal} from 'components/modal/multi-step-modal';
|
||||
|
||||
import DeleteChannelForm from './delete-channel-form';
|
||||
import {FormResponse} from 'components/modal/modal-form-response';
|
||||
|
||||
interface IDeleteChannelModal {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
channelId: string;
|
||||
alias: string;
|
||||
redirect?: () => void;
|
||||
}
|
||||
|
||||
export const DeleteChannelModal = ({isOpen, setIsOpen, channelId, alias, redirect}: IDeleteChannelModal): React.JSX.Element => {
|
||||
const logger = LoggerFactory.getLogger('components/channel-icon-menu/delete-channel/DeleteChannelModal');
|
||||
const dispatch = useDispatch();
|
||||
const [isFormValid, setIsFormValid] = useState(false);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [deleteChannelResponse, setDeleteChannelResponse] = useState<{error: string | boolean | Error; status: string | number; data?: any} | null>(null);
|
||||
const handleSubmit = async () => {
|
||||
if (!isFormValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsFetching(true);
|
||||
|
||||
logger.info('Deleting a channel [%s][%s]', alias, channelId);
|
||||
|
||||
const response = await deleteChannel({channelId, alias});
|
||||
|
||||
logger.info('Channel [%s][%s] was successfully deleted', alias, channelId);
|
||||
|
||||
setDeleteChannelResponse({status: 'ok', error: false, data: response});
|
||||
setIsFetching(false);
|
||||
} catch (e) {
|
||||
const {status, message, requestPayload, statusCode} = transformToPortalError(e);
|
||||
|
||||
setIsFetching(false);
|
||||
|
||||
const errorMessage = (deleteChannelErrorMessages as Record<string, string>)[status] || message || deleteChannelErrorMessages['default'];
|
||||
setDeleteChannelResponse({
|
||||
status: statusCode || 'error',
|
||||
error: errorMessage
|
||||
});
|
||||
|
||||
logger.error(`${errorMessage} [%s]`, status, requestPayload);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = (): void => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const onDeleteSuccess = async (): Promise<void> => {
|
||||
setIsOpen(false);
|
||||
|
||||
logger.info('Updating the list of channels after the [%s][%s] channel was deleted', alias, channelId);
|
||||
|
||||
await dispatch(listChannels() as any);
|
||||
|
||||
logger.info('The list of channels was updated successfully');
|
||||
|
||||
if (redirect) {
|
||||
redirect();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<MultiStepModal
|
||||
isOpen={isOpen}
|
||||
closeModal={deleteChannelResponse?.status === 'ok' ? onDeleteSuccess : handleCloseModal}
|
||||
steps={[
|
||||
{
|
||||
title: 'Delete Channel',
|
||||
component: <DeleteChannelForm alias={alias} setIsValid={setIsFormValid} />,
|
||||
saveButton: {
|
||||
text: 'Delete Channel',
|
||||
className: 'testId-deleteChannel',
|
||||
disabled: !isFormValid || isFetching,
|
||||
onClick: handleSubmit
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Delete Channel',
|
||||
component: <FormResponse response={deleteChannelResponse || {error: '', status: '', data: null}} />,
|
||||
cancelDisabled: true,
|
||||
saveButton: {
|
||||
className: 'testId-doneButton',
|
||||
onClick: onDeleteSuccess
|
||||
}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteChannelModal;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
export {default} from './delete-channel-modal';
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState, useEffect, useCallback} from 'react';
|
||||
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
import {CapabilitiesType} from 'constants/capabilities';
|
||||
import {documentationLinks} from 'constants/links';
|
||||
import {Label} from 'components/forms/label';
|
||||
import Checkbox from 'components/forms/Checkbox';
|
||||
import {NewTabLink} from 'components/new-tab-link';
|
||||
import {Dropdown} from 'components/drop-down';
|
||||
import {InputWithTags} from 'components/ui/input-with-tags';
|
||||
import {DialogForm, Error, Options} from 'components/modal/modal-form-response/style';
|
||||
import {Capabilities} from 'components/create-token-components';
|
||||
import Theme from 'theme';
|
||||
|
||||
const forkChannelOption = ['force', 'additive'];
|
||||
const ForkChannelForm = ({
|
||||
setFormData,
|
||||
setIsValid,
|
||||
channelList,
|
||||
alias
|
||||
}: {
|
||||
setFormData: (data: any) => void;
|
||||
setIsValid: (isValid: boolean) => void;
|
||||
channelList: any[];
|
||||
alias: string;
|
||||
}): React.JSX.Element => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [forkOptions, setForkOptions] = useState<string[]>([]);
|
||||
const [destinationChannelId, setDestinationChannelId] = useState('');
|
||||
const [selectedCapabilities, setSelectedCapabilities] = useState<any[]>([]);
|
||||
const [force, additive] = forkChannelOption;
|
||||
const destinationChannelsList = channelList.filter(channel => channel.alias !== alias);
|
||||
|
||||
const validate = useCallback((): boolean => {
|
||||
if (!destinationChannelId) {
|
||||
setIsValid(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setIsValid(true);
|
||||
setFormData({
|
||||
streamCapabilities: selectedCapabilities.map(({value}) => value),
|
||||
streamTags: tags,
|
||||
options: forkOptions,
|
||||
destinationChannelId
|
||||
});
|
||||
|
||||
return true;
|
||||
}, [destinationChannelId, selectedCapabilities, tags, forkOptions, setIsValid, setFormData]);
|
||||
|
||||
useEffect(() => {
|
||||
validate();
|
||||
}, [validate]);
|
||||
|
||||
const handleForkOptions = (value: string) => {
|
||||
if (forkOptions.indexOf(value) > -1) {
|
||||
const currentForkOptions = forkOptions.filter(item => item !== value);
|
||||
|
||||
setForkOptions(currentForkOptions);
|
||||
} else {
|
||||
setForkOptions([...forkOptions, value]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSetDestinationChannelId = (channelAlias: string): void => {
|
||||
const channels = channelList.filter(channel => channel.alias === channelAlias);
|
||||
|
||||
if (channels[0]) {
|
||||
const {channelId} = channels[0];
|
||||
|
||||
setDestinationChannelId(channelId);
|
||||
setError(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogForm>
|
||||
<h3 className="testId-forkChannelForm">
|
||||
Fork Channel <NewTabLink link={documentationLinks.forkChannel} icon={faQuestionCircle} iconColor="black" />
|
||||
</h3>
|
||||
<p>
|
||||
Fork <strong>"{alias}"</strong> to destination channel:
|
||||
</p>
|
||||
<Dropdown
|
||||
name={'alias'}
|
||||
label="Destination Channel Alias"
|
||||
items={destinationChannelsList}
|
||||
itemKey={'alias'}
|
||||
onSelect={handleSetDestinationChannelId}
|
||||
className="testId-destinationChannelAlias"
|
||||
/>
|
||||
{error && <Error>Please select a Channel from the list</Error>}
|
||||
<Capabilities
|
||||
label="Capabilities:"
|
||||
labelColor={Theme.colors.gray900}
|
||||
iconColor={Theme.colors.gray900}
|
||||
capabilitiesSetTitle={CapabilitiesType.Forking}
|
||||
selectedItems={selectedCapabilities}
|
||||
setSelectedItems={setSelectedCapabilities}
|
||||
/>
|
||||
<Label text="Options" />
|
||||
<Options>
|
||||
<Checkbox value={force} id={force} onChange={() => handleForkOptions(force)} checked={forkOptions.indexOf(force) > -1} label={force} />
|
||||
<Checkbox value={additive} id={additive} onChange={() => handleForkOptions(additive)} checked={forkOptions.indexOf(additive) > -1} label={additive} />
|
||||
</Options>
|
||||
<Label htmlFor="input-tag" text="Stream Tags" />
|
||||
<InputWithTags id="input-tag" onTagListChange={setTags} defaultValue={tags} />
|
||||
</DialogForm>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForkChannelForm;
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState} from 'react';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import LoggerFactory from 'services/logger/LoggerFactory';
|
||||
import {forkChannel} from 'services/Channel.service';
|
||||
import {AppStore} from 'store';
|
||||
import {channelsSelector} from 'store/action/channels';
|
||||
|
||||
import {transformToPortalError} from 'utility/error-handler';
|
||||
import {forkChannelErrorMessages} from 'constants/error-messages';
|
||||
|
||||
import {MultiStepModal} from 'components/modal/multi-step-modal';
|
||||
|
||||
import ForkChannelForm from './fork-channel-form';
|
||||
import {FormResponse} from 'components/modal/modal-form-response';
|
||||
|
||||
interface IForkChannelModal {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
channelId: string;
|
||||
alias: string;
|
||||
}
|
||||
|
||||
export const ForkChannelModal = ({isOpen, setIsOpen, channelId, alias}: IForkChannelModal): React.JSX.Element => {
|
||||
const logger = LoggerFactory.getLogger('components/channel-icon-menu/fork-channel/ForkChannelModal');
|
||||
const channelsState = useSelector((state: AppStore) => channelsSelector(state));
|
||||
const channelList = channelsState.channels || [];
|
||||
const [formData, setFormData] = useState({
|
||||
streamCapabilities: [] as string[],
|
||||
streamTags: [] as string[],
|
||||
options: [] as string[],
|
||||
destinationChannelId: ''
|
||||
});
|
||||
const [isFormValid, setIsFormValid] = useState(false);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [forkResponse, setForkResponse] = useState<{error: string | boolean | Error; status: string | number; data?: any} | null>(null);
|
||||
const handleSubmit = async () => {
|
||||
if (!isFormValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsFetching(true);
|
||||
|
||||
const {streamCapabilities, streamTags, options, destinationChannelId} = formData;
|
||||
|
||||
logger.info('Forking a channel [%s] to [%s]', channelId, destinationChannelId);
|
||||
|
||||
const response = await forkChannel({
|
||||
sourceChannelId: channelId,
|
||||
destinationChannelId,
|
||||
streamCapabilities,
|
||||
streamTags,
|
||||
options
|
||||
});
|
||||
|
||||
logger.info('The channel [%s] was successfully forked to [%s]', channelId, destinationChannelId);
|
||||
|
||||
setForkResponse({
|
||||
status: 'ok',
|
||||
error: false,
|
||||
data: response
|
||||
});
|
||||
setIsFetching(false);
|
||||
} catch (e) {
|
||||
const {status, message, requestPayload, statusCode} = transformToPortalError(e);
|
||||
|
||||
setIsFetching(false);
|
||||
|
||||
const errorMessage = (forkChannelErrorMessages as Record<string, string>)[status] || message || forkChannelErrorMessages['default'];
|
||||
setForkResponse({
|
||||
status: statusCode || 'error',
|
||||
error: errorMessage
|
||||
});
|
||||
|
||||
logger.error(`${errorMessage} [%s]`, status, requestPayload);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = (): void => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<MultiStepModal
|
||||
isOpen={isOpen}
|
||||
closeModal={handleCloseModal}
|
||||
steps={[
|
||||
{
|
||||
title: 'Fork Channel',
|
||||
component: <ForkChannelForm alias={alias} channelList={channelList} setIsValid={setIsFormValid} setFormData={setFormData} />,
|
||||
saveButton: {
|
||||
text: 'Fork Channel',
|
||||
className: 'testId-forkChannel',
|
||||
disabled: !isFormValid || isFetching,
|
||||
onClick: handleSubmit
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Fork Channel',
|
||||
component: <FormResponse response={forkResponse || {error: '', status: '', data: null}} />,
|
||||
cancelDisabled: true,
|
||||
saveButton: {className: 'testId-doneButton'}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForkChannelModal;
|
||||
4
src/components/channel-icon-menu/fork-channel/index.tsx
Normal file
4
src/components/channel-icon-menu/fork-channel/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
export {default} from './fork-channel-modal';
|
||||
65
src/components/channel-icon-menu/index.tsx
Normal file
65
src/components/channel-icon-menu/index.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState} from 'react';
|
||||
import {IconDefinition} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import {IconMenu} from 'components/icon-menu';
|
||||
import {IconMenuPosition} from 'components/icon-menu/types';
|
||||
|
||||
import ForkChannelModal from './fork-channel';
|
||||
import DeleteChannelModal from './delete-channel';
|
||||
import KillChannelModal from './kill-channel';
|
||||
|
||||
interface IChannelIconMenu {
|
||||
data: {
|
||||
name: string;
|
||||
channelId: string;
|
||||
applicationId: string;
|
||||
alias: string;
|
||||
};
|
||||
redirect?: () => void;
|
||||
showTail?: boolean;
|
||||
position?: IconMenuPosition;
|
||||
icon?: IconDefinition;
|
||||
margin?: number;
|
||||
}
|
||||
|
||||
export const ChannelIconMenu = (props: IChannelIconMenu): React.JSX.Element => {
|
||||
const {data, icon, redirect, showTail, position, margin} = props;
|
||||
const {alias, channelId} = data;
|
||||
const [isForkModalOpen, setIsForkModalOpen] = useState(false);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [isKillModalOpen, setIsKillModalOpen] = useState(false);
|
||||
const items = [
|
||||
{
|
||||
value: 'delete',
|
||||
title: 'Delete',
|
||||
className: 'testId-deleteMenuItem',
|
||||
onClick: () => setIsDeleteModalOpen(true)
|
||||
},
|
||||
{
|
||||
value: 'kill',
|
||||
title: 'Kill',
|
||||
className: 'testId-killMenuItem',
|
||||
onClick: () => setIsKillModalOpen(true)
|
||||
},
|
||||
{
|
||||
value: 'fork',
|
||||
title: 'Fork',
|
||||
className: 'testId-forkMenuItem',
|
||||
onClick: () => setIsForkModalOpen(true)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconMenu icon={icon} items={items} showTail={showTail} position={position} margin={margin} />
|
||||
{isForkModalOpen && <ForkChannelModal isOpen={isForkModalOpen} setIsOpen={setIsForkModalOpen} channelId={channelId} alias={alias} />}
|
||||
{isDeleteModalOpen && (
|
||||
<DeleteChannelModal isOpen={isDeleteModalOpen} setIsOpen={setIsDeleteModalOpen} channelId={channelId} alias={alias} redirect={redirect} />
|
||||
)}
|
||||
{isKillModalOpen && <KillChannelModal isOpen={isKillModalOpen} setIsOpen={setIsKillModalOpen} channelId={channelId} alias={alias} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
4
src/components/channel-icon-menu/kill-channel/index.tsx
Normal file
4
src/components/channel-icon-menu/kill-channel/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
export {default} from './kill-channel-modal';
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState, useEffect, ChangeEvent, useCallback} from 'react';
|
||||
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import {documentationLinks} from 'constants/links';
|
||||
|
||||
import Checkbox from 'components/forms/Checkbox';
|
||||
import {Label} from 'components/label';
|
||||
import {NewTabLink} from 'components/new-tab-link';
|
||||
import {DialogForm, Error, Options} from 'components/modal/modal-form-response/style';
|
||||
import {CopyIconButton} from 'components/buttons/copy-icon-button';
|
||||
import {Tooltip, Position} from 'components/tooltip';
|
||||
import {Input} from 'components/ui';
|
||||
|
||||
const keepStreams = 'keep-streams';
|
||||
const keepStreamsTooltipMessage = 'Keeps the removed streams alive';
|
||||
const destroyRequired = 'destroy-required';
|
||||
const destroyRequiredTooltipMessage = 'Returns an error if destroying of a stream fails';
|
||||
const defaultReason = 'portal:killed';
|
||||
const KillChannelForm = ({
|
||||
setFormData,
|
||||
setIsValid,
|
||||
alias
|
||||
}: {
|
||||
setFormData: (data: any) => void;
|
||||
setIsValid: (isValid: boolean) => void;
|
||||
alias: string;
|
||||
}): React.JSX.Element => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [enteredAlias, setEnteredAlias] = useState('');
|
||||
const [reason, setReason] = useState(defaultReason);
|
||||
const [options, setOptions] = useState<string[]>([]);
|
||||
|
||||
const validate = useCallback((): boolean => {
|
||||
if (!enteredAlias) {
|
||||
setError('Please enter a channel alias');
|
||||
setIsValid(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enteredAlias !== alias) {
|
||||
setError('Entered alias does not match the channels alias');
|
||||
setIsValid(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setIsValid(true);
|
||||
setFormData({
|
||||
reason,
|
||||
options,
|
||||
enteredAlias
|
||||
});
|
||||
|
||||
return true;
|
||||
}, [enteredAlias, alias, reason, options, setIsValid, setFormData, setError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (enteredAlias.length) {
|
||||
validate();
|
||||
}
|
||||
}, [enteredAlias, validate]);
|
||||
|
||||
const handleAlias = (event: ChangeEvent<HTMLInputElement>): void => {
|
||||
if (error) {
|
||||
setError(null);
|
||||
}
|
||||
|
||||
setEnteredAlias(event.target.value);
|
||||
};
|
||||
|
||||
const handleKillOptions = (value: string) => {
|
||||
if (options.indexOf(value) > -1) {
|
||||
const currentOptions = options.filter(item => item !== value);
|
||||
|
||||
setOptions(currentOptions);
|
||||
} else {
|
||||
setOptions([...options, value]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReason = (event: ChangeEvent<HTMLInputElement>): void => {
|
||||
if (error) {
|
||||
setError(null);
|
||||
}
|
||||
|
||||
setReason(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogForm>
|
||||
<h3 className="testId-killChannelForm">
|
||||
Kill Channel <NewTabLink link={documentationLinks.killChannel} icon={faQuestionCircle} iconColor="black" />
|
||||
</h3>
|
||||
<p>Terminates all streams and removes them from the channel.</p>
|
||||
<p>To kill the channel, please enter the channel alias:</p>
|
||||
<strong>
|
||||
<CopyIconButton text={alias} quoted />
|
||||
</strong>
|
||||
<Input error={!!error} onChange={handleAlias} value={enteredAlias} name="alias" />
|
||||
{error && (
|
||||
<Error className="error-text">
|
||||
Please enter the channel alias <i>{alias}</i>
|
||||
</Error>
|
||||
)}
|
||||
<h5>Kill options:</h5>
|
||||
<Options>
|
||||
<Tooltip position={Position.Bottom} message={keepStreamsTooltipMessage}>
|
||||
<Checkbox
|
||||
value={keepStreams}
|
||||
id={keepStreams}
|
||||
onChange={() => handleKillOptions(keepStreams)}
|
||||
checked={options.indexOf(keepStreams) > -1}
|
||||
label={keepStreams}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip position={Position.Bottom} message={destroyRequiredTooltipMessage}>
|
||||
<Checkbox
|
||||
value={destroyRequired}
|
||||
id={destroyRequired}
|
||||
onChange={() => handleKillOptions(destroyRequired)}
|
||||
checked={options.indexOf(destroyRequired) > -1}
|
||||
label={destroyRequired}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Options>
|
||||
<Label htmlFor="reason" text="Reason:" />
|
||||
<Input id="reason" error={!!error} onChange={handleReason} value={reason} />
|
||||
</DialogForm>
|
||||
);
|
||||
};
|
||||
|
||||
export default KillChannelForm;
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import React, {useState} from 'react';
|
||||
|
||||
import LoggerFactory from 'services/logger/LoggerFactory';
|
||||
import {killChannel} from 'services/Channel.service';
|
||||
|
||||
import {transformToPortalError} from 'utility/error-handler';
|
||||
import {killChannelErrorMessages} from 'constants/error-messages';
|
||||
|
||||
import {MultiStepModal} from 'components/modal/multi-step-modal';
|
||||
|
||||
import KillChannelForm from './kill-channel-form';
|
||||
import {FormResponse} from 'components/modal/modal-form-response';
|
||||
|
||||
interface IKillChannelModal {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
channelId: string;
|
||||
alias: string;
|
||||
}
|
||||
|
||||
export const KillChannelModal = ({isOpen, setIsOpen, channelId, alias}: IKillChannelModal): React.JSX.Element => {
|
||||
const logger = LoggerFactory.getLogger('components/channel-icon-menu/kill-channel/KillChannelModal');
|
||||
const [formData, setFormData] = useState({
|
||||
reason: '',
|
||||
options: [] as string[],
|
||||
enteredAlias: ''
|
||||
});
|
||||
const [isFormValid, setIsFormValid] = useState(false);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [killResponse, setKillResponse] = useState<{error: string | boolean | Error; status: string | number; data?: any} | null>(null);
|
||||
const handleSubmit = async () => {
|
||||
if (!isFormValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsFetching(true);
|
||||
|
||||
const {reason, options} = formData;
|
||||
|
||||
logger.info('Killing a channel [%s] with reason [%s]', channelId, reason);
|
||||
|
||||
const response = await killChannel({
|
||||
channelId,
|
||||
reason,
|
||||
options,
|
||||
enteredAlias: formData.enteredAlias
|
||||
});
|
||||
|
||||
logger.info('The channel [%s] was successfully killed', channelId);
|
||||
|
||||
setKillResponse({
|
||||
status: 'ok',
|
||||
error: false,
|
||||
data: response
|
||||
});
|
||||
setIsFetching(false);
|
||||
} catch (e) {
|
||||
const {status, message, requestPayload, statusCode} = transformToPortalError(e);
|
||||
|
||||
setIsFetching(false);
|
||||
|
||||
const errorMessage = (killChannelErrorMessages as Record<string, string>)[status] || message || killChannelErrorMessages['default'];
|
||||
setKillResponse({
|
||||
status: statusCode || 'error',
|
||||
error: errorMessage
|
||||
});
|
||||
|
||||
logger.error(`${errorMessage} [%s]`, status, requestPayload);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = (): void => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<MultiStepModal
|
||||
isOpen={isOpen}
|
||||
closeModal={handleCloseModal}
|
||||
steps={[
|
||||
{
|
||||
title: 'Kill Channel',
|
||||
component: <KillChannelForm alias={alias} setIsValid={setIsFormValid} setFormData={setFormData} />,
|
||||
saveButton: {
|
||||
text: 'Kill Channel',
|
||||
className: 'testId-killChannel',
|
||||
disabled: !isFormValid || isFetching,
|
||||
onClick: handleSubmit
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Kill Channel',
|
||||
component: <FormResponse response={killResponse || {error: '', status: '', data: null}} />,
|
||||
cancelDisabled: true,
|
||||
saveButton: {className: 'testId-doneButton'}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default KillChannelModal;
|
||||
Reference in New Issue
Block a user