-
Notifications
You must be signed in to change notification settings - Fork 13.8k
[NEW] Quick action buttons for Omnichannel #21123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 31 commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
8d73776
new quick action buttons at top chat
rafaelblink b2e163d
change color and order of close button
rafaelblink ecd0e0c
header to accept color attr
rafaelblink 1b132f2
return chat queue modal
rafaelblink 76031c2
transcript modal core
rafaelblink 30f2d99
removing unnecessary comments and create forward chat modal.
rafaelblink 682c58d
return queue modal with method
rafaelblink 3d143ee
start transcript modal
rafaelblink 9e66b7d
discard transcript initialized
rafaelblink dc614f6
already transcript request raw message at the center
rafaelblink 293ac25
Forward Chat modal component in place
rafaelblink 0f3f90f
temporary unused value
rafaelblink 5c26331
return queue modal with footer buttons end aligned
rafaelblink 6a0448e
User and department auto completes in place and start with Forward ev…
rafaelblink c541a74
transfering chats via quick buttons
rafaelblink c1fb33b
transfering chats via quick buttons
rafaelblink 7b1ab91
Merge remote-tracking branch 'origin/omnichannel/new-quick-action-but…
rafaelblink 3984971
merge
rafaelblink 9e79cfb
show quick actions only on livechat rooms and remove unused props.
rafaelblink b18a44b
show transcript email requested
rafaelblink 7f3c5a2
start permissions at actions buttons
rafaelblink defee01
missed import
rafaelblink 26299e1
close permission
rafaelblink 1327b48
return to queue permissions
rafaelblink b60dd2e
polishing permissions and implements send transcript button
rafaelblink 7584bc6
Merge branch 'develop' into omnichannel/new-quick-action-buttons
rafaelblink 0b6e430
improve labels and textarea size.
rafaelblink ffe35ee
missed interface status attr
rafaelblink de52caa
id optional attr
rafaelblink 2de4a63
close modal in place
rafaelblink 17bd1b2
Merge branch 'develop' into omnichannel/new-quick-action-buttons
renatobecker d39c37e
transform tsx into js in order to pass on tests.
rafaelblink 6daa1d9
Update packages/rocketchat-i18n/i18n/en.i18n.json
rafaelblink a1d35e5
change useEndpointAction to useEndpoint
rafaelblink cc75315
Merge remote-tracking branch 'origin/omnichannel/new-quick-action-but…
rafaelblink dbad9a6
change modals omnichannel directory
rafaelblink 397a129
solve github LGTM
rafaelblink 1494c23
Merge branch 'develop' into omnichannel/new-quick-action-buttons
renatobecker e25cc96
remove unnecessary stuff
rafaelblink 4e9240b
Merge remote-tracking branch 'origin/omnichannel/new-quick-action-but…
rafaelblink c4f8886
Merge branch 'develop' into omnichannel/new-quick-action-buttons
rafaelblink c7777cb
change useEndPoint to useEndpointData
rafaelblink 671cb7f
change transcript already requested to the top
rafaelblink 5689b61
useAutoFocus and use mutable callbacks
rafaelblink 0330a03
Gazzo PR review changes
rafaelblink c5ba86e
using useMethod instead useMethod
rafaelblink 44f3c1b
fix tests
rafaelblink File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import React, { FC, useCallback, useEffect, useRef, useState, useMemo } from 'react'; | ||
| import { Field, Button, TextInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage'; | ||
|
|
||
| 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 ref = useRef<HTMLInputElement>(); | ||
|
|
||
| useEffect(() => { | ||
| if (typeof ref?.current?.focus === 'function') { | ||
| ref.current.focus(); | ||
| } | ||
| }, [ref]); | ||
|
|
||
| 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 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; | ||
| import { Field, Button, TextAreaInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage'; | ||
| import { useMutableCallback } 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 { useEndpointAction } from '../hooks/useEndpointAction'; | ||
|
|
||
| type ForwardChatModalProps = { | ||
| onForward: (departmentName?: string, userId?: string, comment?: string) => void; | ||
| onCancel: () => void; | ||
| }; | ||
|
|
||
| const ForwardChatModal: FC<ForwardChatModalProps> = ({ onForward, onCancel, ...props }) => { | ||
| const t = useTranslation(); | ||
|
|
||
| const ref = useRef<HTMLInputElement>(); | ||
|
|
||
| useEffect(() => { | ||
| if (typeof ref?.current?.focus === 'function') { | ||
| ref.current.focus(); | ||
| } | ||
| }, [ref]); | ||
|
|
||
| const { values, handlers } = useForm({ departmentName: '', username: '', comment: '' }); | ||
| const { departmentName, username, comment } = values as { departmentName: string; username: string; comment: string }; | ||
| const [userId, setUserId] = useState(''); | ||
|
|
||
| const { handleDepartmentName, handleUsername, handleComment } = handlers; | ||
| const userInfo = useEndpointAction('GET', `users.info?username=${ username }`); | ||
|
|
||
|
|
||
| const handleSend = useCallback(() => { | ||
| onForward(departmentName, userId, comment); | ||
| }, [onForward, departmentName, userId, comment]); | ||
|
|
||
|
|
||
| const onChangeDepartment = useMutableCallback((departmentId: string): void => { | ||
| handleDepartmentName(departmentId); | ||
| handleUsername(''); | ||
| setUserId(''); | ||
| }); | ||
|
|
||
| const onChangeUsername = useMutableCallback((username: string): void => { | ||
| handleUsername(username); | ||
| handleDepartmentName(''); | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| if (!username) { return; } | ||
| const fetchData = async (): Promise<void> => { | ||
| const { user } = await userInfo(); | ||
| 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 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import ModalSeparator from './ModalSeparator'; | ||
|
|
||
| export default ModalSeparator; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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}/> | ||
|
rafaelblink marked this conversation as resolved.
|
||
| <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; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import React, { FC, useCallback, useEffect, useRef, useState, useMemo } from 'react'; | ||
| import { Field, Button, TextInput, Icon, ButtonGroup, Modal } from '@rocket.chat/fuselage'; | ||
|
|
||
| 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 ref = useRef<HTMLInputElement>(); | ||
|
|
||
| useEffect(() => { | ||
| if (typeof ref?.current?.focus === 'function') { | ||
| ref.current.focus(); | ||
| } | ||
| }, [ref]); | ||
|
|
||
| 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'> | ||
| <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={ref} disabled={!!transcriptRequest} error={subjectError} flexGrow={1} value={subject} onChange={handleSubject} /> | ||
| </Field.Row> | ||
| <Field.Error> | ||
| {subjectError} | ||
| </Field.Error> | ||
| </Field> | ||
| {!!transcriptRequest && <p>{t('Livechat_transcript_already_requested_warning')}</p>} | ||
| </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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.