[NEW] Quick action buttons for Omnichannel (#21123)
parent
87b0b0d8aa
commit
9e8c5ec90f
@ -0,0 +1,7 @@ |
||||
import React, { FC } from 'react'; |
||||
|
||||
import './style.css'; |
||||
|
||||
const ModalSeparator: FC<{ text: string }> = ({ text, ...props }) => <h6 className='modal-separator' {...props}>{text}</h6>; |
||||
|
||||
export default ModalSeparator; |
||||
@ -0,0 +1,3 @@ |
||||
import ModalSeparator from './ModalSeparator'; |
||||
|
||||
export default ModalSeparator; |
||||
@ -0,0 +1,20 @@ |
||||
.modal-separator { |
||||
display: flex; |
||||
|
||||
text-align: center; |
||||
text-transform: uppercase; |
||||
|
||||
font-size: 0.625rem; |
||||
font-weight: normal; |
||||
} |
||||
|
||||
.modal-separator::before, |
||||
.modal-separator::after { |
||||
flex: 1; |
||||
|
||||
margin: auto 0.8em; |
||||
|
||||
content: ''; |
||||
|
||||
border-bottom: solid 2px #f2f3f5; |
||||
} |
||||
@ -0,0 +1,64 @@ |
||||
import React, { FC, useCallback, useState, useMemo } from 'react'; |
||||
import { Field, Button, TextInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage'; |
||||
import { useAutoFocus } from '@rocket.chat/fuselage-hooks'; |
||||
|
||||
import { useTranslation } from '../../../contexts/TranslationContext'; |
||||
import { useForm } from '../../../hooks/useForm'; |
||||
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate'; |
||||
|
||||
|
||||
type CloseChatModalProps = { |
||||
onCancel: () => void; |
||||
onConfirm: (comment: string) => void; |
||||
}; |
||||
|
||||
const CloseChatModal: FC<CloseChatModalProps> = ({ onCancel, onConfirm, ...props }) => { |
||||
const t = useTranslation(); |
||||
|
||||
const inputRef = useAutoFocus(true); |
||||
|
||||
const { values, handlers } = useForm({ comment: '' }); |
||||
|
||||
const { comment } = values as { comment: string }; |
||||
const { handleComment } = handlers; |
||||
const [commentError, setCommentError] = useState(''); |
||||
|
||||
|
||||
const handleConfirm = useCallback(() => { |
||||
onConfirm(comment); |
||||
}, [comment, onConfirm]); |
||||
|
||||
useComponentDidUpdate(() => { |
||||
setCommentError(!comment ? t('The_field_is_required', t('Comment')) : ''); |
||||
}, [t, comment]); |
||||
|
||||
const canConfirm = useMemo(() => !!comment, [comment]); |
||||
|
||||
return <Modal {...props}> |
||||
<Modal.Header> |
||||
<Icon name='baloon-close-top-right' size={20}/> |
||||
<Modal.Title>{t('Closing_chat')}</Modal.Title> |
||||
<Modal.Close onClick={onCancel}/> |
||||
</Modal.Header> |
||||
<Modal.Content fontScale='p1'> |
||||
<Box color='neutral-600'>{t('Close_room_description')}</Box> |
||||
<Field marginBlock='x15'> |
||||
<Field.Label>{t('Comment')}*</Field.Label> |
||||
<Field.Row> |
||||
<TextInput ref={inputRef} error={commentError} flexGrow={1} value={comment} onChange={handleComment} placeholder={t('Please_add_a_comment')} /> |
||||
</Field.Row> |
||||
<Field.Error> |
||||
{commentError} |
||||
</Field.Error> |
||||
</Field> |
||||
</Modal.Content> |
||||
<Modal.Footer> |
||||
<ButtonGroup align='end'> |
||||
<Button onClick={onCancel}>{t('Cancel')}</Button> |
||||
<Button disabled={!canConfirm} primary onClick={handleConfirm}>{t('Confirm')}</Button> |
||||
</ButtonGroup> |
||||
</Modal.Footer> |
||||
</Modal>; |
||||
}; |
||||
|
||||
export default CloseChatModal; |
||||
@ -0,0 +1,87 @@ |
||||
import React, { useEffect, useState } from 'react'; |
||||
import { Field, Button, TextAreaInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage'; |
||||
import { useMutableCallback, useAutoFocus } from '@rocket.chat/fuselage-hooks'; |
||||
|
||||
import { useTranslation } from '../../../contexts/TranslationContext'; |
||||
import { useForm } from '../../../hooks/useForm'; |
||||
import ModalSeparator from '../../ModalSeparator'; |
||||
import DepartmentAutoComplete from '../../../views/omnichannel/DepartmentAutoComplete'; |
||||
import { UserAutoComplete } from '../../AutoComplete'; |
||||
import { useEndpointData } from '../../../hooks/useEndpointData'; |
||||
|
||||
const ForwardChatModal = ({ onForward, onCancel, ...props }) => { |
||||
const t = useTranslation(); |
||||
|
||||
const inputRef = useAutoFocus(true); |
||||
|
||||
const { values, handlers } = useForm({ departmentName: '', username: '', comment: '' }); |
||||
const { departmentName, username, comment } = values; |
||||
const [userId, setUserId] = useState(''); |
||||
|
||||
const { handleDepartmentName, handleUsername, handleComment } = handlers; |
||||
const { value } = useEndpointData('GET', `users.info?username=${ username }`); |
||||
|
||||
|
||||
const handleSend = useMutableCallback(() => { |
||||
onForward(departmentName, userId, comment); |
||||
}, [onForward, departmentName, userId, comment]); |
||||
|
||||
|
||||
const onChangeDepartment = useMutableCallback((departmentId) => { |
||||
handleDepartmentName(departmentId); |
||||
handleUsername(''); |
||||
setUserId(''); |
||||
}); |
||||
|
||||
const onChangeUsername = useMutableCallback((username) => { |
||||
handleUsername(username); |
||||
handleDepartmentName(''); |
||||
}); |
||||
|
||||
useEffect(() => { |
||||
if (!username) { return; } |
||||
const fetchData = async () => { |
||||
const { user } = value; |
||||
setUserId(user._id); |
||||
}; |
||||
fetchData(); |
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [username]); |
||||
|
||||
return <Modal {...props}> |
||||
<Modal.Header> |
||||
<Icon name='baloon-arrow-top-right' size={20}/> |
||||
<Modal.Title>{t('Forward_chat')}</Modal.Title> |
||||
<Modal.Close onClick={onCancel}/> |
||||
</Modal.Header> |
||||
<Modal.Content fontScale='p1'> |
||||
<Field mbe={'x30'}> |
||||
<Field.Label>{t('Forward_to_department')}</Field.Label> |
||||
<Field.Row> |
||||
<DepartmentAutoComplete value={departmentName} onChange={onChangeDepartment} flexShrink={1} placeholder={t('Department_name')} /> |
||||
</Field.Row> |
||||
</Field> |
||||
<ModalSeparator text={t('or')} /> |
||||
<Field mbs={'x30'}> |
||||
<Field.Label>{t('Forward_to_user')}</Field.Label> |
||||
<Field.Row> |
||||
<UserAutoComplete flexGrow={1} value={username} onChange={onChangeUsername} placeholder={t('Username')} /> |
||||
</Field.Row> |
||||
</Field> |
||||
<Field marginBlock='x15'> |
||||
<Field.Label>{t('Leave_a_comment')} <Box is='span' color='neutral-600'>({t('Optional')})</Box></Field.Label> |
||||
<Field.Row> |
||||
<TextAreaInput ref={inputRef} rows={8} flexGrow={1} value={comment} onChange={handleComment} /> |
||||
</Field.Row> |
||||
</Field> |
||||
</Modal.Content> |
||||
<Modal.Footer> |
||||
<ButtonGroup align='end'> |
||||
<Button onClick={onCancel}>{t('Cancel')}</Button> |
||||
<Button primary onClick={handleSend}>{t('Forward')}</Button> |
||||
</ButtonGroup> |
||||
</Modal.Footer> |
||||
</Modal>; |
||||
}; |
||||
|
||||
export default ForwardChatModal; |
||||
@ -0,0 +1,44 @@ |
||||
import { Box, Button, ButtonGroup, Icon, Modal } from '@rocket.chat/fuselage'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import { useTranslation } from '../../../contexts/TranslationContext'; |
||||
import { withDoNotAskAgain, RequiredModalProps } from '../../withDoNotAskAgain'; |
||||
|
||||
type ReturnChatQueueModalProps = RequiredModalProps & { |
||||
onMoveChat: () => void; |
||||
onCancel: () => void; |
||||
}; |
||||
|
||||
const ReturnChatQueueModal: FC<ReturnChatQueueModalProps> = ({ |
||||
onCancel, |
||||
onMoveChat, |
||||
confirm = onMoveChat, |
||||
dontAskAgain, |
||||
...props |
||||
}) => { |
||||
const t = useTranslation(); |
||||
|
||||
return <Modal {...props}> |
||||
<Modal.Header> |
||||
<Icon name='burger-arrow-left' size={20}/> |
||||
<Modal.Title>{t('Return_to_the_queue')}</Modal.Title> |
||||
<Modal.Close onClick={onCancel}/> |
||||
</Modal.Header> |
||||
<Modal.Content fontScale='p1'> |
||||
{t('Would_you_like_to_return_the_queue')} |
||||
</Modal.Content> |
||||
<Modal.Footer> |
||||
<Box> |
||||
{dontAskAgain} |
||||
<ButtonGroup align='end'> |
||||
<Button onClick={onCancel}>{t('Cancel')}</Button> |
||||
<Button primary onClick={confirm}>{t('Move_queue')}</Button> |
||||
</ButtonGroup> |
||||
</Box> |
||||
</Modal.Footer> |
||||
</Modal>; |
||||
}; |
||||
|
||||
export const ReturnChatQueueDoNotAskAgain = withDoNotAskAgain<ReturnChatQueueModalProps>(ReturnChatQueueModal); |
||||
|
||||
export default ReturnChatQueueModal; |
||||
@ -0,0 +1,106 @@ |
||||
import React, { FC, useCallback, useEffect, useState, useMemo } from 'react'; |
||||
import { Field, Button, TextInput, Icon, ButtonGroup, Modal } from '@rocket.chat/fuselage'; |
||||
import { useAutoFocus } from '@rocket.chat/fuselage-hooks'; |
||||
|
||||
import { useTranslation } from '../../../contexts/TranslationContext'; |
||||
import { useForm } from '../../../hooks/useForm'; |
||||
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate'; |
||||
import { IRoom } from '../../../../definition/IRoom'; |
||||
|
||||
|
||||
type TranscriptModalProps = { |
||||
email: string; |
||||
room?: IRoom; |
||||
onRequest: (email: string, subject: string) => void; |
||||
onSend?: (email: string, subject: string, token: string) => void; |
||||
onCancel: () => void; |
||||
onDiscard: () => void; |
||||
}; |
||||
|
||||
const TranscriptModal: FC<TranscriptModalProps> = ({ email: emailDefault = '', room, onRequest, onSend, onCancel, onDiscard, ...props }) => { |
||||
const t = useTranslation(); |
||||
|
||||
const inputRef = useAutoFocus(true); |
||||
|
||||
const { values, handlers } = useForm({ email: emailDefault || '', subject: t('Transcript_of_your_livechat_conversation') }); |
||||
|
||||
const { email, subject } = values as { email: string; subject: string }; |
||||
const { handleEmail, handleSubject } = handlers; |
||||
const [emailError, setEmailError] = useState(''); |
||||
const [subjectError, setSubjectError] = useState(''); |
||||
const { transcriptRequest } = room as unknown as IRoom; |
||||
const roomOpen = room && room.open; |
||||
const token = room?.v?.token; |
||||
|
||||
|
||||
const handleRequest = useCallback(() => { |
||||
onRequest(email, subject); |
||||
}, [email, onRequest, subject]); |
||||
|
||||
const handleSend = useCallback(() => { |
||||
onSend && token && onSend(email, subject, token); |
||||
}, [email, onSend, subject, token]); |
||||
|
||||
const handleDiscard = useCallback(() => onDiscard(), [onDiscard]); |
||||
|
||||
useComponentDidUpdate(() => { |
||||
setEmailError(!email ? t('The_field_is_required', t('Email')) : ''); |
||||
}, [t, email]); |
||||
|
||||
useComponentDidUpdate(() => { |
||||
setSubjectError(!subject ? t('The_field_is_required', t('Subject')) : ''); |
||||
}, [t, subject]); |
||||
|
||||
const canSave = useMemo(() => !!subject, [subject]); |
||||
|
||||
useEffect(() => { |
||||
if (transcriptRequest) { |
||||
handleEmail(transcriptRequest.email); |
||||
handleSubject(transcriptRequest.subject); |
||||
} |
||||
}); |
||||
|
||||
return <Modal {...props}> |
||||
<Modal.Header> |
||||
<Icon name='mail-arrow-top-right' size={20}/> |
||||
<Modal.Title>{t('Transcript')}</Modal.Title> |
||||
<Modal.Close onClick={onCancel}/> |
||||
</Modal.Header> |
||||
<Modal.Content fontScale='p1'> |
||||
{!!transcriptRequest && <p>{t('Livechat_transcript_already_requested_warning')}</p>} |
||||
<Field marginBlock='x15'> |
||||
<Field.Label>{t('Email')}*</Field.Label> |
||||
<Field.Row> |
||||
<TextInput disabled={!!emailDefault || !!transcriptRequest} error={emailError} flexGrow={1} value={email} onChange={handleEmail} /> |
||||
</Field.Row> |
||||
<Field.Error> |
||||
{emailError} |
||||
</Field.Error> |
||||
</Field> |
||||
<Field marginBlock='x15'> |
||||
<Field.Label>{t('Subject')}*</Field.Label> |
||||
<Field.Row> |
||||
<TextInput ref={inputRef} disabled={!!transcriptRequest} error={subjectError} flexGrow={1} value={subject} onChange={handleSubject} /> |
||||
</Field.Row> |
||||
<Field.Error> |
||||
{subjectError} |
||||
</Field.Error> |
||||
</Field> |
||||
</Modal.Content> |
||||
<Modal.Footer> |
||||
<ButtonGroup align='end'> |
||||
<Button onClick={onCancel}>{t('Cancel')}</Button> |
||||
{ |
||||
roomOpen && transcriptRequest |
||||
? <Button primary danger onClick={handleDiscard}>{t('Discard')}</Button> |
||||
: <Button disabled={!canSave} primary onClick={handleRequest}>{t('Request')}</Button> |
||||
} |
||||
{ |
||||
!roomOpen && <Button disabled={!canSave} primary onClick={handleSend}>{t('Send')}</Button> |
||||
} |
||||
</ButtonGroup> |
||||
</Modal.Footer> |
||||
</Modal>; |
||||
}; |
||||
|
||||
export default TranscriptModal; |
||||
@ -0,0 +1,222 @@ |
||||
import React, { memo, useContext, useCallback, useState, useEffect, useMemo } from 'react'; |
||||
import { BoxProps, ButtonGroup } from '@rocket.chat/fuselage'; |
||||
import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; |
||||
import { FlowRouter } from 'meteor/kadira:flow-router'; |
||||
import { Session } from 'meteor/session'; |
||||
import toastr from 'toastr'; |
||||
|
||||
import Header from '../../../../../components/Header'; |
||||
import { useTranslation } from '../../../../../contexts/TranslationContext'; |
||||
import { QuickActionsActionConfig, QuickActionsEnum } from '../../../lib/QuickActions'; |
||||
import { useLayout } from '../../../../../contexts/LayoutContext'; |
||||
import { useSetModal } from '../../../../../contexts/ModalContext'; |
||||
import { QuickActionsContext } from '../../../lib/QuickActions/QuickActionsContext'; |
||||
import ReturnChatQueueModal from '../../../../../components/Omnichannel/modals/ReturnChatQueueModal'; |
||||
import ForwardChatModal from '../../../../../components/Omnichannel/modals/ForwardChatModal'; |
||||
import TranscriptModal from '../../../../../components/Omnichannel/modals/TranscriptModal'; |
||||
import CloseChatModal from '../../../../../components/Omnichannel/modals/CloseChatModal'; |
||||
import { handleError } from '../../../../../../app/utils/client'; |
||||
import { IRoom } from '../../../../../../definition/IRoom'; |
||||
import { useAtLeastOnePermission, usePermission, useRole } from '../../../../../contexts/AuthorizationContext'; |
||||
import { useUserId } from '../../../../../contexts/UserContext'; |
||||
import { useOmnichannelRouteConfig } from '../../../../../contexts/OmnichannelContext'; |
||||
import { useEndpoint, useMethod } from '../../../../../contexts/ServerContext'; |
||||
|
||||
|
||||
const QuickActions = ({ room, className }: { room: IRoom; className: BoxProps['className'] }): JSX.Element => { |
||||
const setModal = useSetModal(); |
||||
const { isMobile } = useLayout(); |
||||
const t = useTranslation(); |
||||
const { actions: mapActions } = useContext(QuickActionsContext); |
||||
const actions = (Array.from(mapActions.values()) as QuickActionsActionConfig[]).sort((a, b) => (a.order || 0) - (b.order || 0)); |
||||
const visibleActions = isMobile ? [] : actions.slice(0, 6); |
||||
const [email, setEmail] = useState(''); |
||||
const visitorRoomId = room.v?._id; |
||||
const rid = room._id; |
||||
const uid = useUserId(); |
||||
|
||||
const getVisitorInfo = useEndpoint('GET', `livechat/visitors.info?visitorId=${ visitorRoomId }`); |
||||
|
||||
const getVisitorEmail = useMutableCallback(async () => { |
||||
if (!visitorRoomId) { return; } |
||||
const { visitor: { visitorEmails } } = await getVisitorInfo(); |
||||
setEmail(visitorEmails && visitorEmails.length > 0 && visitorEmails[0].address); |
||||
}); |
||||
|
||||
useEffect(() => { |
||||
getVisitorEmail(); |
||||
}, [room, getVisitorEmail]); |
||||
|
||||
const closeModal = useCallback(() => setModal(null), [setModal]); |
||||
|
||||
const methodReturn = useMethod('livechat:returnAsInquiry'); |
||||
|
||||
const handleMoveChat = useCallback(async () => { |
||||
try { |
||||
await methodReturn(rid); |
||||
closeModal(); |
||||
Session.set('openedRoom', null); |
||||
FlowRouter.go('/home'); |
||||
} catch (error) { |
||||
handleError(error); |
||||
} |
||||
}, [closeModal, methodReturn, rid]); |
||||
|
||||
const requestTranscript = useMethod('livechat:requestTranscript'); |
||||
|
||||
const handleRequestTranscript = useCallback(async (email: string, subject: string) => { |
||||
try { |
||||
await requestTranscript(rid, email, subject); |
||||
closeModal(); |
||||
Session.set('openedRoom', null); |
||||
FlowRouter.go('/home'); |
||||
toastr.success(t('Livechat_transcript_has_been_requested')); |
||||
} catch (error) { |
||||
handleError(error); |
||||
} |
||||
}, [closeModal, requestTranscript, rid, t]); |
||||
|
||||
const sendTranscript = useMethod('livechat:sendTranscript'); |
||||
|
||||
const handleSendTranscript = useCallback(async (email: string, subject: string, token: string) => { |
||||
try { |
||||
await sendTranscript(token, rid, email, subject); |
||||
closeModal(); |
||||
} catch (error) { |
||||
handleError(error); |
||||
} |
||||
}, [closeModal, rid, sendTranscript]); |
||||
|
||||
const discardTranscript = useMethod('livechat:discardTranscript'); |
||||
|
||||
const handleDiscardTranscript = useCallback(async () => { |
||||
try { |
||||
await discardTranscript(rid); |
||||
toastr.success(t('Livechat_transcript_request_has_been_canceled')); |
||||
closeModal(); |
||||
} catch (error) { |
||||
handleError(error); |
||||
} |
||||
}, [closeModal, discardTranscript, rid, t]); |
||||
|
||||
const forwardChat = useMethod('livechat:transfer'); |
||||
|
||||
const handleForwardChat = useCallback(async (departmentId?: string, userId?: string, comment?: string) => { |
||||
if (departmentId && userId) { |
||||
return; |
||||
} |
||||
const transferData: { roomId: string; comment?: string; departmentId?: string; userId?: string } = { |
||||
roomId: rid, |
||||
comment, |
||||
}; |
||||
|
||||
if (departmentId) { transferData.departmentId = departmentId; } |
||||
if (userId) { transferData.userId = userId; } |
||||
|
||||
try { |
||||
await forwardChat(transferData); |
||||
closeModal(); |
||||
toastr.success(t('Transferred')); |
||||
FlowRouter.go('/'); |
||||
} catch (error) { |
||||
handleError(error); |
||||
} |
||||
}, [closeModal, forwardChat, rid, t]); |
||||
|
||||
const closeChat = useMethod('livechat:closeRoom'); |
||||
|
||||
const handleClose = useCallback(async (comment: string) => { |
||||
try { |
||||
await closeChat(rid, comment, { clientAction: true }); |
||||
closeModal(); |
||||
toastr.success(t('Chat_closed_successfully')); |
||||
} catch (error) { |
||||
handleError(error); |
||||
} |
||||
}, [closeChat, closeModal, rid, t]); |
||||
|
||||
const openModal = useMutableCallback((id: string) => { |
||||
switch (id) { |
||||
case QuickActionsEnum.MoveQueue: |
||||
setModal(<ReturnChatQueueModal onMoveChat={handleMoveChat} onCancel={closeModal} />); |
||||
break; |
||||
case QuickActionsEnum.Transcript: |
||||
setModal(<TranscriptModal room={room} email={email} onRequest={handleRequestTranscript} onSend={handleSendTranscript} onDiscard={handleDiscardTranscript} onCancel={closeModal} />); |
||||
break; |
||||
case QuickActionsEnum.ChatForward: |
||||
setModal(<ForwardChatModal onForward={handleForwardChat} onCancel={closeModal} />); |
||||
break; |
||||
case QuickActionsEnum.CloseChat: |
||||
setModal(<CloseChatModal onConfirm={handleClose} onCancel={closeModal} />); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
}); |
||||
|
||||
const actionDefault = useMutableCallback((e) => { |
||||
const index = e.currentTarget.getAttribute('data-quick-actions'); |
||||
const { id } = actions[index]; |
||||
openModal(id); |
||||
}); |
||||
|
||||
const hasManagerRole = useRole('livechat-manager'); |
||||
|
||||
const roomOpen = room && room.open && ((room.servedBy && room.servedBy._id === uid) || hasManagerRole); |
||||
|
||||
const canForwardGuest = usePermission('transfer-livechat-guest'); |
||||
|
||||
const canSendTranscript = usePermission('send-omnichannel-chat-transcript'); |
||||
|
||||
const canCloseRoom = usePermission('close-others-livechat-room'); |
||||
|
||||
const omnichannelRouteConfig = useOmnichannelRouteConfig(); |
||||
|
||||
const hasPermissionButtons = (id: string): boolean => { |
||||
switch (id) { |
||||
case QuickActionsEnum.MoveQueue: |
||||
return !!roomOpen && !!omnichannelRouteConfig?.returnQueue; |
||||
case QuickActionsEnum.ChatForward: |
||||
return !!roomOpen && canForwardGuest; |
||||
case QuickActionsEnum.Transcript: |
||||
return !!email && canSendTranscript; |
||||
case QuickActionsEnum.CloseChat: |
||||
return !!roomOpen && canCloseRoom; |
||||
default: |
||||
break; |
||||
} |
||||
return false; |
||||
}; |
||||
|
||||
const hasPermissionGroup = useAtLeastOnePermission( |
||||
useMemo(() => [ |
||||
'close-others-livechat-room', 'transfer-livechat-guest', |
||||
], []), |
||||
); |
||||
|
||||
return <ButtonGroup mi='x4' medium> |
||||
{ visibleActions.map(({ id, color, icon, title, action = actionDefault }, index) => { |
||||
const props = { |
||||
id, |
||||
icon, |
||||
color, |
||||
title: t(title), |
||||
className, |
||||
tabId: id, |
||||
index, |
||||
primary: false, |
||||
'data-quick-actions': index, |
||||
action, |
||||
key: id, |
||||
}; |
||||
|
||||
if (!hasPermissionGroup || !hasPermissionButtons(id)) { |
||||
return; |
||||
} |
||||
|
||||
return <Header.ToolBoxAction {...props} />; |
||||
})} |
||||
</ButtonGroup>; |
||||
}; |
||||
|
||||
export default memo(QuickActions); |
||||
@ -0,0 +1,4 @@ |
||||
import QuickActions from './QuickActions'; |
||||
|
||||
export default QuickActions; |
||||
export * from './QuickActions'; |
||||
@ -0,0 +1,18 @@ |
||||
import { createContext } from 'react'; |
||||
import { EventHandlerOf } from '@rocket.chat/emitter'; |
||||
|
||||
import { actions, listen, QuickActionsActionConfig, QuickActionsAction, Events } from '.'; |
||||
import './defaultActions'; |
||||
|
||||
|
||||
export type QuickActionsEventHandler = (handler: EventHandlerOf<Events, 'change'>) => Function; |
||||
|
||||
export type ChannelContextValue = { |
||||
actions: Map<QuickActionsActionConfig['id'], QuickActionsAction>; |
||||
listen: QuickActionsEventHandler; |
||||
} |
||||
|
||||
export const QuickActionsContext = createContext<ChannelContextValue>({ |
||||
actions, |
||||
listen, |
||||
}); |
||||
@ -0,0 +1,35 @@ |
||||
import { addAction, QuickActionsEnum } from '.'; |
||||
|
||||
|
||||
addAction(QuickActionsEnum.MoveQueue, { |
||||
groups: ['live'], |
||||
id: QuickActionsEnum.MoveQueue, |
||||
title: 'Move_queue', |
||||
icon: 'burger-arrow-left', |
||||
order: 1, |
||||
}); |
||||
|
||||
addAction(QuickActionsEnum.ChatForward, { |
||||
groups: ['live'], |
||||
id: QuickActionsEnum.ChatForward, |
||||
title: 'Forward_chat', |
||||
icon: 'balloon-arrow-top-right', |
||||
order: 2, |
||||
}); |
||||
|
||||
addAction(QuickActionsEnum.Transcript, { |
||||
groups: ['live'], |
||||
id: QuickActionsEnum.Transcript, |
||||
title: 'Transcript', |
||||
icon: 'mail-arrow-top-right', |
||||
order: 3, |
||||
}); |
||||
|
||||
addAction(QuickActionsEnum.CloseChat, { |
||||
groups: ['live'], |
||||
id: QuickActionsEnum.CloseChat, |
||||
title: 'Close', |
||||
icon: 'balloon-close-top-right', |
||||
order: 4, |
||||
color: 'danger', |
||||
}); |
||||
@ -0,0 +1,47 @@ |
||||
import { ReactNode, MouseEvent } from 'react'; |
||||
import { BoxProps, OptionProps } from '@rocket.chat/fuselage'; |
||||
|
||||
import { IRoom } from '../../../../../definition/IRoom'; |
||||
import { generator, Events as GeneratorEvents } from '../Toolbox/generator'; |
||||
|
||||
|
||||
type QuickActionsHook = ({ room }: { room: IRoom }) => QuickActionsActionConfig | null |
||||
|
||||
type ActionRendererProps = Omit<QuickActionsActionConfig, 'renderAction' | 'groups'> & { |
||||
className: BoxProps['className']; |
||||
tabId: QuickActionsActionConfig['id'] | undefined; |
||||
index: number; |
||||
} |
||||
|
||||
export type ActionRenderer = (props: ActionRendererProps) => ReactNode; |
||||
|
||||
type OptionRendererProps = OptionProps; |
||||
|
||||
export type OptionRenderer = (props: OptionRendererProps) => ReactNode; |
||||
|
||||
export type QuickActionsActionConfig = { |
||||
id: string; |
||||
icon: string; |
||||
color?: string; |
||||
title: string; |
||||
full?: true; |
||||
order?: number; |
||||
renderAction?: ActionRenderer; |
||||
groups: Array<'live'>; |
||||
action?: (e: MouseEvent<HTMLElement>) => void; |
||||
} |
||||
|
||||
export type QuickActionsAction = QuickActionsHook | QuickActionsActionConfig; |
||||
|
||||
const { listen, add: addAction, remove: deleteAction, store: actions } = generator<QuickActionsAction>(); |
||||
|
||||
export type Events = GeneratorEvents<QuickActionsAction>; |
||||
|
||||
export { listen, addAction, deleteAction, actions }; |
||||
|
||||
export enum QuickActionsEnum { |
||||
MoveQueue = 'rocket-move-to-queue', |
||||
ChatForward = 'rocket-chat-forward', |
||||
Transcript = 'rocket-transcript', |
||||
CloseChat = 'rocket-close-chat' |
||||
} |
||||
Loading…
Reference in new issue