@defra/flood-webchat 0.0.1-beta.2 → 0.0.1-beta.20
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 +83 -1
- package/dist/client.js +1871 -212
- package/dist/client.js.map +1 -1
- package/dist/server.js +181 -240
- package/dist/server.js.map +1 -1
- package/main.scss +4 -3
- package/package.json +6 -2
- package/src/client/components/availability/availability.jsx +44 -46
- package/src/client/components/availability/availability.scss +105 -5
- package/src/client/components/chat/chat.jsx +169 -0
- package/src/client/components/chat/chat.scss +143 -0
- package/src/client/components/errorSummary/error-summary.jsx +34 -0
- package/src/client/components/message/message.jsx +20 -0
- package/src/client/components/panel/panel-footer.jsx +9 -0
- package/src/client/components/panel/panel-header.jsx +43 -0
- package/src/client/components/panel/panel.jsx +133 -0
- package/src/client/components/panel/panel.scss +72 -0
- package/src/client/components/screens/end-chat.jsx +35 -0
- package/src/client/components/screens/end-feedback.jsx +18 -0
- package/src/client/components/screens/feedback.jsx +149 -0
- package/src/client/components/screens/pre-chat.jsx +65 -0
- package/src/client/components/screens/request-chat.jsx +157 -0
- package/src/client/components/screens/settings.jsx +67 -0
- package/src/client/components/screens/unavailable.jsx +23 -0
- package/src/client/hooks/useFocusedElements.js +71 -0
- package/src/client/hooks/useTextareaAutosize.js +13 -0
- package/src/client/index.jsx +15 -1
- package/src/client/lib/check-availability.js +1 -0
- package/src/client/lib/message-notification.js +36 -0
- package/src/client/lib/transform-messages.js +35 -0
- package/src/client/store/AppProvider.jsx +140 -0
- package/src/client/store/actions-map.js +119 -0
- package/src/client/store/constants.js +3 -0
- package/src/client/store/reducer.js +28 -0
- package/src/client/store/useApp.js +5 -0
- package/src/client/store/useChatSdk.js +37 -0
- package/src/server/index.js +15 -6
- package/src/server/lib/client.js +31 -35
- package/src/server/lib/utils.js +18 -9
- package/src/client/lib/external-stores.js +0 -4
- package/src/client/lib/external-sync-store.js +0 -42
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
@keyframes blink {
|
|
2
|
+
50% {
|
|
3
|
+
fill: transparent
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.wc-status {
|
|
8
|
+
display: flex;
|
|
9
|
+
border-bottom: 1px solid $govuk-border-colour;
|
|
10
|
+
padding: govuk-spacing(2);
|
|
11
|
+
|
|
12
|
+
&__availability {
|
|
13
|
+
margin-bottom: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&__link {
|
|
17
|
+
margin-left: auto;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.defra-context-footer {
|
|
22
|
+
border-top:2px solid govuk-colour('blue');
|
|
23
|
+
margin-top: 30px;
|
|
24
|
+
padding-top: 10px;
|
|
25
|
+
@include mq ($from: tablet) {
|
|
26
|
+
margin-top: 40px;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.wc-chat {
|
|
31
|
+
&__messages {
|
|
32
|
+
overflow: auto;
|
|
33
|
+
list-style: none;
|
|
34
|
+
padding: 0;
|
|
35
|
+
margin: govuk-spacing(2) 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&__message {
|
|
39
|
+
margin-bottom: govuk-spacing(1);
|
|
40
|
+
|
|
41
|
+
&.inbound {
|
|
42
|
+
text-align: right;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&__from {
|
|
47
|
+
color: govuk-colour('dark-grey');
|
|
48
|
+
margin-bottom: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&__text {
|
|
52
|
+
padding: govuk-spacing(2);
|
|
53
|
+
margin-bottom: 0;
|
|
54
|
+
display: inline-block;
|
|
55
|
+
|
|
56
|
+
&.inbound {
|
|
57
|
+
color: govuk-colour('white');
|
|
58
|
+
background-color: govuk-colour('blue');
|
|
59
|
+
border-radius: 8px 0 8px 8px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&.outbound {
|
|
63
|
+
background-color: govuk-colour('light-grey');
|
|
64
|
+
border-radius: 0 8px 8px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
svg {
|
|
68
|
+
display: block;
|
|
69
|
+
color: govuk-colour('dark-grey');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
svg circle {
|
|
73
|
+
animation: 1s blink infinite;
|
|
74
|
+
fill: currentcolor;
|
|
75
|
+
|
|
76
|
+
&:nth-child(2) {
|
|
77
|
+
animation-delay: 250ms
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&:nth-child(3) {
|
|
81
|
+
animation-delay: 500ms
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.wc-form {
|
|
88
|
+
position: relative;
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: row;
|
|
91
|
+
align-items: center;
|
|
92
|
+
width: 100%;
|
|
93
|
+
|
|
94
|
+
&__label {
|
|
95
|
+
position: absolute;
|
|
96
|
+
top: 9px;
|
|
97
|
+
left: 5px;
|
|
98
|
+
z-index: 3;
|
|
99
|
+
margin-bottom: 0;
|
|
100
|
+
color: govuk-colour('dark-grey');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&__textarea {
|
|
104
|
+
position: relative;
|
|
105
|
+
width: 100%;
|
|
106
|
+
margin-bottom: 0;
|
|
107
|
+
min-height: auto;
|
|
108
|
+
max-height: 120px;
|
|
109
|
+
resize: none;
|
|
110
|
+
border: none;
|
|
111
|
+
overflow-x: hidden;
|
|
112
|
+
overscroll-behavior: contain;
|
|
113
|
+
z-index: 4;
|
|
114
|
+
background-color: transparent;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&__button {
|
|
118
|
+
margin: auto 0 0 govuk-spacing(3);
|
|
119
|
+
width: auto;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.wc-footer {
|
|
124
|
+
&__settings {
|
|
125
|
+
@include govuk-responsive-padding(2);
|
|
126
|
+
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-direction: row;
|
|
129
|
+
border-top: 1px solid govuk-colour('black');
|
|
130
|
+
|
|
131
|
+
&-link {
|
|
132
|
+
&:first-child {
|
|
133
|
+
margin-right: govuk-spacing(3);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
&__input {
|
|
139
|
+
@include govuk-responsive-padding(2);
|
|
140
|
+
|
|
141
|
+
border-top: 1px solid govuk-colour('black');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
export const ErrorSummary = ({ errors }) => {
|
|
4
|
+
const errs = Object.keys(errors)
|
|
5
|
+
|
|
6
|
+
const goToInput = e => {
|
|
7
|
+
e.preventDefault()
|
|
8
|
+
const key = e.target.getAttribute('data-key')
|
|
9
|
+
document.querySelector(`#wc-${key}`).focus()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (errs.length === 0) {
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className='govuk-error-summary govuk-!-static-margin-bottom-7' data-module='govuk-error-summary' tabIndex='-1'>
|
|
18
|
+
<div role='alert'>
|
|
19
|
+
<h2 id='wc-error' className='govuk-error-summary__title'>
|
|
20
|
+
There is a problem
|
|
21
|
+
</h2>
|
|
22
|
+
<div className='govuk-error-summary__body'>
|
|
23
|
+
<ul className='govuk-list govuk-error-summary__list'>
|
|
24
|
+
{errs.map(key => (
|
|
25
|
+
<li key={key}>
|
|
26
|
+
<a href='#' data-key={key} onClick={goToInput}>{errors[key]}</a>
|
|
27
|
+
</li>
|
|
28
|
+
))}
|
|
29
|
+
</ul>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { classnames } from '../../lib/classnames'
|
|
3
|
+
|
|
4
|
+
export function Message ({ message, previousMessage }) {
|
|
5
|
+
const outbound = message.direction === 'outbound'
|
|
6
|
+
|
|
7
|
+
const messageOwnerSameAsPrevious = message?.direction === previousMessage?.direction
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<li className={classnames('wc-chat__message govuk-body', outbound ? 'outbound' : 'inbound')}>
|
|
11
|
+
<div className='wc-chat__from govuk-!-font-size-14'>
|
|
12
|
+
{messageOwnerSameAsPrevious ? null : <span>{outbound ? message.assignee : 'You'}</span>}
|
|
13
|
+
<span className='govuk-visually-hidden'>:</span>
|
|
14
|
+
</div>
|
|
15
|
+
<p className={classnames('wc-chat__text', 'govuk-body-s', outbound ? 'outbound' : 'inbound')}>
|
|
16
|
+
{message.text}
|
|
17
|
+
</p>
|
|
18
|
+
</li>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { useApp } from '../../store/useApp.js'
|
|
3
|
+
|
|
4
|
+
export function PanelHeader () {
|
|
5
|
+
const { thread, setChatVisibility, setUnseenCount } = useApp()
|
|
6
|
+
|
|
7
|
+
const onClose = e => {
|
|
8
|
+
e.preventDefault()
|
|
9
|
+
setChatVisibility(false)
|
|
10
|
+
if (thread) {
|
|
11
|
+
thread.lastMessageSeen()
|
|
12
|
+
setUnseenCount(0)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let ButtonComponent = (
|
|
17
|
+
<button className='wc-header__close' aria-label='Close the webchat' onClick={onClose}>
|
|
18
|
+
<svg aria-hidden='true' focusable='false' width='20' height='20' viewBox='0 0 20 20'>
|
|
19
|
+
<path d='M10,8.6L15.6,3L17,4.4L11.4,10L17,15.6L15.6,17L10,11.4L4.4,17L3,15.6L8.6,10L3,4.4L4.4,3L10,8.6Z' fill='currentColor' />
|
|
20
|
+
</svg>
|
|
21
|
+
</button>
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if (thread) {
|
|
25
|
+
ButtonComponent = (
|
|
26
|
+
<button className='wc-header__hide' aria-label='Minimise the webchat' onClick={onClose}>
|
|
27
|
+
<svg aria-hidden='true' focusable='false' width='20' height='20' viewBox='0 0 20 20'>
|
|
28
|
+
<path d='M10 14.4l-7-7L4.4 6l5.6 5.6L15.6 6 17 7.4l-7 7z' fill='currentColor' />
|
|
29
|
+
</svg>
|
|
30
|
+
</button>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className='wc-header'>
|
|
36
|
+
<h2 id='wc-header-title' className='wc-header__title govuk-heading-s'>
|
|
37
|
+
Floodline Webchat
|
|
38
|
+
</h2>
|
|
39
|
+
|
|
40
|
+
{ButtonComponent}
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React, { useEffect, useState, useCallback } from 'react'
|
|
2
|
+
import { createPortal } from 'react-dom'
|
|
3
|
+
|
|
4
|
+
import { PreChat } from '../screens/pre-chat.jsx'
|
|
5
|
+
import { RequestChat } from '../screens/request-chat.jsx'
|
|
6
|
+
import { Chat } from '../chat/chat.jsx'
|
|
7
|
+
import { Unavailable } from '../screens/unavailable.jsx'
|
|
8
|
+
import { EndChat } from '../screens/end-chat.jsx'
|
|
9
|
+
import { Settings } from '../screens/settings.jsx'
|
|
10
|
+
import { Feedback } from '../screens/feedback.jsx'
|
|
11
|
+
|
|
12
|
+
import { useApp } from '../../store/useApp.js'
|
|
13
|
+
import { useChatSdk } from '../../store/useChatSdk.js'
|
|
14
|
+
import { useFocusedElements } from '../../hooks/useFocusedElements.js'
|
|
15
|
+
import { EndFeedback } from '../screens/end-feedback.jsx'
|
|
16
|
+
|
|
17
|
+
export function Panel () {
|
|
18
|
+
const { sdk, availability, thread, threadId, setThread, setThreadId, setChatVisibility, setMessages, setUnseenCount } = useApp()
|
|
19
|
+
const { fetchThread, fetchMessages } = useChatSdk(sdk)
|
|
20
|
+
|
|
21
|
+
const [screen, setScreen] = useState(threadId ? 2 : 0)
|
|
22
|
+
|
|
23
|
+
useFocusedElements(screen)
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Initializes the eventListener for pressing the escape key
|
|
27
|
+
*/
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const escapeKeyEvent = document.addEventListener('keydown', onEscapeKey)
|
|
30
|
+
|
|
31
|
+
return () => {
|
|
32
|
+
document.removeEventListener('keydown', escapeKeyEvent)
|
|
33
|
+
}
|
|
34
|
+
}, [])
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Recovers the thread if there is a threadId but no thread loaded in to state
|
|
38
|
+
*/
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const recover = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const fetchedThread = await fetchThread(threadId)
|
|
43
|
+
setThread(fetchedThread)
|
|
44
|
+
|
|
45
|
+
const fetchedMessages = await fetchMessages(fetchedThread, threadId)
|
|
46
|
+
setMessages(fetchedMessages)
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.log('[Chat Error] fetchThread', err)
|
|
49
|
+
|
|
50
|
+
setThreadId()
|
|
51
|
+
setThread()
|
|
52
|
+
setScreen(0)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (threadId) {
|
|
57
|
+
if (thread) {
|
|
58
|
+
thread.lastMessageSeen()
|
|
59
|
+
setUnseenCount(0)
|
|
60
|
+
setScreen(2)
|
|
61
|
+
} else {
|
|
62
|
+
recover()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}, [thread, threadId])
|
|
66
|
+
|
|
67
|
+
const onEscapeKey = useCallback(e => {
|
|
68
|
+
if (e.key === 'Escape' || e.key === 'Esc') {
|
|
69
|
+
if (thread) {
|
|
70
|
+
thread.lastMessageSeen()
|
|
71
|
+
setUnseenCount(0)
|
|
72
|
+
}
|
|
73
|
+
setChatVisibility(false)
|
|
74
|
+
}
|
|
75
|
+
}, [])
|
|
76
|
+
|
|
77
|
+
const handleScreenChange = newScreen => e => {
|
|
78
|
+
e.preventDefault()
|
|
79
|
+
setScreen(newScreen)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const goToPreChatScreen = handleScreenChange(0)
|
|
83
|
+
const goToRequestChatScreen = handleScreenChange(1)
|
|
84
|
+
const goToChatScreen = handleScreenChange(2)
|
|
85
|
+
const goToEndChatScreen = handleScreenChange(3)
|
|
86
|
+
const goToFeedbackScreen = handleScreenChange(4)
|
|
87
|
+
const goToSettingsScreen = handleScreenChange(5)
|
|
88
|
+
const goToEndFeedbackScreen = handleScreenChange(6)
|
|
89
|
+
|
|
90
|
+
let ScreenComponent
|
|
91
|
+
|
|
92
|
+
switch (screen) {
|
|
93
|
+
case 0:
|
|
94
|
+
ScreenComponent = <PreChat onContinue={goToRequestChatScreen} />
|
|
95
|
+
break
|
|
96
|
+
case 1:
|
|
97
|
+
ScreenComponent = <RequestChat onPreChatScreen={goToPreChatScreen} />
|
|
98
|
+
break
|
|
99
|
+
case 2:
|
|
100
|
+
ScreenComponent = <Chat onSettingsScreen={goToSettingsScreen} onEndChatScreen={goToEndChatScreen} />
|
|
101
|
+
break
|
|
102
|
+
case 3:
|
|
103
|
+
ScreenComponent = <EndChat onChatScreen={goToChatScreen} onEndChatConfirm={goToFeedbackScreen} />
|
|
104
|
+
break
|
|
105
|
+
case 4:
|
|
106
|
+
ScreenComponent = <Feedback onCancel={() => setChatVisibility(false)} onConfirmSubmit={goToEndFeedbackScreen} />
|
|
107
|
+
break
|
|
108
|
+
case 5:
|
|
109
|
+
ScreenComponent = <Settings onCancel={goToChatScreen} />
|
|
110
|
+
break
|
|
111
|
+
case 6:
|
|
112
|
+
ScreenComponent = <EndFeedback onClose={() => setChatVisibility(false)} />
|
|
113
|
+
break
|
|
114
|
+
default:
|
|
115
|
+
ScreenComponent = <PreChat onContinue={goToRequestChatScreen} />
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (availability === 'UNAVAILABLE') {
|
|
119
|
+
ScreenComponent = <Unavailable />
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const Component = (
|
|
123
|
+
<div id='wc-panel' role='dialog' className='wc-panel' tabIndex='-1' aria-modal='true' aria-labelledby='wc-header-title'>
|
|
124
|
+
{ScreenComponent}
|
|
125
|
+
</div>
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<>
|
|
130
|
+
{createPortal(Component, document.body)}
|
|
131
|
+
</>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.wc-panel {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
position: fixed;
|
|
5
|
+
bottom: 0;
|
|
6
|
+
right: 0;
|
|
7
|
+
top: 0;
|
|
8
|
+
left: 0;
|
|
9
|
+
width: 100%;
|
|
10
|
+
height: 100%;
|
|
11
|
+
background-color: govuk-colour('white');
|
|
12
|
+
border: 1px solid govuk-colour('black');
|
|
13
|
+
z-index: 999;
|
|
14
|
+
outline: none;
|
|
15
|
+
|
|
16
|
+
@include mq ($from: 'tablet') {
|
|
17
|
+
bottom: 0;
|
|
18
|
+
right: 0;
|
|
19
|
+
top: auto;
|
|
20
|
+
left: auto;
|
|
21
|
+
width: 400px;
|
|
22
|
+
height: 540px;
|
|
23
|
+
margin-bottom: 10px;
|
|
24
|
+
margin-right: 10px;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.wc-header {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: row;
|
|
31
|
+
align-items: center;
|
|
32
|
+
background-color: govuk-colour('black');
|
|
33
|
+
|
|
34
|
+
&__link {
|
|
35
|
+
margin: 0 govuk-spacing(2);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&__title {
|
|
39
|
+
padding: 0 0 0 govuk-spacing(2);
|
|
40
|
+
color: govuk-colour('white');
|
|
41
|
+
margin-bottom: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&__close,
|
|
45
|
+
&__hide {
|
|
46
|
+
display: flex;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
color: govuk-colour('white');
|
|
49
|
+
background: none;
|
|
50
|
+
border: 0;
|
|
51
|
+
padding: govuk-spacing(2);
|
|
52
|
+
margin: 0 0 0 auto;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
|
|
55
|
+
&:focus {
|
|
56
|
+
@include govuk-focused-text;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.wc-body {
|
|
62
|
+
@include govuk-responsive-padding(3);
|
|
63
|
+
|
|
64
|
+
flex: 1;
|
|
65
|
+
position: relative;
|
|
66
|
+
overflow-y: auto;
|
|
67
|
+
overscroll-behavior: contain;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.wc-back-link {
|
|
71
|
+
margin: govuk-spacing(1) 0 govuk-spacing(6) 0;
|
|
72
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
|
+
import { useApp } from '../../store/useApp.js'
|
|
4
|
+
|
|
5
|
+
export function EndChat ({ onChatScreen, onEndChatConfirm }) {
|
|
6
|
+
const { setCustomerId, setThreadId, setMessages, agentStatus, thread } = useApp()
|
|
7
|
+
|
|
8
|
+
const confirmEndChat = async e => {
|
|
9
|
+
e.preventDefault()
|
|
10
|
+
|
|
11
|
+
setThreadId()
|
|
12
|
+
setMessages([])
|
|
13
|
+
setCustomerId()
|
|
14
|
+
|
|
15
|
+
// End chat if still open
|
|
16
|
+
if (agentStatus !== 'closed') {
|
|
17
|
+
thread.endChat()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
onEndChatConfirm(e)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<PanelHeader />
|
|
26
|
+
<div className='wc-body'>
|
|
27
|
+
<h3 className='govuk-heading-s' aria-live='polite'>Are you sure you want to end the chat?</h3>
|
|
28
|
+
<div className='govuk-button-group'>
|
|
29
|
+
<a href='#' className='govuk-button govuk-!-font-size-16' data-module='govuk-button' id='confirmEndChat' onClick={confirmEndChat}>Yes, end chat</a>
|
|
30
|
+
<a href='#' className='govuk-link govuk-!-font-size-16' onClick={onChatScreen}>No, resume chat</a>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
|
+
|
|
4
|
+
export function EndFeedback ({ onClose }) {
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<PanelHeader />
|
|
8
|
+
|
|
9
|
+
<div className='wc-body'>
|
|
10
|
+
<h3 id='wc-subtitle' class='govuk-heading-m'>Thank you for your feedback</h3>
|
|
11
|
+
<div>
|
|
12
|
+
<a href='#' role='button' draggable='false' data-module='govuk-button' data-wc-focus-visible data-wc-close-btn onClick={onClose}>Close window</a>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
</>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
|
+
|
|
4
|
+
export function Feedback ({ onCancel, onConfirmSubmit }) {
|
|
5
|
+
const feedbackSubmit = async e => {
|
|
6
|
+
e.preventDefault()
|
|
7
|
+
|
|
8
|
+
console.log('feedback submitted')
|
|
9
|
+
|
|
10
|
+
onConfirmSubmit(e)
|
|
11
|
+
}
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<PanelHeader />
|
|
15
|
+
|
|
16
|
+
<div className='wc-body'>
|
|
17
|
+
<h3 id='wc-subtitle' className='govuk-heading-s'>Give Feedback on Floodline webchat</h3>
|
|
18
|
+
<p>Please note we’re unable to respond to feedback.</p>
|
|
19
|
+
<div className='govuk-form-group'>
|
|
20
|
+
<fieldset className='govuk-fieldset govuk-!-margin-bottom-4'>
|
|
21
|
+
<legend className='govuk-fieldset-heading govuk-fieldset__legend'>
|
|
22
|
+
Overall, how did you feel about webchat?
|
|
23
|
+
</legend>
|
|
24
|
+
<div className='govuk-radios govuk-radios--small' data-module='govuk-radios'>
|
|
25
|
+
<div className='govuk-radios__item'>
|
|
26
|
+
<input
|
|
27
|
+
id='very-satisfied'
|
|
28
|
+
name='satisfaction'
|
|
29
|
+
type='radio'
|
|
30
|
+
className='govuk-radios__input'
|
|
31
|
+
value='Very satisfied'
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<label className='govuk-label govuk-radios__label' for='very-satisfied'>Very satisfied</label>
|
|
35
|
+
</div>
|
|
36
|
+
<div className='govuk-radios__item'>
|
|
37
|
+
<input
|
|
38
|
+
id='satisfied'
|
|
39
|
+
name='satisfaction'
|
|
40
|
+
type='radio'
|
|
41
|
+
className='govuk-radios__input'
|
|
42
|
+
value='Satisfied'
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<label className='govuk-label govuk-radios__label' for='satisfied'>Satisfied</label>
|
|
46
|
+
</div>
|
|
47
|
+
<div className='govuk-radios__item'>
|
|
48
|
+
<input
|
|
49
|
+
id='no-opinion'
|
|
50
|
+
name='satisfaction'
|
|
51
|
+
type='radio'
|
|
52
|
+
className='govuk-radios__input'
|
|
53
|
+
value='No opinion'
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<label className='govuk-label govuk-radios__label' for='no-opinion'>No Opinion</label>
|
|
57
|
+
</div>
|
|
58
|
+
<div className='govuk-radios__item'>
|
|
59
|
+
<input
|
|
60
|
+
id='dissatisfied'
|
|
61
|
+
name='satisfaction'
|
|
62
|
+
type='radio'
|
|
63
|
+
className='govuk-radios__input'
|
|
64
|
+
value='Dissatisfied'
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
<label className='govuk-label govuk-radios__label' for='dissatisfied'>Dissatisfied</label>
|
|
68
|
+
</div>
|
|
69
|
+
<div className='govuk-radios__item'>
|
|
70
|
+
<input
|
|
71
|
+
id='very-dissatisfied'
|
|
72
|
+
name='satisfaction'
|
|
73
|
+
type='radio'
|
|
74
|
+
className='govuk-radios__input'
|
|
75
|
+
value='Very dissatisfied'
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<label className='govuk-label govuk-radios__label' for='very-dissatisfied'>Very dissatisfied</label>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</fieldset>
|
|
82
|
+
|
|
83
|
+
<fieldset className='govuk-fieldset govuk-!-margin-bottom-4'>
|
|
84
|
+
<legend className='govuk-fieldset__legend'>
|
|
85
|
+
Did the adviser answer your question?
|
|
86
|
+
</legend>
|
|
87
|
+
<div className='govuk-radios govuk-radios--small' data-module='govuk-radios'>
|
|
88
|
+
<div className='govuk-radios__item'>
|
|
89
|
+
<input
|
|
90
|
+
id='answer-yes'
|
|
91
|
+
name='answer'
|
|
92
|
+
type='radio'
|
|
93
|
+
className='govuk-radios__input'
|
|
94
|
+
value='Yes'
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
<label className='govuk-label govuk-radios__label' for='answer-yes'>Yes</label>
|
|
98
|
+
</div>
|
|
99
|
+
<div className='govuk-radios__item'>
|
|
100
|
+
<input
|
|
101
|
+
id='answer-no'
|
|
102
|
+
name='answer'
|
|
103
|
+
type='radio'
|
|
104
|
+
className='govuk-radios__input'
|
|
105
|
+
value='No'
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
<label className='govuk-label govuk-radios__label' for='answer-no'>No</label>
|
|
109
|
+
</div>
|
|
110
|
+
<div className='govuk-radios__item'>
|
|
111
|
+
<input
|
|
112
|
+
id='answer-partly'
|
|
113
|
+
name='answer'
|
|
114
|
+
type='radio'
|
|
115
|
+
className='govuk-radios__input'
|
|
116
|
+
value='Partly'
|
|
117
|
+
/>
|
|
118
|
+
|
|
119
|
+
<label className='govuk-label govuk-radios__label' for='answer-partly'>Partly</label>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</fieldset>
|
|
123
|
+
|
|
124
|
+
</div>
|
|
125
|
+
<div className='govuk-form-group'>
|
|
126
|
+
<label className='govuk-label' for='improvements'>
|
|
127
|
+
How can we improve webchat? (optional)
|
|
128
|
+
</label>
|
|
129
|
+
<textarea className='govuk-textarea govuk-!-margin-bottom-4' id='improvements' name='improvements' rows='5' />
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div className='govuk-form-group'>
|
|
133
|
+
<label className='wc-fieldset-heading govuk-label' for='research'>
|
|
134
|
+
Tell us your email address if you want to take part in user research (optional)
|
|
135
|
+
</label>
|
|
136
|
+
<div id='research-hint' class='wc-hint govuk-hint'>
|
|
137
|
+
We will not share your email address or use it for anything else. We’ll delete it after 2 years.
|
|
138
|
+
</div>
|
|
139
|
+
<input className='govuk-input govuk-!-margin-bottom-4' id='research' name='research' aria-describedby='research-hint' type='text' />
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div className='govuk-button-group'>
|
|
143
|
+
<a id='feedback-save' href='#' className='govuk-button govuk-!-font-size-16' data-module='govuk-button' onClick={feedbackSubmit}>Submit</a>
|
|
144
|
+
<a id='feedback-cancel' href='#' className='govuk-link govuk-!-font-size-16' data-module='govuk-button' onClick={onCancel}>Cancel</a>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</>
|
|
148
|
+
)
|
|
149
|
+
}
|