diff --git a/frontend/app/components/Funnels/FunnelWidget/FunnelWidget.tsx b/frontend/app/components/Funnels/FunnelWidget/FunnelWidget.tsx
index 0e93c7e25..814a3a734 100644
--- a/frontend/app/components/Funnels/FunnelWidget/FunnelWidget.tsx
+++ b/frontend/app/components/Funnels/FunnelWidget/FunnelWidget.tsx
@@ -85,7 +85,7 @@ function FunnelWidget(props: Props) {
));
}
-function EmptyStage({ total }: any) {
+export function EmptyStage({ total }: any) {
return useObserver( () => (
diff --git a/frontend/app/components/UsabilityTesting/ResponsesOverview.tsx b/frontend/app/components/UsabilityTesting/ResponsesOverview.tsx
index 3d5b0c5f5..07e780e4c 100644
--- a/frontend/app/components/UsabilityTesting/ResponsesOverview.tsx
+++ b/frontend/app/components/UsabilityTesting/ResponsesOverview.tsx
@@ -2,29 +2,49 @@ import React from 'react';
import { useStore } from 'App/mstore';
import { numberWithCommas } from 'App/utils';
import { Step } from 'Components/UsabilityTesting/TestEdit';
+import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
import { Loader, NoContent, Pagination } from 'UI';
import { Button, Typography, Input } from 'antd';
import { observer } from 'mobx-react-lite';
import { DownOutlined } from '@ant-design/icons';
+import { debounce } from 'App/utils'
+
+let debounceUpdate: any = () => {}
const ResponsesOverview = observer(() => {
const { uxtestingStore } = useStore();
const [search, setSearch] = React.useState('');
const [page, setPage] = React.useState(1);
const [showAll, setShowAll] = React.useState(false);
- const [taskId, setTaskId] = React.useState(uxtestingStore.instance?.tasks[0].taskId);
+ const [taskId, setTaskId] = React.useState
(undefined);
React.useEffect(() => {
- void refreshData();
+ setTaskId(uxtestingStore.instance?.tasks.filter((t) => t.allowTyping)[0].taskId);
+ }, [uxtestingStore.instance?.tasks]);
+
+ React.useEffect(() => {
+ if (taskId) {
+ void refreshData();
+ }
}, [page, taskId]);
- const refreshData = () =>
+ debounceUpdate = debounce((text: string) => {
+ void refreshData(text);
+ }, 200)
+
+ const refreshDataQuery = (text: string) => {
+ setSearch(text)
+ debounceUpdate(text)
+ }
+
+ const refreshData = (searchText?: string) =>
taskId
- ? uxtestingStore.fetchResponses(uxtestingStore.instance!.testId!, taskId, page, search)
+ ? uxtestingStore.fetchResponses(uxtestingStore.instance!.testId!, taskId, page, searchText || search)
: null;
const selectedIndex = uxtestingStore.instance?.tasks.findIndex((task) => task.taskId === taskId)!;
const task = uxtestingStore.instance?.tasks.find((task) => task.taskId === taskId);
+
return (
@@ -32,26 +52,28 @@ const ResponsesOverview = observer(() => {
Select Task / Question
-
- setShowAll(!showAll)}
- icon={ }
- size={'small'}
- />
-
- }
- />
+
setShowAll(false)}>
+ setShowAll(!showAll)}>
+
+ setShowAll(!showAll)}
+ icon={ }
+ size={'small'}
+ />
+
+ }
+ />
+
+
{showAll ? (
{uxtestingStore.instance?.tasks
.filter((t) => t.taskId !== taskId && t.allowTyping)
@@ -87,8 +109,8 @@ const ResponsesOverview = observer(() => {
setSearch(e.target.value)}
+ placeholder={'Filter by keyword or participant'}
+ onChange={(e) => refreshDataQuery(e.target.value)}
classNames={{ input: '!border-0 focus:!border-0' }}
onSearch={() => refreshData()}
/>
diff --git a/frontend/app/components/UsabilityTesting/TestEdit.tsx b/frontend/app/components/UsabilityTesting/TestEdit.tsx
index 56260fe45..bc9d3da78 100644
--- a/frontend/app/components/UsabilityTesting/TestEdit.tsx
+++ b/frontend/app/components/UsabilityTesting/TestEdit.tsx
@@ -16,6 +16,7 @@ import { confirm } from 'UI';
import StepsModal from './StepsModal';
import SidePanel from './SidePanel';
import usePageTitle from 'App/hooks/usePageTitle';
+import { toast } from 'react-toastify'
const menuItems = [
{
@@ -71,6 +72,7 @@ function TestEdit() {
setHasChanged(false);
if (testId && testId !== 'new') {
uxtestingStore.updateTest(uxtestingStore.instance!).then((testId) => {
+ toast.success('The usability test is now live and accessible to participants.');
history.push(withSiteId(usabilityTestingView(testId!.toString()), siteId));
});
} else {
diff --git a/frontend/app/components/UsabilityTesting/TestOverview.tsx b/frontend/app/components/UsabilityTesting/TestOverview.tsx
index 640acfacb..9226f5dd9 100644
--- a/frontend/app/components/UsabilityTesting/TestOverview.tsx
+++ b/frontend/app/components/UsabilityTesting/TestOverview.tsx
@@ -29,18 +29,18 @@ import {
} from '@ant-design/icons';
import SessionItem from 'Shared/SessionItem';
import { CopyButton, Loader, NoContent, Pagination } from 'UI';
-import copy from 'copy-to-clipboard';
-import { Stage } from 'Components/Funnels/FunnelWidget/FunnelWidget';
+import { Stage, EmptyStage } from 'Components/Funnels/FunnelWidget/FunnelWidget';
import { confirm } from 'UI';
import ResponsesOverview from './ResponsesOverview';
import ParticipantOverviewItem from 'Components/UsabilityTesting/ParticipantOverview';
+import { toast } from 'react-toastify'
const { Option } = Select;
const statusItems = [
{ value: 'in-progress', label: 'Ongoing', icon: },
- { value: 'paused', label: 'On Hold', icon: },
- { value: 'closed', label: 'Closed', icon: },
+ { value: 'paused', label: 'Hold', icon: },
+ { value: 'closed', label: 'Close', icon: },
];
const menuItems = [
@@ -310,9 +310,9 @@ const TaskSummary = observer(() => {
index={index + 1}
/>
))}
- {shouldHide ? (
- setShowAll(!showAll)} className={'link mt-4'}>
- {showAll ? 'Hide' : 'Show All'}
+ {shouldHide && !showAll ? (
+
setShowAll(true)}>
+
) : null}
@@ -326,6 +326,17 @@ const Title = observer(({ testId, siteId }: any) => {
const handleChange = (value: string) => {
uxtestingStore.updateTestStatus(value);
+ switch (value) {
+ case 'in-progress':
+ toast.success('The usability test is now live and accessible to participants.');
+ break;
+ case 'paused':
+ toast.success('Usability test is on \'Hold\'—participant activity paused. Resume at your convenience.');
+ break;
+ case 'closed':
+ toast.success('he usability test has been marked as completed. All participant interactions are now finalized.');
+ break;
+ }
};
const onMenuClick = async ({ key }: any) => {
@@ -397,7 +408,8 @@ const Title = observer(({ testId, siteId }: any) => {
title={'Participants Link'}
content={
-
+ Distribute following link via email or other methods to share the survey with test participants.
+
{`${uxtestingStore.instance!.startingPath}?oruxt=${
uxtestingStore.instance!.testId
}`}
diff --git a/frontend/app/components/shared/SessionItem/SessionItem.tsx b/frontend/app/components/shared/SessionItem/SessionItem.tsx
index 45ed2b5eb..c8bf537cb 100644
--- a/frontend/app/components/shared/SessionItem/SessionItem.tsx
+++ b/frontend/app/components/shared/SessionItem/SessionItem.tsx
@@ -123,8 +123,9 @@ function SessionItem(props: RouteComponentProps & Props) {
!ignoreAssist &&
(isRoute(ASSIST_ROUTE, location.pathname) ||
isRoute(ASSIST_LIVE_SESSION, location.pathname) ||
- location.pathname.includes('multiview'))
- || location.pathname.includes('usability-testing');
+ location.pathname.includes('multiview')) ||
+ props.live
+
const isLastPlayed = lastPlayedSessionId === sessionId;
const _metaList = Object.keys(metadata)