agx-chat-web 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/agx-chat.js +3 -0
- package/dist/agx-chat.js.LICENSE.txt +303 -0
- package/dist/agx-chat.js.map +1 -0
- package/dist/agx-chat.min.js +3 -0
- package/dist/agx-chat.min.js.LICENSE.txt +303 -0
- package/dist/agx-chat.min.js.map +1 -0
- package/dist/esm/__tests__/app/Messenger/classes/slaCalculations.spec.d.ts +1 -0
- package/dist/esm/__tests__/app/Messenger/classes/slaCalculations.spec.js +46 -0
- package/dist/esm/__tests__/app/Messenger/classes/slaCalculations.spec.js.map +1 -0
- package/dist/esm/app/Messenger/classes/slaCalculations.d.ts +30 -0
- package/dist/esm/app/Messenger/classes/slaCalculations.js +142 -0
- package/dist/esm/app/Messenger/classes/slaCalculations.js.map +1 -0
- package/dist/esm/app/Messenger/components/ChatButton/ChatButton.d.ts +14 -0
- package/dist/esm/app/Messenger/components/ChatButton/ChatButton.js +31 -0
- package/dist/esm/app/Messenger/components/ChatButton/ChatButton.js.map +1 -0
- package/dist/esm/app/Messenger/components/ChatTabs/ChatTabs.d.ts +9 -0
- package/dist/esm/app/Messenger/components/ChatTabs/ChatTabs.js +15 -0
- package/dist/esm/app/Messenger/components/ChatTabs/ChatTabs.js.map +1 -0
- package/dist/esm/app/Messenger/components/ImagesContainer/ImagesContainer.d.ts +3 -0
- package/dist/esm/app/Messenger/components/ImagesContainer/ImagesContainer.js +24 -0
- package/dist/esm/app/Messenger/components/ImagesContainer/ImagesContainer.js.map +1 -0
- package/dist/esm/app/Messenger/components/IncomingMessage/IncomingMessage.d.ts +3 -0
- package/dist/esm/app/Messenger/components/IncomingMessage/IncomingMessage.js +33 -0
- package/dist/esm/app/Messenger/components/IncomingMessage/IncomingMessage.js.map +1 -0
- package/dist/esm/app/Messenger/components/InfiniteScroll/InfiniteScroll.d.ts +11 -0
- package/dist/esm/app/Messenger/components/InfiniteScroll/InfiniteScroll.js +37 -0
- package/dist/esm/app/Messenger/components/InfiniteScroll/InfiniteScroll.js.map +1 -0
- package/dist/esm/app/Messenger/components/InputFile/InputFile.d.ts +8 -0
- package/dist/esm/app/Messenger/components/InputFile/InputFile.js +59 -0
- package/dist/esm/app/Messenger/components/InputFile/InputFile.js.map +1 -0
- package/dist/esm/app/Messenger/components/MessageBallon/MessageBalloon.d.ts +8 -0
- package/dist/esm/app/Messenger/components/MessageBallon/MessageBalloon.js +32 -0
- package/dist/esm/app/Messenger/components/MessageBallon/MessageBalloon.js.map +1 -0
- package/dist/esm/app/Messenger/components/MessengerAvatar/MessengerAvatar.d.ts +3 -0
- package/dist/esm/app/Messenger/components/MessengerAvatar/MessengerAvatar.js +13 -0
- package/dist/esm/app/Messenger/components/MessengerAvatar/MessengerAvatar.js.map +1 -0
- package/dist/esm/app/Messenger/components/MessengerThemeWrapper/MessengerThemeWrapper.d.ts +40 -0
- package/dist/esm/app/Messenger/components/MessengerThemeWrapper/MessengerThemeWrapper.js +39 -0
- package/dist/esm/app/Messenger/components/MessengerThemeWrapper/MessengerThemeWrapper.js.map +1 -0
- package/dist/esm/app/Messenger/components/SearchInput/SearchInput.d.ts +10 -0
- package/dist/esm/app/Messenger/components/SearchInput/SearchInput.js +32 -0
- package/dist/esm/app/Messenger/components/SearchInput/SearchInput.js.map +1 -0
- package/dist/esm/app/Messenger/components/Select/Select.d.ts +13 -0
- package/dist/esm/app/Messenger/components/Select/Select.js +16 -0
- package/dist/esm/app/Messenger/components/Select/Select.js.map +1 -0
- package/dist/esm/app/Messenger/components/SenderMessages/SenderMessages.d.ts +3 -0
- package/dist/esm/app/Messenger/components/SenderMessages/SenderMessages.js +32 -0
- package/dist/esm/app/Messenger/components/SenderMessages/SenderMessages.js.map +1 -0
- package/dist/esm/app/Messenger/components/SystemMessage/SystemMessage.d.ts +3 -0
- package/dist/esm/app/Messenger/components/SystemMessage/SystemMessage.js +14 -0
- package/dist/esm/app/Messenger/components/SystemMessage/SystemMessage.js.map +1 -0
- package/dist/esm/app/Messenger/components/TextArea/TextArea.d.ts +8 -0
- package/dist/esm/app/Messenger/components/TextArea/TextArea.js +14 -0
- package/dist/esm/app/Messenger/components/TextArea/TextArea.js.map +1 -0
- package/dist/esm/app/Messenger/hooks/useConversations.d.ts +11 -0
- package/dist/esm/app/Messenger/hooks/useConversations.js +59 -0
- package/dist/esm/app/Messenger/hooks/useConversations.js.map +1 -0
- package/dist/esm/app/Messenger/hooks/useThemes.d.ts +31 -0
- package/dist/esm/app/Messenger/hooks/useThemes.js +11 -0
- package/dist/esm/app/Messenger/hooks/useThemes.js.map +1 -0
- package/dist/esm/app/Messenger/icons/AttachFileIcon.d.ts +3 -0
- package/dist/esm/app/Messenger/icons/AttachFileIcon.js +10 -0
- package/dist/esm/app/Messenger/icons/AttachFileIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/CloseIcon.d.ts +1 -0
- package/dist/esm/app/Messenger/icons/CloseIcon.js +9 -0
- package/dist/esm/app/Messenger/icons/CloseIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/EmptyIcon.d.ts +1 -0
- package/dist/esm/app/Messenger/icons/EmptyIcon.js +8 -0
- package/dist/esm/app/Messenger/icons/EmptyIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/MessageIcon.d.ts +5 -0
- package/dist/esm/app/Messenger/icons/MessageIcon.js +12 -0
- package/dist/esm/app/Messenger/icons/MessageIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/ReadIcon.d.ts +3 -0
- package/dist/esm/app/Messenger/icons/ReadIcon.js +7 -0
- package/dist/esm/app/Messenger/icons/ReadIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/SearchIcon.d.ts +1 -0
- package/dist/esm/app/Messenger/icons/SearchIcon.js +8 -0
- package/dist/esm/app/Messenger/icons/SearchIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/TimerIcon.d.ts +1 -0
- package/dist/esm/app/Messenger/icons/TimerIcon.js +6 -0
- package/dist/esm/app/Messenger/icons/TimerIcon.js.map +1 -0
- package/dist/esm/app/Messenger/icons/TrashIcon.d.ts +5 -0
- package/dist/esm/app/Messenger/icons/TrashIcon.js +7 -0
- package/dist/esm/app/Messenger/icons/TrashIcon.js.map +1 -0
- package/dist/esm/app/Messenger/views/MessengerList.d.ts +39 -0
- package/dist/esm/app/Messenger/views/MessengerList.js +50 -0
- package/dist/esm/app/Messenger/views/MessengerList.js.map +1 -0
- package/dist/esm/app/Messenger/views/MessengerListItem.d.ts +11 -0
- package/dist/esm/app/Messenger/views/MessengerListItem.js +87 -0
- package/dist/esm/app/Messenger/views/MessengerListItem.js.map +1 -0
- package/dist/esm/app/Messenger/views/MessengerMessages.d.ts +23 -0
- package/dist/esm/app/Messenger/views/MessengerMessages.js +133 -0
- package/dist/esm/app/Messenger/views/MessengerMessages.js.map +1 -0
- package/dist/esm/app/Messenger/views/NewFormChat.d.ts +10 -0
- package/dist/esm/app/Messenger/views/NewFormChat.js +64 -0
- package/dist/esm/app/Messenger/views/NewFormChat.js.map +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/setupTests.d.ts +1 -0
- package/dist/esm/setupTests.js +6 -0
- package/dist/esm/setupTests.js.map +1 -0
- package/dist/esm/types.d.ts +134 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +67 -0
- package/src/__tests__/app/Messenger/classes/slaCalculations.spec.ts +115 -0
- package/src/app/Messenger/classes/slaCalculations.ts +165 -0
- package/src/app/Messenger/components/ChatButton/ChatButton.tsx +63 -0
- package/src/app/Messenger/components/ChatTabs/ChatTabs.less +18 -0
- package/src/app/Messenger/components/ChatTabs/ChatTabs.tsx +32 -0
- package/src/app/Messenger/components/ImagesContainer/ImagesContainer.less +64 -0
- package/src/app/Messenger/components/ImagesContainer/ImagesContainer.tsx +40 -0
- package/src/app/Messenger/components/IncomingMessage/IncomingMessage.tsx +59 -0
- package/src/app/Messenger/components/InfiniteScroll/InfiniteScroll.tsx +52 -0
- package/src/app/Messenger/components/InputFile/InputFile.tsx +106 -0
- package/src/app/Messenger/components/InputFile/inputFile.less +52 -0
- package/src/app/Messenger/components/MessageBallon/MessageBalloon.tsx +88 -0
- package/src/app/Messenger/components/MessengerAvatar/MessengerAvatar.tsx +21 -0
- package/src/app/Messenger/components/MessengerThemeWrapper/MessengerThemeWrapper.tsx +58 -0
- package/src/app/Messenger/components/SearchInput/SearchInput.less +45 -0
- package/src/app/Messenger/components/SearchInput/SearchInput.tsx +68 -0
- package/src/app/Messenger/components/Select/Select.less +22 -0
- package/src/app/Messenger/components/Select/Select.tsx +41 -0
- package/src/app/Messenger/components/SenderMessages/SenderMessages.tsx +52 -0
- package/src/app/Messenger/components/SystemMessage/SystemMessage.tsx +23 -0
- package/src/app/Messenger/components/TextArea/TextArea.tsx +31 -0
- package/src/app/Messenger/components/TextArea/Textarea.less +22 -0
- package/src/app/Messenger/hooks/useConversations.tsx +80 -0
- package/src/app/Messenger/hooks/useThemes.tsx +14 -0
- package/src/app/Messenger/icons/AttachFileIcon.tsx +11 -0
- package/src/app/Messenger/icons/CloseIcon.tsx +11 -0
- package/src/app/Messenger/icons/EmptyIcon.tsx +11 -0
- package/src/app/Messenger/icons/MessageIcon.tsx +18 -0
- package/src/app/Messenger/icons/ReadIcon.tsx +9 -0
- package/src/app/Messenger/icons/SearchIcon.tsx +12 -0
- package/src/app/Messenger/icons/TimerIcon.tsx +10 -0
- package/src/app/Messenger/icons/TrashIcon.tsx +13 -0
- package/src/app/Messenger/views/Messenger.less +610 -0
- package/src/app/Messenger/views/MessengerList.tsx +172 -0
- package/src/app/Messenger/views/MessengerListItem.tsx +136 -0
- package/src/app/Messenger/views/MessengerMessages.tsx +287 -0
- package/src/app/Messenger/views/NewFormChat.tsx +126 -0
- package/src/assets/right-arrow.svg +10 -0
- package/src/index.ts +17 -0
- package/src/react-app-env.d.ts +16 -0
- package/src/setupTests.ts +5 -0
- package/src/styles/abstracts/animations.less +8 -0
- package/src/styles/abstracts/mixins.less +5 -0
- package/src/styles/abstracts/variables.less +25 -0
- package/src/styles/base/base.less +6 -0
- package/src/styles/index.less +6 -0
- package/src/types.ts +166 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import React, { Fragment } from 'react'
|
|
2
|
+
import { IList } from 'types'
|
|
3
|
+
import ChatTabs from '../components/ChatTabs/ChatTabs'
|
|
4
|
+
import ChatButtons from '../components/ChatButton/ChatButton'
|
|
5
|
+
import SearchInput from '../components/SearchInput/SearchInput'
|
|
6
|
+
import MessengerListItem, { activeItem } from './MessengerListItem'
|
|
7
|
+
import useTheme from '../hooks/useThemes'
|
|
8
|
+
import EmptyIcon from '../icons/EmptyIcon'
|
|
9
|
+
import InfiniteScroll from '../components/InfiniteScroll/InfiniteScroll'
|
|
10
|
+
|
|
11
|
+
interface IMessengerList {
|
|
12
|
+
affix?: React.ReactElement
|
|
13
|
+
loading: boolean
|
|
14
|
+
conversations: IList[]
|
|
15
|
+
currentId?: string
|
|
16
|
+
failed?: boolean
|
|
17
|
+
showBadge: boolean
|
|
18
|
+
tab?: 'list' | 'messages'
|
|
19
|
+
className?: string
|
|
20
|
+
onClick: (item: IList) => void
|
|
21
|
+
searchChat: (search: string, key: string) => void
|
|
22
|
+
searchKeys: { label: string, value: string }[]
|
|
23
|
+
messegerView: 'empty' | 'messages' | 'newChat'
|
|
24
|
+
helpDesk: boolean
|
|
25
|
+
handleChangeTickets: (tab: string) => void
|
|
26
|
+
loadingSearch: boolean
|
|
27
|
+
chatListTabs: { value: string, label: string }[]
|
|
28
|
+
chatButtons: {
|
|
29
|
+
label: string
|
|
30
|
+
onClick: () => void
|
|
31
|
+
type: string
|
|
32
|
+
canSee: boolean
|
|
33
|
+
disabled?: boolean
|
|
34
|
+
}[]
|
|
35
|
+
canSeeTimer: boolean
|
|
36
|
+
hasNext: boolean
|
|
37
|
+
onIntersect: () => void
|
|
38
|
+
sliceCount: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ListLoading = () => {
|
|
42
|
+
const {theme} = useTheme()
|
|
43
|
+
return (
|
|
44
|
+
<Fragment>
|
|
45
|
+
{new Array(5).fill('').map((_, idx) => (
|
|
46
|
+
<button
|
|
47
|
+
key={`asideSkeleton${idx}`}
|
|
48
|
+
style={{background: theme.listItemHover}}
|
|
49
|
+
className="messenger__aside-skeleton"
|
|
50
|
+
/>
|
|
51
|
+
))}
|
|
52
|
+
</Fragment>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ShowProtocol = ({ item }: { item: IList }) => {
|
|
57
|
+
const { theme } = useTheme()
|
|
58
|
+
return (
|
|
59
|
+
<span
|
|
60
|
+
className='messenger__aside-protocol'
|
|
61
|
+
style={{
|
|
62
|
+
background: theme.buttonPrimary,
|
|
63
|
+
color: theme.buttonPrimaryText
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
#{item.protocol}
|
|
67
|
+
</span>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function MessengerList({
|
|
73
|
+
affix,
|
|
74
|
+
conversations,
|
|
75
|
+
tab,
|
|
76
|
+
className = '',
|
|
77
|
+
loading,
|
|
78
|
+
failed,
|
|
79
|
+
showBadge,
|
|
80
|
+
currentId,
|
|
81
|
+
onClick,
|
|
82
|
+
searchChat,
|
|
83
|
+
searchKeys,
|
|
84
|
+
messegerView,
|
|
85
|
+
helpDesk,
|
|
86
|
+
handleChangeTickets,
|
|
87
|
+
chatListTabs,
|
|
88
|
+
chatButtons,
|
|
89
|
+
loadingSearch,
|
|
90
|
+
canSeeTimer,
|
|
91
|
+
hasNext,
|
|
92
|
+
onIntersect,
|
|
93
|
+
sliceCount
|
|
94
|
+
}: IMessengerList) {
|
|
95
|
+
const { theme } = useTheme()
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<aside
|
|
99
|
+
className={`messenger__aside ${className} ${tab === 'messages' ? 'mobile-hide' : ''}`}
|
|
100
|
+
style={{
|
|
101
|
+
border: `1px solid ${theme.borderColor}`,
|
|
102
|
+
borderRadius: '20px 0 0 20px',
|
|
103
|
+
background: theme.asideBg
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<div className="messenger__buttons-container">
|
|
107
|
+
<ChatButtons
|
|
108
|
+
chatButtons={chatButtons}
|
|
109
|
+
messegerView={messegerView}
|
|
110
|
+
removeActiveItem={(e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
111
|
+
activeItem(e, 'messenger__aside-list-item', 'messenger__aside-list-item--active')
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
{helpDesk &&
|
|
116
|
+
<SearchInput
|
|
117
|
+
searchKeys={searchKeys}
|
|
118
|
+
searchChat={searchChat}
|
|
119
|
+
loadingSearch={loadingSearch}
|
|
120
|
+
/>
|
|
121
|
+
}
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
{affix && (
|
|
125
|
+
<div className="messenger__aside-header">
|
|
126
|
+
{React.cloneElement(affix)}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
|
|
130
|
+
<ChatTabs
|
|
131
|
+
chatListTabs={chatListTabs}
|
|
132
|
+
handleChangeTickets={handleChangeTickets}
|
|
133
|
+
/>
|
|
134
|
+
|
|
135
|
+
<div className="messenger__aside-list">
|
|
136
|
+
<InfiniteScroll
|
|
137
|
+
root={document.querySelector('.messenger__aside-list')}
|
|
138
|
+
loading={loading}
|
|
139
|
+
loadingCover={<ListLoading />}
|
|
140
|
+
more={hasNext}
|
|
141
|
+
fetch={onIntersect}
|
|
142
|
+
>
|
|
143
|
+
{Array.isArray(conversations) && conversations.length > 0 && conversations.slice(0, sliceCount).map((item) => {
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
key={item?._id}
|
|
147
|
+
className={`messenger__aside-item-container ${showBadge ? 'messenger__active-badge' : ''}`}
|
|
148
|
+
>
|
|
149
|
+
<ShowProtocol item={item} />
|
|
150
|
+
<MessengerListItem
|
|
151
|
+
canSeeTimer={canSeeTimer}
|
|
152
|
+
currentId={currentId}
|
|
153
|
+
item={item}
|
|
154
|
+
onClick={onClick}
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
})}
|
|
159
|
+
</InfiniteScroll>
|
|
160
|
+
|
|
161
|
+
{failed || (conversations?.length === 0 && !loading) && (
|
|
162
|
+
<div className="messenger__aside-empty">
|
|
163
|
+
<EmptyIcon />
|
|
164
|
+
<p>Nenhuma conversa encontrada</p>
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
</aside>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default MessengerList
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, { useState, useMemo, useEffect } from 'react'
|
|
2
|
+
import { IList } from 'types'
|
|
3
|
+
import { SlaDates, timeAsDayjs } from '../classes/slaCalculations'
|
|
4
|
+
import MessengerAvatar from '../components/MessengerAvatar/MessengerAvatar'
|
|
5
|
+
import useTheme from '../hooks/useThemes'
|
|
6
|
+
import TimerIcon from '../icons/TimerIcon'
|
|
7
|
+
|
|
8
|
+
interface IListItem {
|
|
9
|
+
onClick: (item: IList) => void
|
|
10
|
+
item: IList
|
|
11
|
+
currentId?: string
|
|
12
|
+
canSeeTimer: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function activeItem(e: React.MouseEvent<HTMLElement, MouseEvent>, defaultClass: string, activeClass: string,) {
|
|
16
|
+
const click = document.querySelectorAll(`.${defaultClass}`)
|
|
17
|
+
click.forEach((item) => {
|
|
18
|
+
item.classList.remove(activeClass)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
e.currentTarget.className += ` ${activeClass} `
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function MessengerListItem ({ item, onClick, canSeeTimer, currentId }: IListItem) {
|
|
25
|
+
const [unreadMessages, setUnreadMessages] = useState<typeof item.totalUnreadMessages>(item.totalUnreadMessages)
|
|
26
|
+
const closedChat = useMemo(() => item.status.current.state === 'final' ? 'messenger__aside-list-item--closed' : '', [item])
|
|
27
|
+
const [background, setBackground] = useState<string | undefined>('')
|
|
28
|
+
const [counter, setCounter] = useState<number | null>(null)
|
|
29
|
+
|
|
30
|
+
const { theme } = useTheme()
|
|
31
|
+
const isInprogress = item.status?.current?.systemicValue === 'inprogress'
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
let interval: NodeJS.Timer
|
|
35
|
+
if (isInprogress && canSeeTimer) {
|
|
36
|
+
const sla = new SlaDates(timeAsDayjs(item.currentTime).diff(item.createdAt, 'second'))
|
|
37
|
+
const diference = sla.calculateWorkingTime(
|
|
38
|
+
timeAsDayjs(item.createdAt),
|
|
39
|
+
timeAsDayjs(), { start: 9, end: 18 }
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
setCounter(diference)
|
|
43
|
+
if (sla.canUpdateClock(timeAsDayjs())) {
|
|
44
|
+
interval = setInterval(() => {
|
|
45
|
+
setCounter(prev => prev !== null ? prev + 1 : null)
|
|
46
|
+
}, 1000)
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
setCounter(item.SLATimePassed ?? null)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
clearInterval(interval)
|
|
54
|
+
}
|
|
55
|
+
}, [])
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setUnreadMessages(item.totalUnreadMessages)
|
|
59
|
+
}, [item])
|
|
60
|
+
|
|
61
|
+
function handleClick(e: React.MouseEvent<HTMLElement, MouseEvent>, item: IList) {
|
|
62
|
+
activeItem(e, 'messenger__aside-list-item', 'messenger__aside-list-item--active')
|
|
63
|
+
onClick(item)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function formatUnreadMessages(unread: number) {
|
|
67
|
+
if (!unread) return 0
|
|
68
|
+
if (unread >= 99) return 99
|
|
69
|
+
return unread
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const onMouseOver = () => {
|
|
73
|
+
setBackground(theme?.listItemHover)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const onMouseLeave = () => {
|
|
77
|
+
setBackground('')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
const ShowTimer = () => {
|
|
82
|
+
if (counter === null) return null
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<span
|
|
86
|
+
className='messenger__aside-timer'
|
|
87
|
+
style={{
|
|
88
|
+
background: SlaDates.getColorsByTime(counter),
|
|
89
|
+
color: 'white'
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
<TimerIcon />
|
|
93
|
+
|
|
94
|
+
<b>
|
|
95
|
+
{SlaDates.secondsInHours(counter)}
|
|
96
|
+
</b>
|
|
97
|
+
</span>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<>
|
|
103
|
+
<div
|
|
104
|
+
className={`messenger__aside-list-item ${closedChat}`}
|
|
105
|
+
onClick={(e) => handleClick(e, item)}
|
|
106
|
+
onMouseOver={onMouseOver}
|
|
107
|
+
onMouseLeave={onMouseLeave}
|
|
108
|
+
style={{
|
|
109
|
+
color: theme?.asideFontColor,
|
|
110
|
+
backgroundColor: currentId === item._id ? theme?.active : background,
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<MessengerAvatar image={item?.creator?.image}>
|
|
114
|
+
{(item?.creator && item?.creator.username) ? item?.creator.username.slice(0, 1) : ''}
|
|
115
|
+
</MessengerAvatar>
|
|
116
|
+
|
|
117
|
+
<div className="messenger__aside-list-item--info">
|
|
118
|
+
<p className="messenger__aside-list-item--title">{item?.creator?.username}</p>
|
|
119
|
+
{item?.reason && (<p className="messenger__aside-list-item--description">{item.reason}</p>)}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div className="messenger__aside-list-item--additional">
|
|
123
|
+
{unreadMessages > 0 && (
|
|
124
|
+
<div className="messenger__aside-list-item--unread">
|
|
125
|
+
<span>{formatUnreadMessages(unreadMessages)}</span>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
{canSeeTimer && <ShowTimer />}
|
|
131
|
+
</div>
|
|
132
|
+
</>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default MessengerListItem
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import React, { Fragment, MutableRefObject, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import ImagesContainer from '../components/ImagesContainer/ImagesContainer'
|
|
3
|
+
import MessageBalloon from '../components/MessageBallon/MessageBalloon'
|
|
4
|
+
import AttachFileIcon from '../icons/AttachFileIcon'
|
|
5
|
+
import MessageIcon from '../icons/MessageIcon'
|
|
6
|
+
import rightArrow from '../../../assets/right-arrow.svg'
|
|
7
|
+
import { ICommonProps, IList, IMessages, IOption } from 'types'
|
|
8
|
+
import useTheme from '../hooks/useThemes'
|
|
9
|
+
|
|
10
|
+
import NewChatForm from './NewFormChat'
|
|
11
|
+
import EmptyIcon from '../icons/EmptyIcon'
|
|
12
|
+
|
|
13
|
+
interface IMessengerMessages extends ICommonProps {
|
|
14
|
+
header: React.ReactElement
|
|
15
|
+
current?: IList
|
|
16
|
+
creatorId?: string
|
|
17
|
+
loading: boolean
|
|
18
|
+
allowImages: boolean
|
|
19
|
+
tab?: 'list' | 'messages'
|
|
20
|
+
messegerView: 'empty' | 'messages' | 'newChat',
|
|
21
|
+
className?: string
|
|
22
|
+
messages: IMessages[]
|
|
23
|
+
onSubmit: (value: FormData, callback: () => void) => void
|
|
24
|
+
cancelNewChat: () => void
|
|
25
|
+
formatDate: (date: string | Date) => string | undefined
|
|
26
|
+
onScroll: (e: React.UIEvent<HTMLDivElement, UIEvent>) => void
|
|
27
|
+
reasons: Array<IOption>
|
|
28
|
+
products: Array<IOption>
|
|
29
|
+
submitNewChat: (values: FormData) => void,
|
|
30
|
+
newChatLoading: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface IMessengerForm {
|
|
34
|
+
children: React.ReactElement,
|
|
35
|
+
messegerView: 'empty' | 'messages' | 'newChat',
|
|
36
|
+
reasons: Array<IOption>
|
|
37
|
+
products: Array<IOption>
|
|
38
|
+
submitNewChat: (values: FormData) => void,
|
|
39
|
+
cancelNewChat: () => void,
|
|
40
|
+
newChatLoading: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function MessengerEmpty({ children, messegerView }: { children: React.ReactElement, messegerView: 'empty' | 'messages' | 'newChat' }) {
|
|
44
|
+
const { theme } = useTheme()
|
|
45
|
+
|
|
46
|
+
if (messegerView === 'empty') {
|
|
47
|
+
return (
|
|
48
|
+
<div className="messenger__messages-container--empty" style={{ backgroundColor: theme?.messengerNotSelectedBg }}>
|
|
49
|
+
<MessageIcon width="72" height="72" />
|
|
50
|
+
<p>Selecione uma conversa para visualizar as mensagens</p>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return children
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function MessengerForm({
|
|
59
|
+
children,
|
|
60
|
+
messegerView,
|
|
61
|
+
submitNewChat,
|
|
62
|
+
cancelNewChat,
|
|
63
|
+
reasons,
|
|
64
|
+
products,
|
|
65
|
+
newChatLoading
|
|
66
|
+
}: IMessengerForm) {
|
|
67
|
+
if (messegerView === 'newChat') {
|
|
68
|
+
return (
|
|
69
|
+
<NewChatForm reasons={reasons} products={products} submitNewChat={submitNewChat} cancelNewChat={cancelNewChat} loading={newChatLoading}/>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
return children
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function Loading({ loading }: { loading: boolean }) {
|
|
76
|
+
if (loading) {
|
|
77
|
+
return (
|
|
78
|
+
(
|
|
79
|
+
<div className="messenger__messages-loading">
|
|
80
|
+
<div className="messenger__messages-loading--loader" />
|
|
81
|
+
</div>
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return <></>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function Messages({ messages, creatorId, formatDate }: { messages: IMessages[], creatorId?: string, formatDate: (date: string | Date) => string | undefined }) {
|
|
90
|
+
if (messages && Array.isArray(messages)) {
|
|
91
|
+
return <>
|
|
92
|
+
{messages.map((item, idx) => {
|
|
93
|
+
return (
|
|
94
|
+
<Fragment key={`${item.ticketId}_${item.senderId}_${idx}`}>
|
|
95
|
+
<MessageBalloon
|
|
96
|
+
id={`message_${idx}`}
|
|
97
|
+
formatDate={formatDate}
|
|
98
|
+
creatorId={creatorId}
|
|
99
|
+
item={item}
|
|
100
|
+
/>
|
|
101
|
+
</Fragment>
|
|
102
|
+
)
|
|
103
|
+
})}
|
|
104
|
+
</>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return <></>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function NoMessages({ messages, loading }: { messages: IMessages[], loading: boolean }) {
|
|
111
|
+
const { theme } = useTheme()
|
|
112
|
+
if (!messages || messages.length === 0 && !loading) {
|
|
113
|
+
return <div className="messenger__message-empty"
|
|
114
|
+
style={{color: theme.emptyMessagesFontColor}}
|
|
115
|
+
>
|
|
116
|
+
<EmptyIcon />
|
|
117
|
+
<p>Nenhuma mensagem encontrada</p>
|
|
118
|
+
</div>
|
|
119
|
+
}
|
|
120
|
+
return <></>
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function AllowImages({ allowImages, attachFile, handleFile, closedChat }: { allowImages: boolean, attachFile: MutableRefObject<HTMLInputElement | null>, handleFile: (e: React.ChangeEvent<HTMLInputElement>) => void, closedChat: boolean }) {
|
|
124
|
+
if (allowImages) {
|
|
125
|
+
return <Fragment>
|
|
126
|
+
<input
|
|
127
|
+
type="file"
|
|
128
|
+
size={1}
|
|
129
|
+
className="messenger__messages-send--file"
|
|
130
|
+
ref={attachFile}
|
|
131
|
+
onChange={handleFile}
|
|
132
|
+
accept="image/*"
|
|
133
|
+
/>
|
|
134
|
+
|
|
135
|
+
<AttachFileIcon
|
|
136
|
+
className={`messenger__messages-send__attach ${closedChat ? 'messenger__messages-send__attach--disabled' : ''}`}
|
|
137
|
+
onClick={() => !closedChat ? attachFile.current?.click() : null}
|
|
138
|
+
/>
|
|
139
|
+
</Fragment>
|
|
140
|
+
}
|
|
141
|
+
return <></>
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function MessengerMessages({
|
|
145
|
+
header,
|
|
146
|
+
tab,
|
|
147
|
+
className = '',
|
|
148
|
+
current,
|
|
149
|
+
messages,
|
|
150
|
+
allowImages,
|
|
151
|
+
creatorId,
|
|
152
|
+
loading,
|
|
153
|
+
onSubmit,
|
|
154
|
+
submitNewChat,
|
|
155
|
+
cancelNewChat,
|
|
156
|
+
reasons,
|
|
157
|
+
products,
|
|
158
|
+
messegerView,
|
|
159
|
+
formatDate,
|
|
160
|
+
onScroll,
|
|
161
|
+
newChatLoading
|
|
162
|
+
}: IMessengerMessages) {
|
|
163
|
+
|
|
164
|
+
const [file, setFile] = useState<File | null>()
|
|
165
|
+
const [sending, setSending] = useState<boolean>(false)
|
|
166
|
+
|
|
167
|
+
const inputRef: MutableRefObject<HTMLTextAreaElement | null> = useRef(null)
|
|
168
|
+
const attachFile: MutableRefObject<HTMLInputElement | null> = useRef(null)
|
|
169
|
+
const closedChat = useMemo(() => current?.status.current.state === 'final', [current])
|
|
170
|
+
const { theme } = useTheme()
|
|
171
|
+
|
|
172
|
+
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
|
|
173
|
+
e?.preventDefault()
|
|
174
|
+
if (sending) return
|
|
175
|
+
setSending(true)
|
|
176
|
+
|
|
177
|
+
const data = new FormData()
|
|
178
|
+
if (file) data.append('file', file)
|
|
179
|
+
if (inputRef.current?.value) data.append('message', inputRef.current?.value)
|
|
180
|
+
|
|
181
|
+
onSubmit(data, () => {
|
|
182
|
+
if (inputRef.current) inputRef.current.value = ''
|
|
183
|
+
if (file) setFile(null)
|
|
184
|
+
setSending(false)
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function handleFile(e: React.ChangeEvent<HTMLInputElement>) {
|
|
189
|
+
if (e.target.files) {
|
|
190
|
+
const file = e.target.files[0]
|
|
191
|
+
if (file?.type?.includes('image/')) setFile(e.target.files[0])
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<div className={`${className} ${tab === 'list' ? 'mobile-hide' : ''} messenger__messages`}>
|
|
197
|
+
<MessengerEmpty messegerView={messegerView}>
|
|
198
|
+
<Fragment>
|
|
199
|
+
<MessengerForm
|
|
200
|
+
newChatLoading={newChatLoading}
|
|
201
|
+
reasons={reasons}
|
|
202
|
+
products={products}
|
|
203
|
+
submitNewChat={submitNewChat}
|
|
204
|
+
cancelNewChat={cancelNewChat}
|
|
205
|
+
messegerView={messegerView}
|
|
206
|
+
>
|
|
207
|
+
<>
|
|
208
|
+
{file ? (<ImagesContainer file={file} onClose={() => setFile(null)} />) : (
|
|
209
|
+
<Fragment>
|
|
210
|
+
<div
|
|
211
|
+
className="messenger__messages-header"
|
|
212
|
+
style={
|
|
213
|
+
{ backgroundColor: theme?.headerAndSenderBg,
|
|
214
|
+
border: `1px solid ${theme.borderColor}`
|
|
215
|
+
}}
|
|
216
|
+
>
|
|
217
|
+
{React.cloneElement(header)}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div
|
|
221
|
+
className="messenger__messages-container" onScroll={onScroll}
|
|
222
|
+
style={{
|
|
223
|
+
padding: '1rem',
|
|
224
|
+
backgroundColor: theme?.messengerMessagesBg ,
|
|
225
|
+
border: `1px solid ${theme.borderColor}`
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
<Loading loading={loading} />
|
|
229
|
+
<Messages formatDate={formatDate} messages={messages} creatorId={creatorId} />
|
|
230
|
+
<NoMessages messages={messages} loading={loading} />
|
|
231
|
+
</div>
|
|
232
|
+
</Fragment>
|
|
233
|
+
)}
|
|
234
|
+
<div
|
|
235
|
+
className="messenger__messages-send"
|
|
236
|
+
style={{
|
|
237
|
+
backgroundColor: theme?.headerAndSenderBg,
|
|
238
|
+
border: `1px solid ${theme.borderColor}`,
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
<form
|
|
242
|
+
onSubmit={handleSubmit}
|
|
243
|
+
className="messenger__messages-send--form"
|
|
244
|
+
>
|
|
245
|
+
<textarea
|
|
246
|
+
cols={30}
|
|
247
|
+
rows={10}
|
|
248
|
+
placeholder={closedChat ? 'Conversa finalizada' : 'Digite uma mensagem'}
|
|
249
|
+
ref={inputRef}
|
|
250
|
+
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) handleSubmit() }}
|
|
251
|
+
className={`messenger__messages-send__input ${closedChat || sending ? 'messenger__messages-send__input--disabled' : ''} `}
|
|
252
|
+
disabled={closedChat || sending}
|
|
253
|
+
style={{
|
|
254
|
+
backgroundColor: theme?.inputBg,
|
|
255
|
+
border: theme?.chatInputBorder ? `1px solid ${theme?.chatInputBorder}` : ''
|
|
256
|
+
}}
|
|
257
|
+
/>
|
|
258
|
+
|
|
259
|
+
<AllowImages allowImages={allowImages} handleFile={handleFile} attachFile={attachFile} closedChat={closedChat} />
|
|
260
|
+
|
|
261
|
+
<button
|
|
262
|
+
type="submit"
|
|
263
|
+
className={`messenger__messages-send__button ${closedChat || sending ? 'messenger__messages-send__button--disabled' : ''}`}
|
|
264
|
+
disabled={closedChat || sending}
|
|
265
|
+
style={{
|
|
266
|
+
backgroundColor: theme?.buttonPrimary
|
|
267
|
+
}}
|
|
268
|
+
>
|
|
269
|
+
<img
|
|
270
|
+
alt='Icone para a direita'
|
|
271
|
+
height={25}
|
|
272
|
+
width={25}
|
|
273
|
+
className="ml-1"
|
|
274
|
+
src={rightArrow}
|
|
275
|
+
/>
|
|
276
|
+
</button>
|
|
277
|
+
</form>
|
|
278
|
+
</div>
|
|
279
|
+
</>
|
|
280
|
+
</MessengerForm>
|
|
281
|
+
</Fragment>
|
|
282
|
+
</MessengerEmpty>
|
|
283
|
+
</div>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export default MessengerMessages
|