RocketChat/Rocket.Chat

View on GitHub
apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx

Summary

Maintainability
D
2 days
Test Coverage
import type { ILivechatAgent, ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings';
import {
    Field,
    FieldLabel,
    FieldGroup,
    FieldRow,
    TextInput,
    Button,
    Box,
    MultiSelect,
    Icon,
    Select,
    ContextualbarFooter,
    ButtonGroup,
    CheckOption,
} from '@rocket.chat/fuselage';
import type { SelectOption } from '@rocket.chat/fuselage';
import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useToastMessageDispatch, useSetting, useMethod, useTranslation, useEndpoint, useRouter } from '@rocket.chat/ui-contexts';
import { useQueryClient } from '@tanstack/react-query';
import React, { useMemo } from 'react';
import { useForm, Controller, FormProvider } from 'react-hook-form';

import { getUserEmailAddress } from '../../../../lib/getUserEmailAddress';
import {
    Contextualbar,
    ContextualbarTitle,
    ContextualbarClose,
    ContextualbarHeader,
    ContextualbarScrollableContent,
} from '../../../components/Contextualbar';
import { UserInfoAvatar } from '../../../components/UserInfo';
import { MaxChatsPerAgent } from '../additionalForms';

type AgentEditProps = {
    agentData: Pick<ILivechatAgent, '_id' | 'username' | 'name' | 'status' | 'statusLivechat' | 'emails' | 'livechat'>;
    userDepartments: (Pick<ILivechatDepartmentAgents, 'departmentId'> & { departmentName: string })[];
    availableDepartments: Pick<ILivechatDepartment, '_id' | 'name' | 'archived'>[];
};

