feat(ui) - funnels - issue details
This commit is contained in:
parent
4e2bcf26a4
commit
b26f2e87bf
5 changed files with 63 additions and 39 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import { Loader } from 'UI';
|
||||
|
|
@ -6,23 +6,25 @@ import FunnelIssuesListItem from '../FunnelIssuesListItem';
|
|||
import SessionItem from 'App/components/shared/SessionItem/SessionItem';
|
||||
|
||||
interface Props {
|
||||
funnelId: string;
|
||||
issueId: string;
|
||||
}
|
||||
function FunnelIssueDetails(props: Props) {
|
||||
const { funnelStore } = useStore();
|
||||
const { funnelId, issueId } = props;
|
||||
const funnel = useObserver(() => funnelStore.instance);
|
||||
const funnelIssue = useObserver(() => funnelStore.issueInstance);
|
||||
const loading = useObserver(() => funnelStore.isLoadingIssues);
|
||||
const { dashboardStore, metricStore } = useStore();
|
||||
const { issueId } = props;
|
||||
const filter = useObserver(() => dashboardStore.drillDownFilter);
|
||||
const widget = useObserver(() => metricStore.instance);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [funnelIssue, setFunnelIssue] = useState<any>(null);
|
||||
const [sessions, setSessions] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!funnel || !funnel.exists()) {
|
||||
// funnelStore.fetchFunnel(props.funnelId);
|
||||
funnelStore.fetchFunnel('143');
|
||||
}
|
||||
|
||||
funnelStore.fetchIssue(funnelId, issueId);
|
||||
setLoading(true);
|
||||
widget.fetchIssue(widget.metricId, issueId, filter).then((resp: any) => {
|
||||
setFunnelIssue(resp.issue);
|
||||
setSessions(resp.sessions);
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
|
@ -33,7 +35,7 @@ function FunnelIssueDetails(props: Props) {
|
|||
/>}
|
||||
|
||||
<div className="mt-6">
|
||||
{funnelIssue && funnelIssue.sessions.map(session => (
|
||||
{sessions.map((session: any) => (
|
||||
<SessionItem key={session.id} session={session} />
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import React from 'react';
|
|||
import { withRouter } from 'react-router-dom';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import FunnelIssueDetails from '../FunnelIssueDetails';
|
||||
|
||||
interface Props {
|
||||
|
||||
issueId: string;
|
||||
}
|
||||
function FunnelIssueModal(props: Props) {
|
||||
const { issueId } = props;
|
||||
const { hideModal } = useModal();
|
||||
return (
|
||||
<div style={{ width: '85vw', maxWidth: '1200px' }}>
|
||||
|
|
@ -14,6 +16,7 @@ function FunnelIssueModal(props: Props) {
|
|||
className="border-r shadow p-4 h-screen"
|
||||
style={{ backgroundColor: '#FAFAFA', zIndex: 999, width: '100%' }}
|
||||
>
|
||||
<FunnelIssueDetails issueId={issueId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function FunnelIssuesListItem(props) {
|
|||
const { issue, inDetails = false } = props;
|
||||
const { showModal } = useModal();
|
||||
const onClick = () => {
|
||||
showModal(<FunnelIssueModal />, { right: true });
|
||||
showModal(<FunnelIssueModal issueId={issue.issueId} />, { right: true });
|
||||
}
|
||||
return (
|
||||
<div className={cn('flex flex-col bg-white w-full rounded border relative hover:bg-active-blue', { 'cursor-pointer bg-hover' : !inDetails })} onClick={!inDetails ? onClick : () => null}>
|
||||
|
|
@ -30,49 +30,49 @@ function FunnelIssuesListItem(props) {
|
|||
</div>
|
||||
|
||||
{inDetails && (
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="text-lg font-medium mb-2 capitalize">{issue.title}</div>
|
||||
<div className="text-xl whitespace-nowrap">
|
||||
<TextEllipsis text={issue.contextString} />
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="text-lg font-medium mb-2 capitalize">{issue.title}</div>
|
||||
<div className="text-xl whitespace-nowrap">
|
||||
<TextEllipsis text={issue.contextString} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!inDetails && (
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="text-xl mb-2 capitalize">{issue.title}</div>
|
||||
<div className="text-sm color-gray-medium whitespace-nowrap leading-none">
|
||||
<TextEllipsis text={issue.contextString} />
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="text-xl mb-2 capitalize">{issue.title}</div>
|
||||
<div className="text-sm color-gray-medium whitespace-nowrap leading-none">
|
||||
<TextEllipsis text={issue.contextString} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-center text-sm ml-10 flex-shrink-0">
|
||||
<div className="text-xl mb-2">{issue.affectedUsers}</div>
|
||||
<div className="color-gray-medium leading-none">Affected Users</div>
|
||||
<div className="text-xl mb-2">{issue.affectedUsers}</div>
|
||||
<div className="color-gray-medium leading-none">Affected Users</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center text-sm ml-10 flex-shrink-0">
|
||||
<div className="text-xl mb-2 color-red">{issue.conversionImpact}<span className="text-sm ml-1">%</span></div>
|
||||
<div className="color-gray-medium leading-none">Conversion Impact</div>
|
||||
<div className="text-xl mb-2 color-red">{issue.conversionImpact}<span className="text-sm ml-1">%</span></div>
|
||||
<div className="color-gray-medium leading-none">Conversion Impact</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center text-sm ml-10 flex-shrink-0">
|
||||
<div className="text-xl mb-2">{issue.lostConversions}</div>
|
||||
<div className="color-gray-medium leading-none">Lost Conversions</div>
|
||||
<div className="text-xl mb-2">{issue.lostConversions}</div>
|
||||
<div className="color-gray-medium leading-none">Lost Conversions</div>
|
||||
</div>
|
||||
</div>
|
||||
{inDetails && (
|
||||
<div className="flex items-center px-6 py-4 justify-between border-t">
|
||||
<FunnelIssueGraph issue={issue} />
|
||||
<div className="flex items-center">
|
||||
<Info label="Unaffected sessions" color="rgba(217, 219, 238, 0.7)" />
|
||||
<Info label="Affected sessions" color="rgba(238, 238, 238, 0.7)" />
|
||||
<Info label="Conversion Lost" color="rgba(204, 0, 0, 0.26)" />
|
||||
</div>
|
||||
<FunnelIssueGraph issue={issue} />
|
||||
<div className="flex items-center">
|
||||
<Info label="Unaffected sessions" color="rgba(217, 219, 238, 0.7)" />
|
||||
<Info label="Affected sessions" color="rgba(238, 238, 238, 0.7)" />
|
||||
<Info label="Conversion Lost" color="rgba(204, 0, 0, 0.26)" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -225,4 +225,17 @@ export default class Widget implements IWidget {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
fetchIssue(funnelId: any, issueId: any, params: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
metricService.fetchIssue(funnelId, issueId, params).then((response: any) => {
|
||||
resolve({
|
||||
issue: new Funnelissue().fromJSON(response.issue),
|
||||
sessions: response.sessions.map((s: any) => new Session().fromJson(s)),
|
||||
})
|
||||
}).catch((error: any) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -108,4 +108,10 @@ export default class MetricService implements IMetricService {
|
|||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data || {});
|
||||
}
|
||||
|
||||
fetchIssue(funnelId: string, issueId: string, params: any): Promise<any> {
|
||||
return this.client.post(`/funnels/${funnelId}/issues/${issueId}/sessions`, params)
|
||||
.then((response: { json: () => any; }) => response.json())
|
||||
.then((response: { data: any; }) => response.data || {});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue