From 949207102da98296d41a0b0f2da46928f7d5b2cc Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Thu, 7 Dec 2023 10:18:47 +0100 Subject: [PATCH] fix(ui) uxt fixes --- .../Funnels/FunnelWidget/FunnelWidget.tsx | 2 +- .../UsabilityTesting/ResponsesOverview.tsx | 70 ++++++++++++------- .../components/UsabilityTesting/TestEdit.tsx | 2 + .../UsabilityTesting/TestOverview.tsx | 28 +++++--- .../shared/SessionItem/SessionItem.tsx | 5 +- 5 files changed, 72 insertions(+), 35 deletions(-) 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(false)}> +
setShowAll(!showAll)}> + +
+ } + /> +
+ {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)