const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEditProps) => {
    const t = useTranslation();
    const router = useRouter();
    const queryClient = useQueryClient();

    const voipEnabled = useSetting('VoIP_Enabled');
    const dispatchToastMessage = useToastMessageDispatch();

    const { name, username, livechat, statusLivechat } = agentData;

    const email = getUserEmailAddress(agentData);

    const departments: Pick<ILivechatDepartment, '_id' | 'name' | 'archived'>[] = useMemo(() => {
        const pending = userDepartments
            .filter(({ departmentId }) => !availableDepartments.find((dep) => dep._id === departmentId))
            .map((dep) => ({
                _id: dep.departmentId,
                name: dep.departmentName,
            }));

        return [...availableDepartments, ...pending];
    }, [availableDepartments, userDepartments]);

    const departmentsOptions: SelectOption[] = useMemo(() => {
        const archivedDepartment = (name: string, archived?: boolean) => (archived ? `${name} [${t('Archived')}]` : name);

        return (
            departments.map(({ _id, name, archived }) =>
                name ? [_id, archivedDepartment(name, archived)] : [_id, archivedDepartment(_id, archived)],
            ) || []
        );
    }, [departments, t]);

    const statusOptions: SelectOption[] = useMemo(
        () => [
            ['available', t('Available')],
            ['not-available', t('Not_Available')],
        ],
        [t],
    );

    const initialDepartmentValue = useMemo(() => userDepartments.map(({ departmentId }) => departmentId) || [], [userDepartments]);

    const methods = useForm({
        values: {
            name,
            username,
            email,
            departments: initialDepartmentValue,
            status: statusLivechat,
            maxNumberSimultaneousChat: livechat?.maxNumberSimultaneousChat || 0,
            voipExtension: '',
        },
    });

    const {
        control,
        handleSubmit,
        reset,
        formState: { isDirty },
    } = methods;

    const saveAgentInfo = useMethod('livechat:saveAgentInfo');
    const saveAgentStatus = useEndpoint('POST', '/v1/livechat/agent.status');

    const handleSave = useMutableCallback(async ({ status, departments, ...data }) => {
        try {
            await saveAgentStatus({ agentId: agentData._id, status });
            await saveAgentInfo(agentData._id, data, departments);
            dispatchToastMessage({ type: 'success', message: t('Success') });
            router.navigate('/omnichannel/agents');
            queryClient.invalidateQueries(['livechat-agents']);
        } catch (error) {
            dispatchToastMessage({ type: 'error', message: error });
        }
    });

    const formId = useUniqueId();
    const nameField = useUniqueId();
    const usernameField = useUniqueId();
    const emailField = useUniqueId();
    const departmentsField = useUniqueId();
    const statusField = useUniqueId();
    const voipExtensionField = useUniqueId();

    return (
        <Contextualbar data-qa-id='agent-edit-contextual-bar'>
            <ContextualbarHeader>
                <ContextualbarTitle>{t('Edit_User')}</ContextualbarTitle>
                <ContextualbarClose onClick={() => router.navigate('/omnichannel/agents')} />
            </ContextualbarHeader>
            <ContextualbarScrollableContent>
                <FormProvider {...methods}>
                    <form id={formId} onSubmit={handleSubmit(handleSave)}>
                        {username && (
                            <Box display='flex' flexDirection='column' alignItems='center'>
                                <UserInfoAvatar data-qa-id='agent-edit-avatar' username={username} />
                            </Box>
                        )}
                        <FieldGroup>
                            <Field>
                                <FieldLabel htmlFor={nameField}>{t('Name')}</FieldLabel>
                                <FieldRow>
                                    <Controller
                                        name='name'
                                        control={control}
                                        render={({ field }) => <TextInput id={nameField} data-qa-id='agent-edit-name' {...field} readOnly />}
                                    />
                                </FieldRow>
                            </Field>
                            <Field>
                                <FieldLabel htmlFor={usernameField}>{t('Username')}</FieldLabel>
                                <FieldRow>
                                    <Controller
                                        name='username'
                                        control={control}
                                        render={({ field }) => (
                                            <TextInput
                                                id={usernameField}
                                                data-qa-id='agent-edit-username'
                                                {...field}
                                                readOnly
                                                addon={<Icon name='at' size='x20' />}
                                            />
                                        )}
                                    />
                                </FieldRow>
                            </Field>
                            <Field>
                                <FieldLabel htmlFor={emailField}>{t('Email')}</FieldLabel>
                                <FieldRow>
                                    <Controller
                                        name='email'
                                        control={control}
                                        render={({ field }) => (
                                            <TextInput
                                                id={emailField}
                                                data-qa-id='agent-edit-email'
                                                {...field}
                                                readOnly
                                                addon={<Icon name='mail' size='x20' />}
                                            />
                                        )}
                                    />
                                </FieldRow>
                            </Field>
                            <Field>
                                <FieldLabel htmlFor={departmentsField}>{t('Departments')}</FieldLabel>
                                <FieldRow>
                                    <Controller
                                        name='departments'
                                        control={control}
                                        render={({ field }) => (
                                            <MultiSelect
                                                id={departmentsField}
                                                data-qa-id='agent-edit-departments'
                                                options={departmentsOptions}
                                                {...field}
                                                placeholder={t('Select_an_option')}
                                                renderItem={({ label, ...props }) => (
                                                    <CheckOption {...props} label={<span style={{ whiteSpace: 'normal' }}>{label}</span>} />
                                                )}
                                            />
                                        )}
                                    />
                                </FieldRow>
                            </Field>
                            <Field>
                                <FieldLabel htmlFor={statusField}>{t('Status')}</FieldLabel>
                                <FieldRow>
                                    <Controller
                                        name='status'
                                        control={control}
                                        render={({ field }) => (
                                            <Select
                                                id={statusField}
                                                data-qa-id='agent-edit-status'
                                                {...field}
                                                options={statusOptions}
                                                placeholder={t('Select_an_option')}
                                            />
                                        )}
                                    />
                                </FieldRow>
                            </Field>
                            {MaxChatsPerAgent && <MaxChatsPerAgent />}
                            {voipEnabled && (
                                <Field>
                                    <FieldLabel htmlFor={voipExtensionField}>{t('VoIP_Extension')}</FieldLabel>
                                    <FieldRow>
                                        <Controller
                                            name='voipExtension'
                                            control={control}
                                            render={({ field }) => <TextInput id={voipExtensionField} {...field} data-qa-id='agent-edit-voip-extension' />}
                                        />
                                    </FieldRow>
                                </Field>
                            )}
                        </FieldGroup>
                    </form>
                </FormProvider>
            </ContextualbarScrollableContent>
            <ContextualbarFooter>
                <ButtonGroup stretch>
                    <Button data-qa-id='agent-edit-reset' type='reset' disabled={!isDirty} onClick={() => reset()}>
                        {t('Reset')}
                    </Button>
                    <Button form={formId} primary type='submit' data-qa-id='agent-edit-save' disabled={!isDirty}>
                        {t('Save')}
                    </Button>
                </ButtonGroup>
            </ContextualbarFooter>
        </Contextualbar>
    );
};

export default AgentEdit;