From 8b921560529e43f624cfdb2596dff17d1c127dc4 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 26 Aug 2020 14:25:10 -0300 Subject: [PATCH 001/162] wip --- client/omnichannel/departments/AgentEdit.js | 229 ++++++++++++++++++ .../departments/DepartmentsPage.js | 72 ++++++ .../departments/DepartmentsRoute.js | 118 +++++++++ client/omnichannel/departments/Skeleton.js | 11 + client/omnichannel/routes.js | 5 + packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 6 files changed, 436 insertions(+) create mode 100644 client/omnichannel/departments/AgentEdit.js create mode 100644 client/omnichannel/departments/DepartmentsPage.js create mode 100644 client/omnichannel/departments/DepartmentsRoute.js create mode 100644 client/omnichannel/departments/Skeleton.js diff --git a/client/omnichannel/departments/AgentEdit.js b/client/omnichannel/departments/AgentEdit.js new file mode 100644 index 0000000000000..f7d0627785218 --- /dev/null +++ b/client/omnichannel/departments/AgentEdit.js @@ -0,0 +1,229 @@ +import React, { useMemo, useRef, useState } from 'react'; +import { Field, TextInput, NumberInput, SelectFiltered, Box, MultiSelect, Icon, Select, ToggleSwitch, TextAreaInput } from '@rocket.chat/fuselage'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useSubscription } from 'use-subscription'; + +import { useMethod } from '../../contexts/ServerContext'; +import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; +import { useTranslation } from '../../contexts/TranslationContext'; +import VerticalBar from '../../components/basic/VerticalBar'; +import { UserInfo } from '../../components/basic/UserInfo'; +import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental'; +import { FormSkeleton } from './Skeleton'; +import { useForm } from '../../hooks/useForm'; +import { getUserEmailAddress } from '../../helpers/getUserEmailAddress'; +import { useRoute } from '../../contexts/RouterContext'; +import { formsSubscription } from '../additionalForms'; +import Page from '../../components/basic/Page'; + +export default function EditDepartmentWithData({ id, reload }) { + const t = useTranslation(); + const { data, state, error } = useEndpointDataExperimental(`livechat/department/${ id }`) || {}; + // const { data: userDepartments, state: userDepartmentsState, error: userDepartmentsError } = useEndpointDataExperimental(`livechat/agents/${ id }/departments`); + // const { data: availableDepartments, state: availableDepartmentsState, error: availableDepartmentsError } = useEndpointDataExperimental('livechat/department'); + + if ([state].includes(ENDPOINT_STATES.LOADING)) { + return ; + } + + if (error) { + return {t('User_not_found')}; + } + + return ; +} + +// abandonedRoomsCloseCustomMessage: "fuk u" +// chatClosingTags: ["asd"] +// departmentsAllowedToForward: "" +// description: "Binus is gud" +// email: "asd@asd.com" +// enabled: true +// maxNumberSimultaneousChat: "3" +// name: "binus" +// numAgents: 2 +// offlineMessageChannelName: "" +// requestTagBeforeClosingChat: false +// showOnOfflineForm: true +// showOnRegistration: true +// visitorInactivityTimeoutInSeconds: "12" +// waitingQueueMessage: "Wait for binus" +// _id: "xmF2DLvaLfgorggK5" +// _updatedAt: "2020-08-24T21:22:38.276Z" + +// rooms.autocomplete.channelAndPrivate + +const useQuery = ({ name }) => useMemo(() => ({ selector: JSON.stringify({ name }) }), [name]); + +export function EditDepartment({ data, userDepartments, availableDepartments, id, reload, title }) { + const t = useTranslation(); + const agentsRoute = useRoute('omnichannel-departments'); + + const { department } = data || { department: {} }; + + const { values, handlers, hasUnsavedChanges } = useForm({ + name: department.name, + email: department.email, + description: department.description, + enabled: department.enabled, + maxNumberSimultaneousChat: department.maxNumberSimultaneousChat, + showOnRegistration: department.showOnRegistration, + showOnOfflineForm: department.showOnOfflineForm, + abandonedRoomsCloseCustomMessage: department.abandonedRoomsCloseCustomMessage, + chatClosingTags: department.chatClosingTags, + numAgents: department.numAgents, + requestTagBeforeClosingChat: department.requestTagBeforeClosingChat, + offlineMessageChannelName: department.offlineMessageChannelName, + visitorInactivityTimeoutInSeconds: department.visitorInactivityTimeoutInSeconds, + waitingQueueMessage: department.waitingQueueMessage, + }); + const { + handleName, + handleEmail, + handleDescription, + handleEnabled, + handleMaxNumberSimultaneousChat, + handleShowOnRegistration, + handleShowOnOfflineForm, + handleAbandonedRoomsCloseCustomMessage, + handleChatClosingTags, + handleNumAgents, + handleRequestTagBeforeClosingChat, + handleOfflineMessageChannelName, + handleVisitorInactivityTimeoutInSeconds, + handleWaitingQueueMessage, + } = handlers; + const { + name, + email, + description, + enabled, + maxNumberSimultaneousChat, + showOnRegistration, + showOnOfflineForm, + abandonedRoomsCloseCustomMessage, + chatClosingTags, + numAgents, + requestTagBeforeClosingChat, + offlineMessageChannelName, + visitorInactivityTimeoutInSeconds, + waitingQueueMessage, + } = values; + + // const defaultValidations = { + // enabled: Boolean, + // name: String, + // description: Match.Optional(String), + // showOnRegistration: Boolean, + // email: String, + // showOnOfflineForm: Boolean, + // requestTagBeforeClosingChat: Match.Optional(Boolean), + // chatClosingTags: Match.Optional([String]), + // }; + + // // The Livechat Form department support addition/custom fields, so those fields need to be added before validating + // Object.keys(departmentData).forEach((field) => { + // if (!defaultValidations.hasOwnProperty(field)) { + // defaultValidations[field] = Match.OneOf(String, Match.Integer, Boolean); + // } + // }); + + // check(departmentData, defaultValidations); + // check(departmentAgents, Match.Maybe({ + // upsert: Match.Maybe(Array), + // remove: Match.Maybe(Array), + // })); + + const query = useQuery({ offlineMessageChannelName }); + + const { data: autoCompleteChannels } = useEndpointDataExperimental('rooms.autocomplete.channelAndPrivate', query) || {}; + + const channelOpts = useMemo(() => (autoCompleteChannels && autoCompleteChannels.items ? autoCompleteChannels.items.map(({ _id, name }) => [_id, name || _id]) : []), [autoCompleteChannels]); + + console.log(channelOpts); + + const saveAgentInfo = useMethod('livechat:saveDepartment'); + + const dispatchToastMessage = useToastMessageDispatch(); + + const handleSave = useMutableCallback(async () => { + try { + await saveAgentInfo(id); + dispatchToastMessage({ type: 'success', message: t('saved') }); + agentsRoute.push({}); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + console.log(error); + } + }); + + return + + + + + {t('Enabled')} + + + + + + {t('Name')} + + + + + + {t('Description')} + + + + + + {t('Show_on_registration_page')} + + + + + + {t('Email')} + + } onChange={handleEmail}/> + + + + {t('Show_on_offline_page')} + + + + + + {t('Livechat_DepartmentOfflineMessageToChannel')} + + + + + + {t('Max_number_of_chats_per_agent')} + + + + + + {t('How_long_to_wait_to_consider_visitor_abandonment_in_seconds')} + + + + + + {t('Waiting_queue_message')} + + + + + + + ; +} + + diff --git a/client/omnichannel/departments/DepartmentsPage.js b/client/omnichannel/departments/DepartmentsPage.js new file mode 100644 index 0000000000000..0d4ad6187a5cd --- /dev/null +++ b/client/omnichannel/departments/DepartmentsPage.js @@ -0,0 +1,72 @@ +import React, { useState, useEffect } from 'react'; +import { TextInput, Button, Box, Icon } from '@rocket.chat/fuselage'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import Page from '../../components/basic/Page'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useEndpointAction } from '../../hooks/useEndpointAction'; +import { GenericTable } from '../../components/GenericTable'; +import { UserAutoComplete } from '../../components/basic/AutoComplete'; + +const FilterByText = ({ setFilter, ...props }) => { + const t = useTranslation(); + const [text, setText] = useState(''); + + const handleChange = useMutableCallback((event) => setText(event.currentTarget.value)); + const onSubmit = useMutableCallback((e) => e.preventDefault()); + + useEffect(() => { + setFilter({ text }); + }, [setFilter, text]); + return + } onChange={handleChange} value={text} /> + ; +}; + + +function AddDepartment({ reload, ...props }) { + const t = useTranslation(); + const [username, setUsername] = useState(); + + const saveAction = useEndpointAction('POST', 'livechat/users/department', { username }); + + const handleSave = useMutableCallback(async () => { + if (!username) { + return; + } + const result = await saveAction(); + if (!result.success) { + return; + } + reload(); + setUsername(); + }); + return + + + ; +} + +function DepartmentsPage({ + data, + reload, + header, + setParams, + params, + title, + renderRow, + children, +}) { + return + + + + + + + + {children} + ; +} + +export default DepartmentsPage; diff --git a/client/omnichannel/departments/DepartmentsRoute.js b/client/omnichannel/departments/DepartmentsRoute.js new file mode 100644 index 0000000000000..c02d83081d3ad --- /dev/null +++ b/client/omnichannel/departments/DepartmentsRoute.js @@ -0,0 +1,118 @@ + +import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import React, { useMemo, useCallback, useState } from 'react'; +import { Box, Table, Icon } from '@rocket.chat/fuselage'; + +import { Th } from '../../components/GenericTable'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental'; +import { useEndpointAction } from '../../hooks/useEndpointAction'; +import { usePermission } from '../../contexts/AuthorizationContext'; +import NotAuthorizedPage from '../../components/NotAuthorizedPage'; +import DepartmentsPage from './DepartmentsPage'; +import EditDepartmentWithData from './AgentEdit'; +// import AgentInfo from './AgentInfo'; +import UserAvatar from '../../components/basic/avatar/UserAvatar'; +import { useRouteParameter, useRoute } from '../../contexts/RouterContext'; +import VerticalBar from '../../components/basic/VerticalBar'; + +export function RemoveAgentButton({ _id, reload }) { + const deleteAction = useEndpointAction('DELETE', `livechat/department/${ _id }`); + + const handleRemoveClick = useMutableCallback(async (e) => { + e.preventDefault(); + const result = await deleteAction(); + if (result.success === true) { + reload(); + } + }); + + return ; +} + +const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); + +const useQuery = ({ text, itemsPerPage, current }, [column, direction]) => useMemo(() => ({ + fields: JSON.stringify({ name: 1, username: 1, emails: 1, avatarETag: 1 }), + text, + sort: JSON.stringify({ [column]: sortDir(direction), usernames: column === 'name' ? sortDir(direction) : undefined }), + ...itemsPerPage && { count: itemsPerPage }, + ...current && { offset: current }, +}), [text, itemsPerPage, current, column, direction]); + +function AgentsRoute() { + const t = useTranslation(); + const canViewAgents = usePermission('manage-livechat-departments'); + + const [params, setParams] = useState({ text: '', current: 0, itemsPerPage: 25 }); + const [sort, setSort] = useState(['name', 'asc']); + + const debouncedParams = useDebouncedValue(params, 500); + const debouncedSort = useDebouncedValue(sort, 500); + const query = useQuery(debouncedParams, debouncedSort); + const departmentsRoute = useRoute('omnichannel-departments'); + const context = useRouteParameter('context'); + const id = useRouteParameter('id'); + + const onHeaderClick = useMutableCallback((id) => { + const [sortBy, sortDirection] = sort; + + if (sortBy === id) { + setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']); + return; + } + setSort([id, 'asc']); + }); + + const onRowClick = useMutableCallback((id) => () => departmentsRoute.push({ + context: 'edit', + id, + })); + + const { data, reload } = useEndpointDataExperimental('livechat/department', query) || {}; + console.log(data); + + + const header = useMemo(() => [ + {t('Name')}, + {t('Description')}, + {t('Num_Agents')}, + {t('Enabled')}, + {t('Show_on_registration_page')}, + {t('Remove')}, + ].filter(Boolean), [sort, onHeaderClick, t]); + + const renderRow = useCallback(({ name, _id, description, numAgents, enabled, showOnRegistration }) => + {name} + {description} + {numAgents || '0'} + {enabled ? t('Yes') : t('No')} + {showOnRegistration ? t('Yes') : t('No')} + + , [onRowClick, t, reload]); + + if (!canViewAgents) { + return ; + } + + if (context === 'edit' || context === 'new') { + return ; + } + + + return + ; +} + +export default AgentsRoute; diff --git a/client/omnichannel/departments/Skeleton.js b/client/omnichannel/departments/Skeleton.js new file mode 100644 index 0000000000000..2fc5e9097c149 --- /dev/null +++ b/client/omnichannel/departments/Skeleton.js @@ -0,0 +1,11 @@ +import React from 'react'; +import { Box, Skeleton } from '@rocket.chat/fuselage'; + +export const FormSkeleton = (props) => + + + + + + +; diff --git a/client/omnichannel/routes.js b/client/omnichannel/routes.js index 9def7135deab4..fa2743167e563 100644 --- a/client/omnichannel/routes.js +++ b/client/omnichannel/routes.js @@ -73,3 +73,8 @@ registerOmnichannelRoute('/current', { name: 'omnichannel-current-chats', lazyRouteComponent: () => import('./currentChats/CurrentChatsRoute'), }); + +registerOmnichannelRoute('/departments/:context?/:id?', { + name: 'omnichannel-departments', + lazyRouteComponent: () => import('./departments/DepartmentsRoute'), +}); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 6cc3ad660783d..94be80098e0a5 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1824,6 +1824,7 @@ "How_knowledgeable_was_the_chat_agent": "How knowledgeable was the chat agent?", "How_long_to_wait_after_agent_goes_offline": "How Long to Wait After Agent Goes Offline", "How_long_to_wait_to_consider_visitor_abandonment": "How Long to Wait to Consider Visitor Abandonment?", + "How_long_to_wait_to_consider_visitor_abandonment_in_seconds": "How Long to Wait to Consider Visitor Abandonment?", "How_responsive_was_the_chat_agent": "How responsive was the chat agent?", "How_satisfied_were_you_with_this_chat": "How satisfied were you with this chat?", "How_to_handle_open_sessions_when_agent_goes_offline": "How to Handle Open Sessions When Agent Goes Offline", From 6e34e484a452aafc6efa46726db1d5be9a74372b Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 31 Aug 2020 17:12:25 -0300 Subject: [PATCH 002/162] wip --- client/omnichannel/departments/AgentEdit.js | 37 ++++++++++++--------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/client/omnichannel/departments/AgentEdit.js b/client/omnichannel/departments/AgentEdit.js index f7d0627785218..af38b13a155d6 100644 --- a/client/omnichannel/departments/AgentEdit.js +++ b/client/omnichannel/departments/AgentEdit.js @@ -1,5 +1,5 @@ import React, { useMemo, useRef, useState } from 'react'; -import { Field, TextInput, NumberInput, SelectFiltered, Box, MultiSelect, Icon, Select, ToggleSwitch, TextAreaInput } from '@rocket.chat/fuselage'; +import { Field, TextInput, NumberInput, SelectFiltered, Box, MultiSelect, Icon, Select, ToggleSwitch, TextAreaInput, ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSubscription } from 'use-subscription'; @@ -62,20 +62,19 @@ export function EditDepartment({ data, userDepartments, availableDepartments, id const { department } = data || { department: {} }; const { values, handlers, hasUnsavedChanges } = useForm({ - name: department.name, - email: department.email, - description: department.description, - enabled: department.enabled, - maxNumberSimultaneousChat: department.maxNumberSimultaneousChat, - showOnRegistration: department.showOnRegistration, - showOnOfflineForm: department.showOnOfflineForm, - abandonedRoomsCloseCustomMessage: department.abandonedRoomsCloseCustomMessage, - chatClosingTags: department.chatClosingTags, - numAgents: department.numAgents, - requestTagBeforeClosingChat: department.requestTagBeforeClosingChat, - offlineMessageChannelName: department.offlineMessageChannelName, - visitorInactivityTimeoutInSeconds: department.visitorInactivityTimeoutInSeconds, - waitingQueueMessage: department.waitingQueueMessage, + name: (department && department.name) || '', + email: (department && department.email) || '', + description: (department && department.description) || '', + enabled: (department && department.enabled) || '', + maxNumberSimultaneousChat: (department && department.maxNumberSimultaneousChat) || undefined, + showOnRegistration: (department && department.showOnRegistration) || true, + showOnOfflineForm: (department && department.showOnOfflineForm) || true, + abandonedRoomsCloseCustomMessage: (department && department.abandonedRoomsCloseCustomMessage) || '', + chatClosingTags: (department && department.chatClosingTags) || '', + requestTagBeforeClosingChat: (department && department.requestTagBeforeClosingChat) || false, + offlineMessageChannelName: (department && department.offlineMessageChannelName) || '', + visitorInactivityTimeoutInSeconds: (department && department.visitorInactivityTimeoutInSeconds) || undefined, + waitingQueueMessage: (department && department.waitingQueueMessage) || '', }); const { handleName, @@ -159,7 +158,13 @@ export function EditDepartment({ data, userDepartments, availableDepartments, id return - + + + + + {t('Enabled')} From c43ed83a494d6eaed51fccd832004fa7231037be Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 8 Sep 2020 12:20:02 -0300 Subject: [PATCH 003/162] more work in progress --- client/components/basic/AutoCompleteAgent.js | 5 +- .../{AgentEdit.js => DepartmentEdit.js} | 21 ++- .../departments/DepartmentsAgentsTable.js | 169 ++++++++++++++++++ .../departments/DepartmentsRoute.js | 2 +- 4 files changed, 190 insertions(+), 7 deletions(-) rename client/omnichannel/departments/{AgentEdit.js => DepartmentEdit.js} (91%) create mode 100644 client/omnichannel/departments/DepartmentsAgentsTable.js diff --git a/client/components/basic/AutoCompleteAgent.js b/client/components/basic/AutoCompleteAgent.js index 5e42fdd0df5fb..c070dda62b485 100644 --- a/client/components/basic/AutoCompleteAgent.js +++ b/client/components/basic/AutoCompleteAgent.js @@ -9,7 +9,8 @@ export const AutoCompleteAgent = React.memo((props) => { const [filter, setFilter] = useState(''); const { data } = useEndpointDataExperimental('livechat/users/agent', useMemo(() => ({ text: filter }), [filter])); - const options = useMemo(() => (data && [{ value: 'all', label: t('All') }, ...data.users.map((user) => ({ value: user._id, label: user.name }))]) || [{ value: 'all', label: t('All') }], [data, t]); + const options = useMemo(() => (data && [...data.users.map((user) => ({ value: user._id, label: user.name }))]) || [], [data]); + const optionsWithAll = useMemo(() => (data && [{ value: 'all', label: t('All') }, ...data.users.map((user) => ({ value: user._id, label: user.name }))]) || [{ value: 'all', label: t('All') }], [data, t]); return { setFilter={setFilter} renderSelected={({ label }) => <>{label}} renderItem={({ value, ...props }) => + + {t('Department')}: + + + + + {t('Agents')}: + + ; } - - diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js new file mode 100644 index 0000000000000..06b95648403e6 --- /dev/null +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -0,0 +1,169 @@ + +import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import React, { useMemo, useCallback, useState, useEffect } from 'react'; +import { Box, Table, Icon, Button, NumberInput } from '@rocket.chat/fuselage'; + +import { Th, GenericTable } from '../../components/GenericTable'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental'; +import { useEndpointAction } from '../../hooks/useEndpointAction'; +import UserAvatar from '../../components/basic/avatar/UserAvatar'; +import { useRouteParameter, useRoute } from '../../contexts/RouterContext'; +import VerticalBar from '../../components/basic/VerticalBar'; +import DeleteWarningModal from '../DeleteWarningModal'; +import { useSetModal } from '../../contexts/ModalContext'; +import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; +import { AutoCompleteAgent } from '../../components/basic/AutoCompleteAgent'; + +function AddAgent({ agentList, setAgentList, ...props }) { + const t = useTranslation(); + const [userId, setUserId] = useState(); + const getAgent = useEndpointAction('GET', `livechat/users/agent/${ userId }`); + const dispatchToastMessage = useToastMessageDispatch(); + + const handleAgent = useMutableCallback((e) => setUserId(e)); + console.log(userId); + + const handleSave = useMutableCallback(async () => { + if (!userId) { + return; + } + const { user } = await getAgent(); + + if (agentList.filter((e) => e.agentId === userId).length === 0) { + setAgentList([user, ...agentList]); + setUserId(); + } else { + dispatchToastMessage({ type: 'error', message: t('Already_selected') }); + } + }); + return + + + ; +} + +function AgentsPage({ + data, + reload, + header, + setParams, + params, + renderRow, + children, + agentList, + setAgentList, +}) { + console.log(data); + return + + + {children} + ; +} + + +export function RemoveAgentButton({ _id, setAgentList, agentList }) { + const setModal = useSetModal(); + const dispatchToastMessage = useToastMessageDispatch(); + const t = useTranslation(); + + const handleDelete = useMutableCallback((e) => { + e.stopPropagation(); + const onDeleteAgent = async () => { + setAgentList([...agentList.filter((e) => e._id !== _id)]); + dispatchToastMessage({ type: 'success', message: t('Agent_removed') }); + setModal(); + }; + + setModal( setModal()}/>); + }); + + return + + ; +} + +export function Count({ agentId, setAgentList, agentList }) { + const t = useTranslation(); + const [agentCount, setAgentCount] = useState(agentList.find((agent) => agent.agentId === agentId).count); + const [updatedList, setUpdatedList] = useState(agentList); + + useMemo(() => setAgentList(updatedList), [setAgentList, updatedList]); + + const changeList = () => { + setUpdatedList(agentList.map((agent) => { + if (agent.agentId === agentId) { + agent.count = agentCount; + } + return agent; + })); + debugger + console.log('NEWLIST', updatedList); + // setAgentList(newList); + // debugger + // console.log(agentList); + }; + console.log(agentCount); + + const handleCount = useMutableCallback(async (e) => { + setAgentCount(Number(e.currentTarget.value)); + changeList(); + }); + + return + + ; +} + +const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); + +function DepartmentsAgentsTable({ _id, agents }) { + const t = useTranslation(); + + console.log(agents); + + const [agentList, setAgentList] = useState(agents || []); + const [data, setData] = useState({}); + + useMemo(() => setData({ users: agentList }), [agentList]); + + const mediaQuery = useMediaQuery('(min-width: 1024px)'); + + const header = useMemo(() => [ + {t('Name')}, + {t('Username')}, + {t('Email')}, + {t('Remove')}, + ].filter(Boolean), [t]); + + const renderRow = useCallback(({ emails, agentId, username, name, avatarETag }) => + + + + + + {name || username} + {!mediaQuery && name && {`@${ username }`} } + + + + + + + + , [ agentList, mediaQuery]); + + return + ; +} + +export default DepartmentsAgentsTable; diff --git a/client/omnichannel/departments/DepartmentsRoute.js b/client/omnichannel/departments/DepartmentsRoute.js index c02d83081d3ad..6d883b0489f34 100644 --- a/client/omnichannel/departments/DepartmentsRoute.js +++ b/client/omnichannel/departments/DepartmentsRoute.js @@ -10,7 +10,7 @@ import { useEndpointAction } from '../../hooks/useEndpointAction'; import { usePermission } from '../../contexts/AuthorizationContext'; import NotAuthorizedPage from '../../components/NotAuthorizedPage'; import DepartmentsPage from './DepartmentsPage'; -import EditDepartmentWithData from './AgentEdit'; +import EditDepartmentWithData from './DepartmentEdit'; // import AgentInfo from './AgentInfo'; import UserAvatar from '../../components/basic/avatar/UserAvatar'; import { useRouteParameter, useRoute } from '../../contexts/RouterContext'; From 8b826a1f141356bb15019e67f0e062432413c63d Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 8 Sep 2020 12:23:46 -0300 Subject: [PATCH 004/162] lint --- .../omnichannel/departments/DepartmentEdit.js | 27 ++++++++----------- .../departments/DepartmentsAgentsTable.js | 19 +++++-------- .../departments/DepartmentsRoute.js | 6 ++--- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/client/omnichannel/departments/DepartmentEdit.js b/client/omnichannel/departments/DepartmentEdit.js index 9ed8e85d4f810..872920291d479 100644 --- a/client/omnichannel/departments/DepartmentEdit.js +++ b/client/omnichannel/departments/DepartmentEdit.js @@ -1,20 +1,15 @@ /* eslint-disable complexity */ -import React, { useMemo, useRef, useState } from 'react'; -import { Field, TextInput, NumberInput, SelectFiltered, Box, MultiSelect, Icon, Divider, ToggleSwitch, TextAreaInput, ButtonGroup, Button } from '@rocket.chat/fuselage'; +import React, { useMemo } from 'react'; +import { Field, TextInput, NumberInput, SelectFiltered, Box, Icon, Divider, ToggleSwitch, TextAreaInput, ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSubscription } from 'use-subscription'; import { useMethod } from '../../contexts/ServerContext'; import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; import { useTranslation } from '../../contexts/TranslationContext'; -import VerticalBar from '../../components/basic/VerticalBar'; -import { UserInfo } from '../../components/basic/UserInfo'; import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental'; import { FormSkeleton } from './Skeleton'; import { useForm } from '../../hooks/useForm'; -import { getUserEmailAddress } from '../../helpers/getUserEmailAddress'; import { useRoute } from '../../contexts/RouterContext'; -import { formsSubscription } from '../additionalForms'; import Page from '../../components/basic/Page'; import { AutoCompleteDepartment } from '../../components/basic/AutoCompleteDepartment'; import DepartmentsAgentsTable from './DepartmentsAgentsTable'; @@ -58,7 +53,7 @@ export default function EditDepartmentWithData({ id, reload }) { const useQuery = ({ name }) => useMemo(() => ({ selector: JSON.stringify({ name }) }), [name]); -export function EditDepartment({ data, userDepartments, availableDepartments, id, reload, title }) { +export function EditDepartment({ data, id, title }) { const t = useTranslation(); const agentsRoute = useRoute('omnichannel-departments'); @@ -88,10 +83,10 @@ export function EditDepartment({ data, userDepartments, availableDepartments, id handleMaxNumberSimultaneousChat, handleShowOnRegistration, handleShowOnOfflineForm, - handleAbandonedRoomsCloseCustomMessage, - handleChatClosingTags, - handleNumAgents, - handleRequestTagBeforeClosingChat, + // handleAbandonedRoomsCloseCustomMessage, + // handleChatClosingTags, + // handleNumAgents, + // handleRequestTagBeforeClosingChat, handleOfflineMessageChannelName, handleVisitorInactivityTimeoutInSeconds, handleWaitingQueueMessage, @@ -105,10 +100,10 @@ export function EditDepartment({ data, userDepartments, availableDepartments, id maxNumberSimultaneousChat, showOnRegistration, showOnOfflineForm, - abandonedRoomsCloseCustomMessage, - chatClosingTags, - numAgents, - requestTagBeforeClosingChat, + // abandonedRoomsCloseCustomMessage, + // chatClosingTags, + // numAgents, + // requestTagBeforeClosingChat, offlineMessageChannelName, visitorInactivityTimeoutInSeconds, waitingQueueMessage, diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js index 06b95648403e6..9ab45d1e8fc15 100644 --- a/client/omnichannel/departments/DepartmentsAgentsTable.js +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -1,15 +1,12 @@ -import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import React, { useMemo, useCallback, useState, useEffect } from 'react'; +import { useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import React, { useMemo, useCallback, useState } from 'react'; import { Box, Table, Icon, Button, NumberInput } from '@rocket.chat/fuselage'; import { Th, GenericTable } from '../../components/GenericTable'; import { useTranslation } from '../../contexts/TranslationContext'; -import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental'; import { useEndpointAction } from '../../hooks/useEndpointAction'; import UserAvatar from '../../components/basic/avatar/UserAvatar'; -import { useRouteParameter, useRoute } from '../../contexts/RouterContext'; -import VerticalBar from '../../components/basic/VerticalBar'; import DeleteWarningModal from '../DeleteWarningModal'; import { useSetModal } from '../../contexts/ModalContext'; import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; @@ -88,7 +85,7 @@ export function RemoveAgentButton({ _id, setAgentList, agentList }) { export function Count({ agentId, setAgentList, agentList }) { const t = useTranslation(); - const [agentCount, setAgentCount] = useState(agentList.find((agent) => agent.agentId === agentId).count); + const [agentCount, setAgentCount] = useState(agentList.find((agent) => agent.agentId === agentId).counta); const [updatedList, setUpdatedList] = useState(agentList); useMemo(() => setAgentList(updatedList), [setAgentList, updatedList]); @@ -100,8 +97,6 @@ export function Count({ agentId, setAgentList, agentList }) { } return agent; })); - debugger - console.log('NEWLIST', updatedList); // setAgentList(newList); // debugger // console.log(agentList); @@ -118,9 +113,7 @@ export function Count({ agentId, setAgentList, agentList }) { ; } -const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); - -function DepartmentsAgentsTable({ _id, agents }) { +function DepartmentsAgentsTable({ agents }) { const t = useTranslation(); console.log(agents); @@ -139,7 +132,7 @@ function DepartmentsAgentsTable({ _id, agents }) { {t('Remove')}, ].filter(Boolean), [t]); - const renderRow = useCallback(({ emails, agentId, username, name, avatarETag }) => + const renderRow = useCallback(({ agentId, username, name, avatarETag }) => @@ -154,7 +147,7 @@ function DepartmentsAgentsTable({ _id, agents }) { - , [ agentList, mediaQuery]); + , [agentList, mediaQuery]); return Date: Wed, 9 Sep 2020 14:42:32 -0300 Subject: [PATCH 005/162] Fix IE11 support livechat widget --- package-lock.json | 28 +++++++++++++++++++++------- package.json | 2 +- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index de3fb6b6b2641..3b2455825f34d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5128,14 +5128,15 @@ "integrity": "sha512-V/lq5xdIxx016pKB8kSQuQ+IKLC0ULdkZ72M/DEMU1DqXahWDEJeRiBppOO8YN5/mw5INaf8asWOqxJfIFld0w==" }, "@rocket.chat/livechat": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.7.2.tgz", - "integrity": "sha512-CikbwDG2ohvnQbSlQIYBe9YUmIyrBgGYtPUXFAI5J9zp6UFg2S0icO2wAN7O3IJJl/RKOVKyWO+3/L83DypVVw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.7.3.tgz", + "integrity": "sha512-Az3zyqILC61LfVa0amkO1vb4rXJniFIOuc/iw1TFLqaG1wWow8aBohqqIPoQs2DptJThp0R8w8APlrJEQrGZJw==", "dev": true, "requires": { "@kossnocorp/desvg": "^0.2.0", "@rocket.chat/sdk": "^1.0.0-alpha.41", "@rocket.chat/ui-kit": "^0.14.1", + "css-vars-ponyfill": "^2.3.2", "date-fns": "^2.15.0", "desvg": "^1.0.2", "emoji-mart": "^3.0.0", @@ -5147,7 +5148,8 @@ "preact": "^10.4.6", "preact-i18nline": "^2.0.0", "preact-router": "^3.2.1", - "query-string": "^6.13.1" + "query-string": "^6.13.1", + "whatwg-fetch": "^3.4.0" }, "dependencies": { "query-string": { @@ -5160,6 +5162,12 @@ "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } + }, + "whatwg-fetch": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz", + "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==", + "dev": true } } }, @@ -14937,6 +14945,12 @@ } } }, + "css-vars-ponyfill": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/css-vars-ponyfill/-/css-vars-ponyfill-2.3.2.tgz", + "integrity": "sha512-XkZfj0ROhem0Zdv44+LF15COsYmxnqL7Wd/gvwuWAauYoALbt2x94b6dIKF9fB6SIyOMYEQngA82t9RnC6b/aw==", + "dev": true + }, "css-what": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz", @@ -15340,9 +15354,9 @@ "integrity": "sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA==" }, "date-fns": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.0.tgz", - "integrity": "sha512-DWTRyfOA85sZ4IiXPHhiRIOs3fW5U6Msrp+gElXARa6EpoQTXPyHQmh7hr+ssw2nx9FtOQWnAMJKgL5vaJqILw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", + "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==", "dev": true }, "date-now": { diff --git a/package.json b/package.json index 5818410a827a9..0966101fb2fba 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@babel/preset-react": "^7.10.4", "@octokit/rest": "^16.43.2", "@rocket.chat/eslint-config": "^0.3.0", - "@rocket.chat/livechat": "^1.7.2", + "@rocket.chat/livechat": "^1.7.3", "@settlin/spacebars-loader": "^1.0.8", "@storybook/addon-actions": "^5.3.19", "@storybook/addon-knobs": "^5.3.19", From 6bed223cacb650fd127aa31bd26e665af2f631f7 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 15 Sep 2020 18:05:58 -0300 Subject: [PATCH 006/162] More wip --- .../omnichannel/departments/DepartmentEdit.js | 173 +++++++++++++----- .../departments/DepartmentsAgentsTable.js | 64 +++++-- .../departments/DepartmentsRoute.js | 21 +-- .../DepartmentBusinessHours.js | 22 +++ .../additionalForms/DepartmentForwarding.js | 23 +++ .../additionalForms/EeNumberInput.js | 18 ++ .../additionalForms/EeTextAreaInput.js | 18 ++ .../additionalForms/EeTextInput.js | 18 ++ .../omnichannel/additionalForms/register.js | 5 + 9 files changed, 282 insertions(+), 80 deletions(-) create mode 100644 ee/client/omnichannel/additionalForms/DepartmentBusinessHours.js create mode 100644 ee/client/omnichannel/additionalForms/DepartmentForwarding.js create mode 100644 ee/client/omnichannel/additionalForms/EeNumberInput.js create mode 100644 ee/client/omnichannel/additionalForms/EeTextAreaInput.js create mode 100644 ee/client/omnichannel/additionalForms/EeTextInput.js diff --git a/client/omnichannel/departments/DepartmentEdit.js b/client/omnichannel/departments/DepartmentEdit.js index 872920291d479..e4858cb47a234 100644 --- a/client/omnichannel/departments/DepartmentEdit.js +++ b/client/omnichannel/departments/DepartmentEdit.js @@ -1,7 +1,8 @@ /* eslint-disable complexity */ -import React, { useMemo } from 'react'; -import { Field, TextInput, NumberInput, SelectFiltered, Box, Icon, Divider, ToggleSwitch, TextAreaInput, ButtonGroup, Button } from '@rocket.chat/fuselage'; +import React, { useMemo, useState } from 'react'; +import { Field, TextInput, Chip, SelectFiltered, Box, Icon, Divider, ToggleSwitch, TextAreaInput, ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useSubscription } from 'use-subscription'; import { useMethod } from '../../contexts/ServerContext'; import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; @@ -13,6 +14,8 @@ import { useRoute } from '../../contexts/RouterContext'; import Page from '../../components/basic/Page'; import { AutoCompleteDepartment } from '../../components/basic/AutoCompleteDepartment'; import DepartmentsAgentsTable from './DepartmentsAgentsTable'; +import { formsSubscription } from '../additionalForms'; + export default function EditDepartmentWithData({ id, reload }) { const t = useTranslation(); @@ -56,9 +59,32 @@ const useQuery = ({ name }) => useMemo(() => ({ selector: JSON.stringify({ name export function EditDepartment({ data, id, title }) { const t = useTranslation(); const agentsRoute = useRoute('omnichannel-departments'); + const eeForms = useSubscription(formsSubscription); + + const { + useEeNumberInput = () => {}, + useEeTextInput = () => {}, + useEeTextAreaInput = () => {}, + useDepartmentForwarding = () => {}, + useDepartmentBusinessHours = () => {}, + } = eeForms; + + const MaxChats = useEeNumberInput(); + const VisitorInactivity = useEeNumberInput(); + const WaitingQueueMessageInput = useEeTextAreaInput(); + const AbandonedMessageInput = useEeTextInput(); + const DepartmentForwarding = useDepartmentForwarding(); + const DepartmentBusinessHours = useDepartmentBusinessHours(); + const [agentList, setAgentList] = useState([]); + const { department } = data || { department: {} }; + const [tags, setTags] = useState(department && department.chatClosingTags); + const [tagsText, setTagsText] = useState(); + + console.log(data); + const { values, handlers, hasUnsavedChanges } = useForm({ name: (department && department.name) || '', email: (department && department.email) || '', @@ -68,12 +94,11 @@ export function EditDepartment({ data, id, title }) { showOnRegistration: (department && department.showOnRegistration) || true, showOnOfflineForm: (department && department.showOnOfflineForm) || true, abandonedRoomsCloseCustomMessage: (department && department.abandonedRoomsCloseCustomMessage) || '', - chatClosingTags: (department && department.chatClosingTags) || '', requestTagBeforeClosingChat: (department && department.requestTagBeforeClosingChat) || false, offlineMessageChannelName: (department && department.offlineMessageChannelName) || '', visitorInactivityTimeoutInSeconds: (department && department.visitorInactivityTimeoutInSeconds) || undefined, waitingQueueMessage: (department && department.waitingQueueMessage) || '', - forwardDepartment: (department && department.forwardDepartment) || '', + departmentsAllowedToForward: (department && department.departmentsAllowedToForward) || [], }); const { handleName, @@ -83,14 +108,12 @@ export function EditDepartment({ data, id, title }) { handleMaxNumberSimultaneousChat, handleShowOnRegistration, handleShowOnOfflineForm, - // handleAbandonedRoomsCloseCustomMessage, - // handleChatClosingTags, - // handleNumAgents, - // handleRequestTagBeforeClosingChat, + handleAbandonedRoomsCloseCustomMessage, + handleRequestTagBeforeClosingChat, handleOfflineMessageChannelName, handleVisitorInactivityTimeoutInSeconds, handleWaitingQueueMessage, - handleForwardDepartment, + handleDepartmentsAllowedToForward, } = handlers; const { name, @@ -100,16 +123,28 @@ export function EditDepartment({ data, id, title }) { maxNumberSimultaneousChat, showOnRegistration, showOnOfflineForm, - // abandonedRoomsCloseCustomMessage, - // chatClosingTags, - // numAgents, - // requestTagBeforeClosingChat, + abandonedRoomsCloseCustomMessage, + requestTagBeforeClosingChat, offlineMessageChannelName, visitorInactivityTimeoutInSeconds, waitingQueueMessage, - forwardDepartment, + departmentsAllowedToForward, } = values; + const handleTagChipClick = (tag) => () => { + setTags((tags) => tags.filter((_tag) => _tag !== tag)); + }; + + const handleTagTextSubmit = useMutableCallback(() => { + if (!tags.includes(tagsText)) { + setTags([...tags, tagsText]); + setTagsText(''); + } + }); + + const handleTagTextChange = useMutableCallback((e) => { + setTagsText(e.target.value); + }); // const defaultValidations = { // enabled: Boolean, // name: String, @@ -138,17 +173,54 @@ export function EditDepartment({ data, id, title }) { const { data: autoCompleteChannels } = useEndpointDataExperimental('rooms.autocomplete.channelAndPrivate', query) || {}; - const channelOpts = useMemo(() => (autoCompleteChannels && autoCompleteChannels.items ? autoCompleteChannels.items.map(({ _id, name }) => [_id, name || _id]) : []), [autoCompleteChannels]); + const channelOpts = useMemo(() => (autoCompleteChannels && autoCompleteChannels.items ? autoCompleteChannels.items.map(({ name }) => [name, name]) : []), [autoCompleteChannels]); console.log(channelOpts); - const saveAgentInfo = useMethod('livechat:saveDepartment'); + const saveDepartmentInfo = useMethod('livechat:saveDepartment'); const dispatchToastMessage = useToastMessageDispatch(); + // enabled: true, + // name: 'asdasdasdasdasd', + // description: 'asdsdasdad', + // showOnRegistration: true, + // showOnOfflineForm: true, + // requestTagBeforeClosingChat: true, + // email: 'asdadss@gmail.com', + // chatClosingTags: [ 'pinus' ], + // offlineMessageChannelName: 'general', + // maxNumberSimultaneousChat: '10', + // visitorInactivityTimeoutInSeconds: '20', + // abandonedRoomsCloseCustomMessage: 'fuk u', + // waitingQueueMessage: 'fuk me', + // departmentsAllowedToForward: 'inNc2tPHTbyBo2dmt,8rbimWYR4HLLqii3t' + + const nameError = useMemo(() => (!name || name.length === 0 ? t('The_field_is_required', 'name') : undefined), [name, t]); + const emailError = useMemo(() => (!email || email.length === 0 ? t('The_field_is_required', 'email') : undefined), [email, t]); + const tagError = useMemo(() => ((requestTagBeforeClosingChat && !tags) || (requestTagBeforeClosingChat && tags.length === 0) ? t('The_field_is_required', 'tags') : undefined), [tags, t]); + + const handleSave = useMutableCallback(async () => { + const payload = { + enabled, + name, + description, + showOnRegistration, + showOnOfflineForm, + requestTagBeforeClosingChat, + email, + chatClosingTags: tags, + offlineMessageChannelName, + maxNumberSimultaneousChat, + visitorInactivityTimeoutInSeconds, + abandonedRoomsCloseCustomMessage, + waitingQueueMessage, + departmentsAllowedToForward: departmentsAllowedToForward && departmentsAllowedToForward[0], + }; + console.log(agentList); try { - await saveAgentInfo(id); + await saveDepartmentInfo(id, payload, agentList); dispatchToastMessage({ type: 'success', message: t('saved') }); agentsRoute.push({}); } catch (error) { @@ -161,80 +233,83 @@ export function EditDepartment({ data, id, title }) { - - + {t('Enabled')} - + - + {t('Name')} - + - + {t('Description')} - + - + {t('Show_on_registration_page')} - + {t('Email')} - } onChange={handleEmail}/> + } onChange={handleEmail} placeholder={t('Email')} /> - + {t('Show_on_offline_page')} - + {t('Livechat_DepartmentOfflineMessageToChannel')} - + - - {t('Max_number_of_chats_per_agent')} + {MaxChats && } + {VisitorInactivity && } + {AbandonedMessageInput && } + {WaitingQueueMessageInput && } + {DepartmentForwarding && } + + {t('Request_tag_before_closing_chat')} - + - - {t('How_long_to_wait_to_consider_visitor_abandonment_in_seconds')} + + {t('Tag')} - + + + {t('Conversation_closing_tags_description')} + {tags && tags.length > 0 && + {tags.map((tag, i) => {tag})} + } - - {t('Waiting_queue_message')} - - - - - - {t('Department')}: - - + {DepartmentBusinessHours && } - + {t('Agents')}: - + diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js index 9ab45d1e8fc15..1595cb1a26338 100644 --- a/client/omnichannel/departments/DepartmentsAgentsTable.js +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -1,6 +1,6 @@ import { useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import React, { useMemo, useCallback, useState } from 'react'; +import React, { useMemo, useCallback, useState, useLayoutEffect } from 'react'; import { Box, Table, Icon, Button, NumberInput } from '@rocket.chat/fuselage'; import { Th, GenericTable } from '../../components/GenericTable'; @@ -60,7 +60,7 @@ function AgentsPage({ } -export function RemoveAgentButton({ _id, setAgentList, agentList }) { +export function RemoveAgentButton({ agentId, setAgentList, agentList }) { const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); const t = useTranslation(); @@ -68,7 +68,10 @@ export function RemoveAgentButton({ _id, setAgentList, agentList }) { const handleDelete = useMutableCallback((e) => { e.stopPropagation(); const onDeleteAgent = async () => { - setAgentList([...agentList.filter((e) => e._id !== _id)]); + const newList = agentList.filter((listItem) => listItem.agentId !== agentId); + console.log(newList); + debugger + setAgentList(newList); dispatchToastMessage({ type: 'success', message: t('Agent_removed') }); setModal(); }; @@ -85,35 +88,55 @@ export function RemoveAgentButton({ _id, setAgentList, agentList }) { export function Count({ agentId, setAgentList, agentList }) { const t = useTranslation(); - const [agentCount, setAgentCount] = useState(agentList.find((agent) => agent.agentId === agentId).counta); + const [agentCount, setAgentCount] = useState(agentList.find((agent) => agent.agentId === agentId).count || 0); const [updatedList, setUpdatedList] = useState(agentList); - useMemo(() => setAgentList(updatedList), [setAgentList, updatedList]); + useLayoutEffect(() => setAgentList(updatedList), [setAgentList, updatedList]); - const changeList = () => { + console.log(agentCount); + + const handleCount = useMutableCallback(async (e) => { + const countValue = Number(e.currentTarget.value); + setAgentCount(countValue); setUpdatedList(agentList.map((agent) => { if (agent.agentId === agentId) { - agent.count = agentCount; + agent.count = countValue; } return agent; })); - // setAgentList(newList); - // debugger - // console.log(agentList); - }; - console.log(agentCount); + }); - const handleCount = useMutableCallback(async (e) => { - setAgentCount(Number(e.currentTarget.value)); - changeList(); + return + + ; +} + +export function Order({ agentId, setAgentList, agentList }) { + const t = useTranslation(); + const [agentOrder, setAgentOrder] = useState(agentList.find((agent) => agent.agentId === agentId).order || 0); + const [updatedList, setUpdatedList] = useState(agentList); + + useLayoutEffect(() => setAgentList(updatedList), [setAgentList, updatedList]); + + console.log(agentOrder); + + const handleOrder = useMutableCallback(async (e) => { + const orderValue = Number(e.currentTarget.value); + setAgentOrder(orderValue); + setUpdatedList(agentList.map((agent) => { + if (agent.agentId === agentId) { + agent.order = orderValue; + } + return agent; + })); }); return - + ; } -function DepartmentsAgentsTable({ agents }) { +function DepartmentsAgentsTable({ agents, setAgentListFinal }) { const t = useTranslation(); console.log(agents); @@ -122,13 +145,14 @@ function DepartmentsAgentsTable({ agents }) { const [data, setData] = useState({}); useMemo(() => setData({ users: agentList }), [agentList]); + useMemo(() => setAgentListFinal(agentList && agentList.users), [agentList, setAgentListFinal]); const mediaQuery = useMediaQuery('(min-width: 1024px)'); const header = useMemo(() => [ {t('Name')}, - {t('Username')}, - {t('Email')}, + {t('Count')}, + {t('Order')}, {t('Remove')}, ].filter(Boolean), [t]); @@ -145,7 +169,7 @@ function DepartmentsAgentsTable({ agents }) { - + , [agentList, mediaQuery]); diff --git a/client/omnichannel/departments/DepartmentsRoute.js b/client/omnichannel/departments/DepartmentsRoute.js index 1efc4b1d1cc3e..1a4429942ec92 100644 --- a/client/omnichannel/departments/DepartmentsRoute.js +++ b/client/omnichannel/departments/DepartmentsRoute.js @@ -11,10 +11,9 @@ import { usePermission } from '../../contexts/AuthorizationContext'; import NotAuthorizedPage from '../../components/NotAuthorizedPage'; import DepartmentsPage from './DepartmentsPage'; import EditDepartmentWithData from './DepartmentEdit'; -// import AgentInfo from './AgentInfo'; import { useRouteParameter, useRoute } from '../../contexts/RouterContext'; -export function RemoveAgentButton({ _id, reload }) { +export function RemoveDepartmentButton({ _id, reload }) { const deleteAction = useEndpointAction('DELETE', `livechat/department/${ _id }`); const handleRemoveClick = useMutableCallback(async (e) => { @@ -38,9 +37,9 @@ const useQuery = ({ text, itemsPerPage, current }, [column, direction]) => useMe ...current && { offset: current }, }), [text, itemsPerPage, current, column, direction]); -function AgentsRoute() { +function DepartmentsRoute() { const t = useTranslation(); - const canViewAgents = usePermission('manage-livechat-departments'); + const canViewDepartments = usePermission('manage-livechat-departments'); const [params, setParams] = useState({ text: '', current: 0, itemsPerPage: 25 }); const [sort, setSort] = useState(['name', 'asc']); @@ -74,22 +73,22 @@ function AgentsRoute() { const header = useMemo(() => [ {t('Name')}, {t('Description')}, - {t('Num_Agents')}, + {t('Num_Departments')}, {t('Enabled')}, {t('Show_on_registration_page')}, {t('Remove')}, ].filter(Boolean), [sort, onHeaderClick, t]); - const renderRow = useCallback(({ name, _id, description, numAgents, enabled, showOnRegistration }) => + const renderRow = useCallback(({ name, _id, description, numDepartments, enabled, showOnRegistration }) => {name} {description} - {numAgents || '0'} + {numDepartments || '0'} {enabled ? t('Yes') : t('No')} {showOnRegistration ? t('Yes') : t('No')} - + , [onRowClick, t, reload]); - if (!canViewAgents) { + if (!canViewDepartments) { return ; } @@ -97,7 +96,7 @@ function AgentsRoute() { return ; + title={context === 'edit' ? t('Edit_department') : t('New_department')} />; } @@ -113,4 +112,4 @@ function AgentsRoute() { ; } -export default AgentsRoute; +export default DepartmentsRoute; diff --git a/ee/client/omnichannel/additionalForms/DepartmentBusinessHours.js b/ee/client/omnichannel/additionalForms/DepartmentBusinessHours.js new file mode 100644 index 0000000000000..49ea87039a197 --- /dev/null +++ b/ee/client/omnichannel/additionalForms/DepartmentBusinessHours.js @@ -0,0 +1,22 @@ +import React, { useMemo } from 'react'; +import { Field, TextInput } from '@rocket.chat/fuselage'; +// import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useTranslation } from '../../../../client/contexts/TranslationContext'; +import { useEndpointDataExperimental } from '../../../../client/hooks/useEndpointDataExperimental'; + +export const DepartmentBusinessHours = ({ bhId }) => { + const t = useTranslation(); + const { data } = useEndpointDataExperimental('livechat/business-hour', useMemo(() => ({ _id: bhId, type: 'custom' }), [bhId])); + + const name = data && data.businessHour && data.businessHour.name; + + return + {t('Business_Hour')} + + + + ; +}; + +export default DepartmentBusinessHours; diff --git a/ee/client/omnichannel/additionalForms/DepartmentForwarding.js b/ee/client/omnichannel/additionalForms/DepartmentForwarding.js new file mode 100644 index 0000000000000..c8b40cdd250d9 --- /dev/null +++ b/ee/client/omnichannel/additionalForms/DepartmentForwarding.js @@ -0,0 +1,23 @@ +import React, { useMemo } from 'react'; +import { Field, MultiSelectFiltered } from '@rocket.chat/fuselage'; +// import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useTranslation } from '../../../../client/contexts/TranslationContext'; +import { useEndpointDataExperimental } from '../../../../client/hooks/useEndpointDataExperimental'; + +export const DepartmentForwarding = ({ value, handler, label, placeholder }) => { + const t = useTranslation(); + const { data } = useEndpointDataExperimental('livechat/department'); + + const options = useMemo(() => (data && [...data.departments.map((department) => [department._id, department.name])]) || [], [data]); + + return + {t(label)} + + + + {t('List_of_departments_for_forward_description')} + ; +}; + +export default DepartmentForwarding; diff --git a/ee/client/omnichannel/additionalForms/EeNumberInput.js b/ee/client/omnichannel/additionalForms/EeNumberInput.js new file mode 100644 index 0000000000000..f3dca042d3389 --- /dev/null +++ b/ee/client/omnichannel/additionalForms/EeNumberInput.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { NumberInput, Field } from '@rocket.chat/fuselage'; +// import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useTranslation } from '../../../../client/contexts/TranslationContext'; + +export const EeNumberInput = ({ value, handler, label, placeholder }) => { + const t = useTranslation(); + + return + {t(label)} + + + + ; +}; + +export default EeNumberInput; diff --git a/ee/client/omnichannel/additionalForms/EeTextAreaInput.js b/ee/client/omnichannel/additionalForms/EeTextAreaInput.js new file mode 100644 index 0000000000000..aa302699d25a5 --- /dev/null +++ b/ee/client/omnichannel/additionalForms/EeTextAreaInput.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { TextAreaInput, Field } from '@rocket.chat/fuselage'; +// import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useTranslation } from '../../../../client/contexts/TranslationContext'; + +export const EeTextAreaInput = ({ value, handler, label, placeholder }) => { + const t = useTranslation(); + + return + {t(label)} + + + + ; +}; + +export default EeTextAreaInput; diff --git a/ee/client/omnichannel/additionalForms/EeTextInput.js b/ee/client/omnichannel/additionalForms/EeTextInput.js new file mode 100644 index 0000000000000..078d25d651c2b --- /dev/null +++ b/ee/client/omnichannel/additionalForms/EeTextInput.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { TextInput, Field } from '@rocket.chat/fuselage'; +// import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useTranslation } from '../../../../client/contexts/TranslationContext'; + +export const EeTextInput = ({ value, handler, label, placeholder }) => { + const t = useTranslation(); + + return + {t(label)} + + + + ; +}; + +export default EeTextInput; diff --git a/ee/client/omnichannel/additionalForms/register.js b/ee/client/omnichannel/additionalForms/register.js index 8865d8609052d..fd8b5c698ca30 100644 --- a/ee/client/omnichannel/additionalForms/register.js +++ b/ee/client/omnichannel/additionalForms/register.js @@ -11,6 +11,11 @@ hasLicense('livechat-enterprise').then((enabled) => { registerForm({ useCustomFieldsAdditionalForm: () => useMemo(() => lazy(() => import('./CustomFieldsAdditionalForm')), []) }); registerForm({ useMaxChatsPerAgent: () => useMemo(() => lazy(() => import('./MaxChatsPerAgent')), []) }); registerForm({ useMaxChatsPerAgentDisplay: () => useMemo(() => lazy(() => import('./MaxChatsPerAgentDisplay')), []) }); + registerForm({ useEeNumberInput: () => useMemo(() => lazy(() => import('./EeNumberInput')), []) }); + registerForm({ useEeTextAreaInput: () => useMemo(() => lazy(() => import('./EeTextAreaInput')), []) }); + registerForm({ useEeTextInput: () => useMemo(() => lazy(() => import('./EeTextInput')), []) }); + registerForm({ useDepartmentForwarding: () => useMemo(() => lazy(() => import('./DepartmentForwarding')), []) }); + registerForm({ useDepartmentBusinessHours: () => useMemo(() => lazy(() => import('./DepartmentBusinessHours')), []) }); registerForm({ useCustomFieldsAdditionalForm: () => useMemo(() => lazy(() => import('./CustomFieldsAdditionalForm')), []) }); registerForm({ useBusinessHoursTimeZone: () => useMemo(() => lazy(() => import('./BusinessHoursTimeZone')), []) }); registerForm({ useBusinessHoursMultiple: () => useMemo(() => lazy(() => import('./BusinessHoursMultiple')), []) }); From e2cdc0bc5fdfeffc54491db6f49f01d24e838d71 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 15 Sep 2020 18:07:02 -0300 Subject: [PATCH 007/162] lint --- client/omnichannel/departments/DepartmentEdit.js | 3 +-- client/omnichannel/departments/DepartmentsAgentsTable.js | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/client/omnichannel/departments/DepartmentEdit.js b/client/omnichannel/departments/DepartmentEdit.js index e4858cb47a234..4184d9ade378d 100644 --- a/client/omnichannel/departments/DepartmentEdit.js +++ b/client/omnichannel/departments/DepartmentEdit.js @@ -12,7 +12,6 @@ import { FormSkeleton } from './Skeleton'; import { useForm } from '../../hooks/useForm'; import { useRoute } from '../../contexts/RouterContext'; import Page from '../../components/basic/Page'; -import { AutoCompleteDepartment } from '../../components/basic/AutoCompleteDepartment'; import DepartmentsAgentsTable from './DepartmentsAgentsTable'; import { formsSubscription } from '../additionalForms'; @@ -85,7 +84,7 @@ export function EditDepartment({ data, id, title }) { console.log(data); - const { values, handlers, hasUnsavedChanges } = useForm({ + const { values, handlers } = useForm({ name: (department && department.name) || '', email: (department && department.email) || '', description: (department && department.description) || '', diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js index 1595cb1a26338..937241c2dd184 100644 --- a/client/omnichannel/departments/DepartmentsAgentsTable.js +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -70,7 +70,6 @@ export function RemoveAgentButton({ agentId, setAgentList, agentList }) { const onDeleteAgent = async () => { const newList = agentList.filter((listItem) => listItem.agentId !== agentId); console.log(newList); - debugger setAgentList(newList); dispatchToastMessage({ type: 'success', message: t('Agent_removed') }); setModal(); From d42faed39ca507569007a078e60a789c1e6667ca Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 21 Sep 2020 16:54:25 -0300 Subject: [PATCH 008/162] Add correct buttons and fix some errors --- .../omnichannel/departments/DepartmentEdit.js | 4 +- .../departments/DepartmentsPage.js | 41 ++++++------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/client/omnichannel/departments/DepartmentEdit.js b/client/omnichannel/departments/DepartmentEdit.js index 4184d9ade378d..59727b291591f 100644 --- a/client/omnichannel/departments/DepartmentEdit.js +++ b/client/omnichannel/departments/DepartmentEdit.js @@ -79,7 +79,7 @@ export function EditDepartment({ data, id, title }) { const { department } = data || { department: {} }; - const [tags, setTags] = useState(department && department.chatClosingTags); + const [tags, setTags] = useState((department && department.chatClosingTags) || []); const [tagsText, setTagsText] = useState(); console.log(data); @@ -88,7 +88,7 @@ export function EditDepartment({ data, id, title }) { name: (department && department.name) || '', email: (department && department.email) || '', description: (department && department.description) || '', - enabled: (department && department.enabled) || '', + enabled: (department && department.enabled) || true, maxNumberSimultaneousChat: (department && department.maxNumberSimultaneousChat) || undefined, showOnRegistration: (department && department.showOnRegistration) || true, showOnOfflineForm: (department && department.showOnOfflineForm) || true, diff --git a/client/omnichannel/departments/DepartmentsPage.js b/client/omnichannel/departments/DepartmentsPage.js index 0d4ad6187a5cd..a3995b05ed8a7 100644 --- a/client/omnichannel/departments/DepartmentsPage.js +++ b/client/omnichannel/departments/DepartmentsPage.js @@ -4,9 +4,9 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import Page from '../../components/basic/Page'; import { useTranslation } from '../../contexts/TranslationContext'; -import { useEndpointAction } from '../../hooks/useEndpointAction'; import { GenericTable } from '../../components/GenericTable'; -import { UserAutoComplete } from '../../components/basic/AutoComplete'; +import { useRoute } from '../../contexts/RouterContext'; + const FilterByText = ({ setFilter, ...props }) => { const t = useTranslation(); @@ -23,33 +23,8 @@ const FilterByText = ({ setFilter, ...props }) => { ; }; - -function AddDepartment({ reload, ...props }) { - const t = useTranslation(); - const [username, setUsername] = useState(); - - const saveAction = useEndpointAction('POST', 'livechat/users/department', { username }); - - const handleSave = useMutableCallback(async () => { - if (!username) { - return; - } - const result = await saveAction(); - if (!result.success) { - return; - } - reload(); - setUsername(); - }); - return - - - ; -} - function DepartmentsPage({ data, - reload, header, setParams, params, @@ -57,10 +32,18 @@ function DepartmentsPage({ renderRow, children, }) { + const departmentsRoute = useRoute('omnichannel-departments'); + + const onAddNew = useMutableCallback(() => departmentsRoute.push({ + context: 'new', + })); return - - + + + From bae5a2404791ba35e3eb3b1fba159154a1eff951 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 21 Sep 2020 17:05:51 -0300 Subject: [PATCH 009/162] fix import --- client/omnichannel/departments/DepartmentsAgentsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js index 937241c2dd184..9088fb30df016 100644 --- a/client/omnichannel/departments/DepartmentsAgentsTable.js +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -7,7 +7,7 @@ import { Th, GenericTable } from '../../components/GenericTable'; import { useTranslation } from '../../contexts/TranslationContext'; import { useEndpointAction } from '../../hooks/useEndpointAction'; import UserAvatar from '../../components/basic/avatar/UserAvatar'; -import DeleteWarningModal from '../DeleteWarningModal'; +import DeleteWarningModal from '../../components/DeleteWarningModal'; import { useSetModal } from '../../contexts/ModalContext'; import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext'; import { AutoCompleteAgent } from '../../components/basic/AutoCompleteAgent'; From a06f54a933201f375b80c2b58082a35700bce5d7 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 21 Sep 2020 17:17:43 -0300 Subject: [PATCH 010/162] Fix error with empty department agents --- client/omnichannel/departments/DepartmentsAgentsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js index 9088fb30df016..a846a8b86bf68 100644 --- a/client/omnichannel/departments/DepartmentsAgentsTable.js +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -144,7 +144,7 @@ function DepartmentsAgentsTable({ agents, setAgentListFinal }) { const [data, setData] = useState({}); useMemo(() => setData({ users: agentList }), [agentList]); - useMemo(() => setAgentListFinal(agentList && agentList.users), [agentList, setAgentListFinal]); + useMemo(() => setAgentListFinal((agentList && agentList.users) || []), [agentList, setAgentListFinal]); const mediaQuery = useMediaQuery('(min-width: 1024px)'); From af468cd282806af6d9f1b98403380d2cd843a7ee Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 21 Sep 2020 17:22:25 -0300 Subject: [PATCH 011/162] fix title and tags --- client/omnichannel/departments/DepartmentEdit.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/omnichannel/departments/DepartmentEdit.js b/client/omnichannel/departments/DepartmentEdit.js index 59727b291591f..4087749a8202c 100644 --- a/client/omnichannel/departments/DepartmentEdit.js +++ b/client/omnichannel/departments/DepartmentEdit.js @@ -16,7 +16,7 @@ import DepartmentsAgentsTable from './DepartmentsAgentsTable'; import { formsSubscription } from '../additionalForms'; -export default function EditDepartmentWithData({ id, reload }) { +export default function EditDepartmentWithData({ id, reload, title }) { const t = useTranslation(); const { data, state, error } = useEndpointDataExperimental(`livechat/department/${ id }`) || {}; // const { data: userDepartments, state: userDepartmentsState, error: userDepartmentsError } = useEndpointDataExperimental(`livechat/agents/${ id }/departments`); @@ -30,7 +30,7 @@ export default function EditDepartmentWithData({ id, reload }) { return {t('User_not_found')}; } console.log(data); - return ; + return ; } // abandonedRoomsCloseCustomMessage: "fuk u" @@ -291,8 +291,8 @@ export function EditDepartment({ data, id, title }) { - - {t('Tag')} + {requestTagBeforeClosingChat && + {t('Conversation_closing_tags')} + - - {t('Enabled')} - - - - - - {t('Name')} - - - - - - {t('Description')} - - - - - - {t('Show_on_registration_page')} - - - - - - {t('Email')} - - } onChange={handleEmail} placeholder={t('Email')} /> - - - - {t('Show_on_offline_page')} - - - - - - {t('Livechat_DepartmentOfflineMessageToChannel')} - - - - - {MaxChats && } - {VisitorInactivity && } - {AbandonedMessageInput && } - {WaitingQueueMessageInput && } - {DepartmentForwarding && } - - {t('Request_tag_before_closing_chat')} - - - - - {requestTagBeforeClosingChat && - {t('Conversation_closing_tags')} - - - - - {t('Conversation_closing_tags_description')} - {tags && tags.length > 0 && - {tags.map((tag, i) => {tag})} - } - } - {DepartmentBusinessHours && } - - - {t('Agents')}: - - + + + {t('Enabled')} + + + + + + {t('Name')}* + + + + + + {t('Description')} + + + + + + {t('Show_on_registration_page')} + + + + + + {t('Email')}* + + } onChange={handleEmail} placeholder={t('Email')} /> + + + + {t('Show_on_offline_page')} + + + + + + {t('Livechat_DepartmentOfflineMessageToChannel')} + + + + + {MaxChats && } + {VisitorInactivity && } + {AbandonedMessageInput && } + {WaitingQueueMessageInput && } + {DepartmentForwarding && } + + {t('Request_tag_before_closing_chat')} + + + + + {requestTagBeforeClosingChat && + {t('Conversation_closing_tags')}* + + + + + {t('Conversation_closing_tags_description')} + {tags && tags.length > 0 && + {tags.map((tag, i) => {tag})} + } + } + {DepartmentBusinessHours && } + + + {t('Agents')}: + + + ; diff --git a/client/omnichannel/departments/DepartmentsAgentsTable.js b/client/omnichannel/departments/DepartmentsAgentsTable.js index a846a8b86bf68..ae7c6e1d9f384 100644 --- a/client/omnichannel/departments/DepartmentsAgentsTable.js +++ b/client/omnichannel/departments/DepartmentsAgentsTable.js @@ -1,6 +1,6 @@ import { useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import React, { useMemo, useCallback, useState, useLayoutEffect } from 'react'; +import React, { useCallback, useState, useLayoutEffect, useEffect } from 'react'; import { Box, Table, Icon, Button, NumberInput } from '@rocket.chat/fuselage'; import { Th, GenericTable } from '../../components/GenericTable'; @@ -19,7 +19,6 @@ function AddAgent({ agentList, setAgentList, ...props }) { const dispatchToastMessage = useToastMessageDispatch(); const handleAgent = useMutableCallback((e) => setUserId(e)); - console.log(userId); const handleSave = useMutableCallback(async () => { if (!userId) { @@ -27,11 +26,11 @@ function AddAgent({ agentList, setAgentList, ...props }) { } const { user } = await getAgent(); - if (agentList.filter((e) => e.agentId === userId).length === 0) { + if (agentList.filter((e) => e._id === user._id).length === 0) { setAgentList([user, ...agentList]); setUserId(); } else { - dispatchToastMessage({ type: 'error', message: t('Already_selected') }); + dispatchToastMessage({ type: 'error', message: t('This_agent_was_already_selected') }); } }); return @@ -43,7 +42,6 @@ function AddAgent({ agentList, setAgentList, ...props }) { function AgentsPage({ data, reload, - header, setParams, params, renderRow, @@ -51,12 +49,20 @@ function AgentsPage({ agentList, setAgentList, }) { - console.log(data); - return - + const t = useTranslation(); + + const [header] = useState(() => [ + {t('Name')}, + {t('Count')}, + {t('Order')}, + {t('Remove')}, + ]); + + return <> + {children} - ; + ; } @@ -69,7 +75,6 @@ export function RemoveAgentButton({ agentId, setAgentList, agentList }) { e.stopPropagation(); const onDeleteAgent = async () => { const newList = agentList.filter((listItem) => listItem.agentId !== agentId); - console.log(newList); setAgentList(newList); dispatchToastMessage({ type: 'success', message: t('Agent_removed') }); setModal(); @@ -78,11 +83,7 @@ export function RemoveAgentButton({ agentId, setAgentList, agentList }) { setModal( setModal()}/>); }); - return - - ; + return ; } export function Count({ agentId, setAgentList, agentList }) { @@ -92,8 +93,6 @@ export function Count({ agentId, setAgentList, agentList }) { useLayoutEffect(() => setAgentList(updatedList), [setAgentList, updatedList]); - console.log(agentCount); - const handleCount = useMutableCallback(async (e) => { const countValue = Number(e.currentTarget.value); setAgentCount(countValue); @@ -105,9 +104,7 @@ export function Count({ agentId, setAgentList, agentList }) { })); }); - return - - ; + return ; } export function Order({ agentId, setAgentList, agentList }) { @@ -117,8 +114,6 @@ export function Order({ agentId, setAgentList, agentList }) { useLayoutEffect(() => setAgentList(updatedList), [setAgentList, updatedList]); - console.log(agentOrder); - const handleOrder = useMutableCallback(async (e) => { const orderValue = Number(e.currentTarget.value); setAgentOrder(orderValue); @@ -130,31 +125,18 @@ export function Order({ agentId, setAgentList, agentList }) { })); }); - return - - ; + return ; } function DepartmentsAgentsTable({ agents, setAgentListFinal }) { - const t = useTranslation(); - - console.log(agents); - const [agentList, setAgentList] = useState(agents || []); const [data, setData] = useState({}); - useMemo(() => setData({ users: agentList }), [agentList]); - useMemo(() => setAgentListFinal((agentList && agentList.users) || []), [agentList, setAgentListFinal]); + useEffect(() => setData({ users: agentList }), [agentList]); + useEffect(() => setAgentListFinal((agentList && agentList.users) || []), [agentList, setAgentListFinal]); const mediaQuery = useMediaQuery('(min-width: 1024px)'); - const header = useMemo(() => [ - {t('Name')}, - {t('Count')}, - {t('Order')}, - {t('Remove')}, - ].filter(Boolean), [t]); - const renderRow = useCallback(({ agentId, username, name, avatarETag }) => @@ -167,14 +149,19 @@ function DepartmentsAgentsTable({ agents, setAgentListFinal }) { - - - + + + + + + + + + , [agentList, mediaQuery]); return [ {t('Name')}, From 068c64821cbe2e0a032fbbdc5c56e0ffdb0d4ff9 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 22 Sep 2020 14:40:47 -0300 Subject: [PATCH 016/162] update fuselage --- client/contexts/CallContext.ts | 28 +++++ client/providers/CallProvider.tsx | 103 +++++++++++++++++ client/sidebar/Call.js | 120 ++++++++++++++++++++ client/sidebar/index.js | 3 + package-lock.json | 178 ++++++++++++------------------ package.json | 16 +-- 6 files changed, 335 insertions(+), 113 deletions(-) create mode 100644 client/contexts/CallContext.ts create mode 100644 client/providers/CallProvider.tsx create mode 100644 client/sidebar/Call.js create mode 100644 client/sidebar/index.js diff --git a/client/contexts/CallContext.ts b/client/contexts/CallContext.ts new file mode 100644 index 0000000000000..19113bbb5701b --- /dev/null +++ b/client/contexts/CallContext.ts @@ -0,0 +1,28 @@ +import { createContext, useContext } from 'react'; + +type CallActions = { + id: string; + icon?: string; + variation?: 'success' | 'danger' | 'normal'; + disabled?: boolean; + callback: () => undefined; +} + +type Call = { + name: string; + avatar: string; + actions: Array; +} + +type CallContextValue = { + calls: Array; + setCalls: (calls) => {}; +} + +export const CallContext = createContext({ + calls: [], + setCalls: () => undefined, +}); + +export const useCall = (): CallContextValue => + useContext(CallContext); diff --git a/client/providers/CallProvider.tsx b/client/providers/CallProvider.tsx new file mode 100644 index 0000000000000..628f6b9064541 --- /dev/null +++ b/client/providers/CallProvider.tsx @@ -0,0 +1,103 @@ +import React, { FC, useState, useMemo, useEffect } from 'react'; +import { Avatar, Sidebar, ActionButton } from '@rocket.chat/fuselage'; + +import { CallContext } from '../contexts/CallContext'; + +const avatar = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z'; + +const VARIATIONS = { + success: { primary: true, success: true }, + danger: { primary: true, danger: true }, + normal: { }, +}; + + +export const CallProvider: FC = ({ children }) => { + const [calls, setCalls] = useState([]); + + const value = useMemo(() => ({ + calls, + }), [calls]); + + useEffect(() => { + // do whatever you want here... + setCalls([{ + id: 'call1', + name: 'Guilherme Gazzo', + icon: 'lock', + avatar, + actions: [ + { + icon: 'phone', + label: 'Call', + callback: () => alert('Call'), + variation: 'normal', + }, + { + icon: 'phone', + label: 'Call', + callback: () => alert('Call'), + variation: 'success', + }, + { + icon: 'circle-cross', + label: 'End Call', + callback: () => alert('End Call'), + variation: 'danger', + }, + ], + }, + { + id: 'call2', + name: 'Guilherme Gazzo 2', + icon: 'lock', + avatar, + actions: [ + { + icon: 'phone', + label: 'Call', + callback: () => alert('Call'), + variation: 'normal', + }, + { + icon: 'phone', + label: 'Call', + callback: () => alert('Call'), + variation: 'success', + }, + { + icon: 'circle-cross', + label: 'End Call', + callback: () => alert('End Call'), + variation: 'danger', + }, + ], + render: (call): FC => + + + + + { call.icon && } + {call.name} + + {call.actions && + + {call.actions.map((action, index) => + )} + + } + , + }, + ]); + }, []); + + return ; +}; diff --git a/client/sidebar/Call.js b/client/sidebar/Call.js new file mode 100644 index 0000000000000..2599ae599640d --- /dev/null +++ b/client/sidebar/Call.js @@ -0,0 +1,120 @@ +import React, { useMemo } from 'react'; +import colors from '@rocket.chat/fuselage-tokens/colors'; +import { Avatar, Sidebar, ActionButton, Box } from '@rocket.chat/fuselage'; + +import { useTranslation } from '../contexts/TranslationContext'; +import { useCall } from '../contexts/CallContext'; + + +const groupByName = (colors) => { + const map = {}; + + Object.entries(colors).reduce((map, [key, value]) => { + const [group, ...id] = key; + + map[group] = map[group] || {}; + + map[group][id.join('')] = value; + return map; + }, map); + return map; +}; + + +const COLOR_MAP = { + b: 'blue', + g: 'green', + n: 'neutral', + o: 'orange', + p: 'purple', + r: 'red', + y: 'yellow', +}; + +const invertGroup = (colors) => { + const keys = Object.keys(colors); + const values = Object.values(colors).reverse(); + return Object.fromEntries(keys.map((_, i) => [keys[i], values[i]])); +}; + +const invert = (colors) => Object.entries(colors).reduce((result, [key, colors]) => { + result[key] = invertGroup(colors); + return result; +}, {}); + +const toCssVars = (colors, selector = ':root') => `${ selector } { ${ Object.entries(colors).map(([group, colors]) => Object.entries(colors).map(([id, value]) => `--rcx-color-${ COLOR_MAP[group] }-${ id }: ${ value };`).join('\n')).join('\n') }}`; + +const VARIATIONS = { + success: { primary: true, success: true }, + danger: { primary: true, danger: true }, + normal: { ghost: true }, +}; + + +export default () => { + const t = useTranslation(); + + const { calls } = useCall(); + + if (!calls?.length) { + return; + } + + return <> + + + + + + + + + + + + + Title + + + + + + {t('Calls')} + {calls.map((call, index) => (call.render ? call.render(call) : + + + + + { call.icon && } + {call.name} + + {call.actions && + + {call.actions.map((action, index) => + )} + + } + ))} + + ; +}; diff --git a/client/sidebar/index.js b/client/sidebar/index.js new file mode 100644 index 0000000000000..453efd7c841e9 --- /dev/null +++ b/client/sidebar/index.js @@ -0,0 +1,3 @@ +import { createTemplateForComponent } from '../reactAdapters'; + +createTemplateForComponent('SidebarCall', () => import('./Call')); diff --git a/package-lock.json b/package-lock.json index dd10719517e01..aef9502f37fa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5034,7 +5034,6 @@ "requires": { "adm-zip": "^0.4.9", "cryptiles": "^4.1.3", - "file-type": "^15.0.1", "lodash.clonedeep": "^4.5.0", "semver": "^5.5.0", "stack-trace": "0.0.10", @@ -5042,6 +5041,17 @@ "uuid": "^3.2.1" }, "dependencies": { + "file-type": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-15.0.1.tgz", + "integrity": "sha512-0LieQlSA3bWUdErNrxzxfI4rhsvNAVPBO06R8pTc1hp9SE6nhqlVyvhcaXoMmtXkBTPnQenbMPLW9X76hH76oQ==", + "requires": { + "readable-web-to-node-stream": "^2.0.0", + "strtok3": "^6.0.3", + "token-types": "^2.0.0", + "typedarray-to-buffer": "^3.1.5" + } + }, "typescript": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", @@ -5050,9 +5060,9 @@ } }, "@rocket.chat/css-in-js": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.14.1.tgz", - "integrity": "sha512-YC96YAhkIexMNz9cJh/Leimxr3SWp45ZSIv/+looRkdN8BNTrCX9bRkxFZq2CChAuSlvKbuHSTqPXNSCa2i9Bg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.15.1.tgz", + "integrity": "sha512-pnRVaVgZ1VFpFo+8y/NRAizpz+TZ8VRW9a0GjsojJXMFfN5I7DLou4Jh+WTCVSiDc4W5SEy9unzmmAC+VPW3dQ==", "requires": { "@emotion/hash": "^0.8.0", "@emotion/stylis": "^0.8.5" @@ -5068,40 +5078,29 @@ } }, "@rocket.chat/fuselage": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage/-/fuselage-0.14.1.tgz", - "integrity": "sha512-eo4+481cnSCBzLx5AataqoSfBW1uEMHNLxBbQWLS6ytsedR0R+ISTMaCcFj9d6WoJ5G8A0I5Yl3V51DiMfcAEA==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage/-/fuselage-0.15.1.tgz", + "integrity": "sha512-KBFGZmIwLZ1LaFclO+n6qhi+S7IFXGe6RI3p2V/pqAkyWphJFaW0cdP14Y+zFdcnc8YdoVBtpPihDEmO1KUIFQ==", "requires": { - "@rocket.chat/css-in-js": "^0.14.1", - "@rocket.chat/fuselage-tokens": "^0.14.1", + "@rocket.chat/css-in-js": "^0.15.1", + "@rocket.chat/fuselage-tokens": "^0.15.1", "invariant": "^2.2.4", "react-keyed-flatten-children": "^1.2.0" - }, - "dependencies": { - "@rocket.chat/css-in-js": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.14.1.tgz", - "integrity": "sha512-YC96YAhkIexMNz9cJh/Leimxr3SWp45ZSIv/+looRkdN8BNTrCX9bRkxFZq2CChAuSlvKbuHSTqPXNSCa2i9Bg==", - "requires": { - "@emotion/hash": "^0.8.0", - "@emotion/stylis": "^0.8.5" - } - } } }, "@rocket.chat/fuselage-hooks": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-hooks/-/fuselage-hooks-0.14.1.tgz", - "integrity": "sha512-9BIabHV/v8/DM/Q2lKMwLvnhWhppVJiL6/M3I3W6NbRFw7uVzDAHn51UBKHSQKcWHyWeiAlV0e5oHiTAP1WV/g==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-hooks/-/fuselage-hooks-0.15.1.tgz", + "integrity": "sha512-n0OFi73Z0p6nc8QRjZv3o5GtylcZvnQ27miFuCHB+2P011K7aCJl/7jZGIap2PkwRjY6tUnP+EYZZK4N3l0jTA==", "requires": { - "@rocket.chat/fuselage-tokens": "^0.14.1", + "@rocket.chat/fuselage-tokens": "^0.15.1", "use-subscription": "^1.4.1" } }, "@rocket.chat/fuselage-polyfills": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-polyfills/-/fuselage-polyfills-0.14.1.tgz", - "integrity": "sha512-DjMwnT+jgnNb0LSLPjW95YEEdFRehURpSqkg938BpEIiHF8DPbMtkDuro9nNblZ8i+1+iGFj8SQE5PSsukWlpg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-polyfills/-/fuselage-polyfills-0.15.1.tgz", + "integrity": "sha512-711MIC4taq897ZL67BtbMjpefkgAisnqmBTW1DvtJl+CIuu6rpuRdBRRscphzYrXHkWv+3+0riwp6atjxz+PjQ==", "requires": { "@juggle/resize-observer": "^3.1.2", "clipboard-polyfill": "^2.8.6", @@ -5109,19 +5108,22 @@ } }, "@rocket.chat/fuselage-tokens": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.14.1.tgz", - "integrity": "sha512-AE+aKfsynTecGwNHnTnI/G9cdQkeo7ZEuxbrpgtLyF5oLx4MqKPpPLdBYwTXG2Jeno+3dJI/XkiYyffK2xbsbQ==" + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.15.1.tgz", + "integrity": "sha512-RFvEkpY7BOPIg8+dHea/2hcP92sSTNrhcxGEpIFFV5INsOAnsJupHztPLTWqGHWrX0BNx1H9ZpPGiALyD5XyNA==" }, "@rocket.chat/fuselage-ui-kit": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-ui-kit/-/fuselage-ui-kit-0.14.1.tgz", - "integrity": "sha512-b3tonwxpHSnm9/WpcQui8NkE1s1AABhX5tyRqWJDjJ2eTgCT1cWfgrh1xwtyANk4biuQEfDaVhVsdCxFh+OgYQ==" + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-ui-kit/-/fuselage-ui-kit-0.15.1.tgz", + "integrity": "sha512-xP0Zu9LzyqTsRr+kNOkPAD+LaTi/23o6dlkkpDFGwqH8iBcM2af0SRiSwWF79qyl3ZsaipzKxs4ZtMgTnb01ug==", + "requires": { + "@rocket.chat/fuselage-polyfills": "^0.15.1" + } }, "@rocket.chat/icons": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/icons/-/icons-0.14.1.tgz", - "integrity": "sha512-V/lq5xdIxx016pKB8kSQuQ+IKLC0ULdkZ72M/DEMU1DqXahWDEJeRiBppOO8YN5/mw5INaf8asWOqxJfIFld0w==" + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/icons/-/icons-0.15.1.tgz", + "integrity": "sha512-wPKIH3vv8enZ4gQzL0ozZu0VRcYSERLHnBAMX0bF75jPsh9uro26D/8L0A9BSEwh/Uc08wz1II4WaFUi61GlZQ==" }, "@rocket.chat/livechat": { "version": "1.7.4", @@ -5148,6 +5150,12 @@ "whatwg-fetch": "^3.4.0" }, "dependencies": { + "@rocket.chat/ui-kit": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.14.1.tgz", + "integrity": "sha512-gugjURxPPXurLKh9tNMvr7EgWciMDNjwXehqLnlNZj2Efg1PcuSQnG3/xD92YJzOuzKnKhZ8948v/KfVxcY87Q==", + "dev": true + }, "query-string": { "version": "6.13.2", "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.2.tgz", @@ -5168,9 +5176,9 @@ } }, "@rocket.chat/mp3-encoder": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/mp3-encoder/-/mp3-encoder-0.14.1.tgz", - "integrity": "sha512-GZUTJ+VyamxR+QvCD0wm//hT60CJ6r+NsH/gs6ejbpGjzYSGNvYCO2jkWw6K0JEVGqgagGD0ufPQ7WfK2fgLtg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/mp3-encoder/-/mp3-encoder-0.15.1.tgz", + "integrity": "sha512-dOyOuxgteKy3ZJPScfRrqxa0/GQfWcWAr/WbZLCA4RheZrIAoBgr8/RWzV/2PZOvRZmJW2OTKveuJ8EauGDOhA==", "requires": { "lamejs": "git+https://github.com/zhuker/lamejs.git" } @@ -5218,9 +5226,9 @@ } }, "@rocket.chat/ui-kit": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.14.1.tgz", - "integrity": "sha512-gugjURxPPXurLKh9tNMvr7EgWciMDNjwXehqLnlNZj2Efg1PcuSQnG3/xD92YJzOuzKnKhZ8948v/KfVxcY87Q==" + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.15.1.tgz", + "integrity": "sha512-uTMQ+5nIq/95V2BttpY8hE/8DmyKedqLe7yy+LTf/6Ms9zMOI5p1kHDYQDvCadpQYUDH4e9BI1ilIJ2vanAwcA==" }, "@samverschueren/stream-to-observable": { "version": "0.3.1", @@ -8132,11 +8140,6 @@ } } }, - "@tokenizer/token": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.1.1.tgz", - "integrity": "sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w==" - }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -8221,7 +8224,8 @@ "@types/debug": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true }, "@types/dedent": { "version": "0.7.0", @@ -17915,7 +17919,7 @@ }, "chownr": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true @@ -17950,7 +17954,7 @@ }, "debug": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "optional": true, @@ -17981,7 +17985,7 @@ }, "fs-minipass": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -18015,7 +18019,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, @@ -18047,7 +18051,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -18068,7 +18072,7 @@ }, "inherits": { "version": "2.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true @@ -18109,14 +18113,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, @@ -18127,7 +18131,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, @@ -18137,7 +18141,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -18147,7 +18151,7 @@ }, "ms": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true, "optional": true @@ -18161,7 +18165,7 @@ }, "needle": { "version": "2.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "dev": true, "optional": true, @@ -18173,7 +18177,7 @@ }, "node-pre-gyp": { "version": "0.12.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "dev": true, "optional": true, @@ -18203,14 +18207,14 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "optional": true, @@ -18290,7 +18294,7 @@ }, "process-nextick-args": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true @@ -18310,7 +18314,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -18335,7 +18339,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "", + "resolved": false, "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, @@ -18366,7 +18370,7 @@ }, "semver": { "version": "5.7.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true, "optional": true @@ -18426,7 +18430,7 @@ }, "tar": { "version": "4.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, @@ -18466,7 +18470,7 @@ }, "yallist": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true @@ -26376,11 +26380,6 @@ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.4.456.tgz", "integrity": "sha512-yckJEHq3F48hcp6wStEpbN9McOj328Ib09UrBlGAKxvN2k+qYPN5iq6TH6jD1C0pso7zTep+g/CKsYgdrQd5QA==" }, - "peek-readable": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.0.tgz", - "integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA==" - }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -29175,11 +29174,6 @@ "util-deprecate": "~1.0.1" } }, - "readable-web-to-node-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-2.0.0.tgz", - "integrity": "sha512-+oZJurc4hXpaaqsN68GoZGQAQIA3qr09Or4fqEsargABnbe5Aau8hFn6ISVleT3cpY/0n/8drn7huyyEvTbghA==" - }, "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -31383,16 +31377,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, - "strtok3": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.4.tgz", - "integrity": "sha512-rqWMKwsbN9APU47bQTMEYTPcwdpKDtmf1jVhHzNW2cL1WqAxaM9iBb9t5P2fj+RV2YsErUWgQzHD5JwV0uCTEQ==", - "requires": { - "@tokenizer/token": "^0.1.1", - "@types/debug": "^4.1.5", - "peek-readable": "^3.1.0" - } - }, "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", @@ -32710,22 +32694,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "token-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-2.0.0.tgz", - "integrity": "sha512-WWvu8sGK8/ZmGusekZJJ5NM6rRVTTDO7/bahz4NGiSDb/XsmdYBn6a1N/bymUHuWYTWeuLUg98wUzvE4jPdCZw==", - "requires": { - "@tokenizer/token": "^0.1.0", - "ieee754": "^1.1.13" - }, - "dependencies": { - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - } - } - }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", diff --git a/package.json b/package.json index 45a2f4b661dde..56a57c8b67746 100644 --- a/package.json +++ b/package.json @@ -131,14 +131,14 @@ "@nivo/line": "^0.61.1", "@nivo/pie": "^0.61.1", "@rocket.chat/apps-engine": "1.18.0-beta.3848", - "@rocket.chat/css-in-js": "^0.14.1", - "@rocket.chat/fuselage": "^0.14.1", - "@rocket.chat/fuselage-hooks": "^0.14.1", - "@rocket.chat/fuselage-polyfills": "^0.14.1", - "@rocket.chat/fuselage-ui-kit": "^0.14.1", - "@rocket.chat/icons": "^0.14.1", - "@rocket.chat/mp3-encoder": "^0.14.1", - "@rocket.chat/ui-kit": "^0.14.1", + "@rocket.chat/css-in-js": "^0.15.1", + "@rocket.chat/fuselage": "^0.15.1", + "@rocket.chat/fuselage-hooks": "^0.15.1", + "@rocket.chat/fuselage-polyfills": "^0.15.1", + "@rocket.chat/fuselage-ui-kit": "^0.15.1", + "@rocket.chat/icons": "^0.15.1", + "@rocket.chat/mp3-encoder": "^0.15.1", + "@rocket.chat/ui-kit": "^0.15.1", "@slack/client": "^4.12.0", "@types/fibers": "^3.1.0", "@types/mkdirp": "^1.0.1", From f265d879c8eaecb2e4261648b1ba9743bc08f273 Mon Sep 17 00:00:00 2001 From: Gabriel Henriques Date: Wed, 23 Sep 2020 14:34:42 -0300 Subject: [PATCH 017/162] Sidebar variations --- client/components/sidebar/Condensed.js | 50 ++++++++++++++ .../components/sidebar/Condensed.stories.js | 65 ++++++++++++++++++ client/components/sidebar/Extended.js | 60 ++++++++++++++++ client/components/sidebar/Extended.stories.js | 68 +++++++++++++++++++ client/components/sidebar/Medium.js | 49 +++++++++++++ client/components/sidebar/Medium.stories.js | 65 ++++++++++++++++++ 6 files changed, 357 insertions(+) create mode 100644 client/components/sidebar/Condensed.js create mode 100644 client/components/sidebar/Condensed.stories.js create mode 100644 client/components/sidebar/Extended.js create mode 100644 client/components/sidebar/Extended.stories.js create mode 100644 client/components/sidebar/Medium.js create mode 100644 client/components/sidebar/Medium.stories.js diff --git a/client/components/sidebar/Condensed.js b/client/components/sidebar/Condensed.js new file mode 100644 index 0000000000000..c8277f7abbacc --- /dev/null +++ b/client/components/sidebar/Condensed.js @@ -0,0 +1,50 @@ +import React, { useState } from 'react'; +import { Sidebar, Menu, Box, Option } from '@rocket.chat/fuselage'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +const Condensed = ({ + titleIcon = , + title = '', + avatar, + actions, + menuOptions, + unread, + ...props +}) => { + const [hovered, setHovered] = useState(false); + + const onMouseEnter = useMutableCallback(() => { + setHovered(true); + }); + + const onMouseLeave = useMutableCallback(() => { + setHovered(false); + }); + + return + + + { avatar } + + + + { titleIcon } + {title} + + + { + { actions } + { menuOptions &&