Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1b01893
show custom fields at chat info page and fix some fields at chat room…
rafaelblink Mar 25, 2021
b840a06
Enterprise property field at Chat edit form.
rafaelblink Mar 26, 2021
eccfc21
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
rafaelblink Mar 26, 2021
ee8cb7d
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 28, 2021
25b403c
show OS and browser info
rafaelblink Mar 29, 2021
3da53a1
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 29, 2021
eb3e2b1
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 30, 2021
fedeb11
Save button available with changes on fields at Edit Room Form.
rafaelblink Mar 30, 2021
9afa0dd
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 30, 2021
ecfbd87
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 30, 2021
8cadb58
Contact page button save only available with custom fields changes.
rafaelblink Mar 30, 2021
803b6d0
Chat room form name isn't required
rafaelblink Mar 31, 2021
ed2e299
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 31, 2021
f3e4e77
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
renatobecker Mar 31, 2021
a2e970c
check if a EE component.
rafaelblink Mar 31, 2021
b39224e
Merge remote-tracking branch 'origin/omnichannel/inputs-edit-room-reg…
rafaelblink Mar 31, 2021
8fbc3a9
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
rafaelblink Mar 31, 2021
7da4f6a
livechatData removed after merge
rafaelblink Mar 31, 2021
7882cdc
Merge branch 'develop' into omnichannel/inputs-edit-room-regression
rafaelblink Mar 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/livechat/server/methods/saveInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Meteor.methods({
topic: Match.Optional(String),
tags: Match.Optional([String]),
livechatData: Match.Optional(Object),
priorityId: Match.Optional(String),
}));

const room = LivechatRooms.findOneById(roomData._id);
Expand Down
2 changes: 1 addition & 1 deletion client/omnichannel/directory/OmnichannelDirectoryPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ContactNewEdit, ContactEditWithData } from './contacts/contextualBar/Co
import { ContactInfo } from './contacts/contextualBar/ContactInfo';
import ChatTab from './chats/ChatTab';
import { ChatInfo } from './chats/contextualBar/ChatInfo';
import { RoomEditWithData } from './chats/contextualBar/ChatRoomEdit';
import { RoomEditWithData } from './chats/contextualBar/ChatRoomForm';


const OmnichannelDirectoryPage = () => {
Expand Down
92 changes: 88 additions & 4 deletions client/omnichannel/directory/chats/contextualBar/ChatInfo.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import moment from 'moment';
import { Box, Margins, Tag, Avatar, Button, Icon, ButtonGroup } from '@rocket.chat/fuselage';
import { css } from '@rocket.chat/css-in-js';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import UAParser from 'ua-parser-js';

import VerticalBar from '../../../../components/VerticalBar';
import UserCard from '../../../../components/UserCard';
Expand All @@ -16,6 +17,7 @@ import UserAvatar from '../../../../components/avatar/UserAvatar';
import { UserStatus } from '../../../../components/UserStatus';
import { roomTypes } from '../../../../../app/utils/client';
import { useRoute } from '../../../../contexts/RouterContext';
import { hasPermission } from '../../../../../app/authorization';


const wordBreak = css`
Expand Down Expand Up @@ -90,16 +92,92 @@ const AgentField = ({ agent }) => {
</>;
};

const CustomField = ({ id, value }) => {
const t = useTranslation();
const { value: data, phase: state, error } = useEndpointData(`livechat/custom-fields/${ id }`);
if (state === AsyncStatePhase.LOADING) {
return <FormSkeleton />;
}
if (error || !data || !data.customField) {
return <Box mbs='x16'>{t('Custom_Field_Not_Found')}</Box>;
}
const { label } = data.customField;
return label && <>
<Label>{label}</Label>
<Info>{value}</Info>
</>;
};

const PriorityField = ({ id }) => {
const t = useTranslation();
const { value: data, phase: state, error } = useEndpointData(`livechat/priorities.getOne?priorityId=${ id }`);
if (state === AsyncStatePhase.LOADING) {
return <FormSkeleton />;
}
if (error || !data) {
return <Box mbs='x16'>{t('Custom_Field_Not_Found')}</Box>;
}
const { name } = data;
return <>
<Label>{t('Priority')}</Label>
<Info>{name}</Info>
</>;
};

const VisitorClientInfo = ({ uid }) => {
const t = useTranslation();
const { value: userData, phase: state, error } = useEndpointData(`livechat/visitors.info?visitorId=${ uid }`);
if (state === AsyncStatePhase.LOADING) {
return <FormSkeleton />;
}

if (error || !userData || !userData.userAgent) {
return null;
}

const clientData = {};
const ua = new UAParser();
ua.setUA(userData.userAgent);
clientData.os = `${ ua.getOS().name } ${ ua.getOS().version }`;
clientData.browser = `${ ua.getBrowser().name } ${ ua.getBrowser().version }`;

return <>
{clientData.os && <>
<Label>{t('OS')}</Label>
<Info>{clientData.os}</Info>
</>}
{clientData.browser && <>
<Label>{t('Browser')}</Label>
<Info>{clientData.browser}</Info>
</>}
</>;
};

export function ChatInfo({ id, route }) {
const t = useTranslation();

const formatDateAndTime = useFormatDateAndTime();

const { value: allCustomFields, phase: stateCustomFields } = useEndpointData('livechat/custom-fields');
const [customFields, setCustomFields] = useState([]);
const formatDuration = useFormatDuration();

const { value: data, phase: state, error } = useEndpointData(`rooms.info?roomId=${ id }`);
const { room: { ts, tags, closedAt, departmentId, v, servedBy, metrics, topic, waitingResponse, responseBy } } = data || { room: { v: { } } };
const { room: { ts, tags, closedAt, departmentId, v, servedBy, metrics, topic, waitingResponse, responseBy, priorityId, livechatData } } = data || { room: { v: { } } };
const routePath = useRoute(route || 'omnichannel-directory');
const canViewCustomFields = () => hasPermission('view-livechat-room-customfields');
const visitorId = v?._id;

useEffect(() => {
if (allCustomFields) {
const { customFields: customFieldsAPI } = allCustomFields;
setCustomFields(customFieldsAPI);
}
}, [allCustomFields, stateCustomFields]);

const checkIsVisibleAndScopeRoom = (key) => {
const field = customFields.find(({ _id }) => _id === key);
if (field && field.visibility === 'visible' && field.scope === 'room') { return true; }
return false;
};

const onEditClick = useMutableCallback(() => routePath.push(
route ? {
Expand All @@ -124,6 +202,7 @@ export function ChatInfo({ id, route }) {
<VerticalBar.ScrollableContent p='x24'>
<Margins block='x4'>
<ContactField contact={v} room={data.room} />
{visitorId && <VisitorClientInfo uid={visitorId}/>}
{servedBy && <AgentField agent={servedBy} />}
{ departmentId && <DepartmentField departmentId={departmentId} /> }
{tags && tags.length > 0 && <>
Expand Down Expand Up @@ -168,6 +247,11 @@ export function ChatInfo({ id, route }) {
<Label>{t('Inactivity_Time')}</Label>
<Info>{moment(responseBy.lastMessageTs).fromNow(true)}</Info>
</>}
{ canViewCustomFields()
&& livechatData
&& Object.keys(livechatData).map((key) => checkIsVisibleAndScopeRoom(key) && livechatData[key] && <CustomField key={key} id={key} value={livechatData[key]} />)
}
{priorityId && <PriorityField id={priorityId} />}
</Margins>
</VerticalBar.ScrollableContent>
<VerticalBar.Footer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import { useSubscription } from 'use-subscription';
import { useTranslation } from '../../../../contexts/TranslationContext';
import VerticalBar from '../../../../components/VerticalBar';
import { useForm } from '../../../../hooks/useForm';
import { useComponentDidUpdate } from '../../../../hooks/useComponentDidUpdate';
// import { useEndpointAction } from '../../../../hooks/useEndpointAction';
import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import { FormSkeleton } from '../../Skeleton';
import { AsyncStatePhase } from '../../../../hooks/useAsyncState';
import { isEmail } from '../../../../../app/utils';
import { hasAtLeastOnePermission } from '../../../../../app/authorization';
import CustomFieldsForm from '../../../../components/CustomFieldsForm';
import { useMethod } from '../../../../contexts/ServerContext';
Expand All @@ -30,6 +27,7 @@ const initialValuesRoom = {
topic: '',
tags: '',
livechatData: '',
priorityId: '',
};


Expand All @@ -38,13 +36,10 @@ const getInitialValuesUser = (visitor) => {
return initialValuesUser;
}

const { name, fname, phone, visitorEmails, livechatData } = visitor;
const { name, fname } = visitor;

return {
name: (name || fname) ?? '',
email: visitorEmails ? visitorEmails[0].address : '',
phone: phone ? phone[0].phoneNumber : '',
livechatData: livechatData ?? '',
};
};

Expand All @@ -53,11 +48,13 @@ const getInitialValuesRoom = (room) => {
return initialValuesRoom;
}

const { topic, tags } = room;
const { topic, tags, livechatData, priorityId } = room;

return {
topic: topic ?? '',
tags: tags ?? [],
livechatData: livechatData ?? '',
priorityId: priorityId ?? '',
};
};

Expand Down Expand Up @@ -104,58 +101,56 @@ function VisitorData({ room, reload, close }) {
export function RoomEdit({ room, visitor, reload, close }) {
const t = useTranslation();

const { values, handlers } = useForm(getInitialValuesUser(visitor));
const { values: valuesRoom, handlers: handlersRoom } = useForm(getInitialValuesRoom(room));
const { values, handlers, hasUnsavedChanges: hasUnsavedChangesContact } = useForm(getInitialValuesUser(visitor));
const { values: valuesRoom, handlers: handlersRoom, hasUnsavedChanges: hasUnsavedChangesRoom } = useForm(getInitialValuesRoom(room));
const canViewCustomFields = () => hasAtLeastOnePermission(['view-livechat-room-customfields', 'edit-livechat-room-customfields']);

const {
handleName,
handleEmail,
handlePhone,
} = handlers;
const {
name,
email,
phone,
} = values;

const {
handleTopic,
handleTags,
handlePriorityId,
} = handlersRoom;
const {
topic,
tags,
priorityId,
} = valuesRoom;

const forms = useSubscription(formsSubscription);

const {
useCurrentChatTags = () => {},
usePrioritiesSelect = () => {},
} = forms;

const Tags = useCurrentChatTags();
const PrioritiesSelect = usePrioritiesSelect();

const { values: valueCustom, handlers: handleValueCustom } = useForm({
livechatData: values.livechatData,

const { values: valueCustom, handlers: handleValueCustom, hasUnsavedChanges: hasUnsavedChangesCustomFields } = useForm({
livechatData: valuesRoom.livechatData,
});

const { handleLivechatData } = handleValueCustom;
const { livechatData } = valueCustom;


const [nameError, setNameError] = useState();
const [emailError, setEmailError] = useState();
const [phoneError, setPhoneError] = useState();
const [customFieldsError, setCustomFieldsError] = useState([]);

const { value: allCustomFields, phase: state } = useEndpointData('livechat/custom-fields');
const { value: allCustomFields, phase: stateCustomFields } = useEndpointData('livechat/custom-fields');
const { value: prioritiesResult = {}, phase: statePriorities } = useEndpointData('livechat/priorities.list');

const jsonConverterToValidFormat = (customFields) => {
const jsonObj = {};
// eslint-disable-next-line no-return-assign
customFields.map(({ _id, label, visibility, options, scope, defaultValue, required }) =>
(visibility === 'visible' & scope === 'visitor')
(visibility === 'visible' & scope === 'room')
&& (jsonObj[_id] = {
label,
type: options ? 'select' : 'text',
Expand All @@ -171,48 +166,23 @@ export function RoomEdit({ room, visitor, reload, close }) {
? jsonConverterToValidFormat(allCustomFields.customFields) : {}), [allCustomFields]);


// const saveRoom = useEndpointAction('POST', 'omnichannel/contact');

const dispatchToastMessage = useToastMessageDispatch();

useComponentDidUpdate(() => {
setNameError(!name ? t('The_field_is_required', t('Name')) : '');
}, [t, name]);

useComponentDidUpdate(() => {
setEmailError(email && !isEmail(email) ? t('Validate_email_address') : null);
}, [t, email]);

useComponentDidUpdate(() => {
!phone && setPhoneError(null);
}, [phone]);

const saveRoom = useMethod('livechat:saveInfo');

const handleSave = useMutableCallback(async (e) => {
e.preventDefault();
let error = false;
if (!name) {
setNameError(t('The_field_is_required', 'name'));
error = true;
}

if (error) {
return;
}

const userData = {
_id: visitor._id,
name,
email,
phone,
livechatData,
};

const roomData = {
_id: room._id,
topic,
tags: Object.values(tags),
livechatData,
...priorityId && { priorityId },
};

try {
Expand All @@ -225,41 +195,21 @@ export function RoomEdit({ room, visitor, reload, close }) {
}
});

const formIsValid = name && !emailError && !phoneError && customFieldsError.length === 0;
const formIsValid = (hasUnsavedChangesContact || hasUnsavedChangesRoom || hasUnsavedChangesCustomFields) && customFieldsError.length === 0;

if ([state].includes(AsyncStatePhase.LOADING)) {
if ([stateCustomFields, statePriorities].includes(AsyncStatePhase.LOADING)) {
return <FormSkeleton/>;
}

const { priorities } = prioritiesResult;

return <>
<VerticalBar.ScrollableContent is='form'>
<Field>
<Field.Label>{t('Name')}*</Field.Label>
<Field.Row>
<TextInput error={nameError} flexGrow={1} value={name} onChange={handleName} />
</Field.Row>
<Field.Error>
{nameError}
</Field.Error>
</Field>
<Field>
<Field.Label>{t('Email')}</Field.Label>
<Field.Label>{t('Name')}</Field.Label>
<Field.Row>
<TextInput error={emailError} flexGrow={1} value={email} onChange={handleEmail} />
<TextInput flexGrow={1} value={name} onChange={handleName} />
</Field.Row>
<Field.Error>
{t(emailError)}
</Field.Error>
</Field>
<Field>
<Field.Label>{t('Phone')}</Field.Label>
<Field.Row>
<TextInput error={phoneError} flexGrow={1} value={phone} onChange={handlePhone} />
</Field.Row>
<Field.Error>
{t(phoneError)}
</Field.Error>
</Field>
{ canViewCustomFields() && allCustomFields
&& <CustomFieldsForm jsonCustomFields={jsonCustomField} customFieldsData={livechatData}
Expand All @@ -270,12 +220,14 @@ export function RoomEdit({ room, visitor, reload, close }) {
<TextInput flexGrow={1} value={topic} onChange={handleTopic} />
</Field.Row>
</Field>
<Field>
{Tags && (tags && Object.values(tags).length > 0) && <Field>
<Field.Label mb='x4'>{t('Tags')}</Field.Label>
<Field.Row>
<Tags value={Object.values(tags)} handler={handleTags} />
</Field.Row>
</Field>
</Field>}
{PrioritiesSelect && (priorities && priorities.length > 0)
&& <PrioritiesSelect value={priorityId} label={t('Priority')} options={priorities} handler={handlePriorityId} />}
</VerticalBar.ScrollableContent>
<VerticalBar.Footer>
<ButtonGroup stretch>
Expand Down
Loading