feat(ui) - funnels - issues
This commit is contained in:
parent
34425b8b02
commit
bec68eb375
9 changed files with 116 additions and 4 deletions
|
|
@ -0,0 +1,36 @@
|
|||
import { useStore } from 'App/mstore';
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import React, { useEffect } from 'react';
|
||||
import { NoContent, Loader } from 'UI';
|
||||
import FunnelIssuesSort from '../FunnelIssuesSort';
|
||||
|
||||
function FunnelIssues(props) {
|
||||
const { funnelStore } = useStore();
|
||||
const funnel = useObserver(() => funnelStore.instance);
|
||||
const issues = useObserver(() => funnelStore.issues);
|
||||
const loading = useObserver(() => funnelStore.isLoadingIssues);
|
||||
|
||||
useEffect(() => {
|
||||
funnelStore.fetchIssues(funnel?.funnelId);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="my-8">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="font-medium text-2xl">Most significant issues <span className="font-normal">identified in this funnel</span></h1>
|
||||
<FunnelIssuesSort />
|
||||
</div>
|
||||
<Loader loading={loading}>
|
||||
<NoContent
|
||||
show={issues.length === 0}
|
||||
title="No issues found."
|
||||
animatedIcon="empty-state"
|
||||
>
|
||||
Issues
|
||||
</NoContent>
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FunnelIssues;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import Select from 'Shared/Select';
|
||||
|
||||
const sortOptions = [
|
||||
{ value: 'afectedUsers-desc', label: 'Affected Users (High)' },
|
||||
{ value: 'afectedUsers-asc', label: 'Affected Users (Low)' },
|
||||
{ value: 'conversionImpact-desc', label: 'Conversion Impact (High)' },
|
||||
{ value: 'conversionImpact-asc', label: 'Conversion Impact (Low)' },
|
||||
{ value: 'lostConversions-desc', label: 'Lost Conversions (High)' },
|
||||
{ value: 'lostConversions-asc', label: 'Lost Conversions (Low)' },
|
||||
]
|
||||
function FunnelIssuesSort(props) {
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
plain
|
||||
options={sortOptions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FunnelIssuesSort;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './FunnelIssuesSort';
|
||||
|
|
@ -7,6 +7,7 @@ import { Icon, BackLink, Loader } from 'UI';
|
|||
import { useObserver } from 'mobx-react-lite';
|
||||
import { withSiteId } from 'App/routes';
|
||||
import WidgetName from '../WidgetName';
|
||||
import FunnelIssues from '../FunnelIssues/FunnelIssues';
|
||||
interface Props {
|
||||
history: any;
|
||||
match: any
|
||||
|
|
@ -17,6 +18,7 @@ function WidgetView(props: Props) {
|
|||
const { metricStore } = useStore();
|
||||
const widget = useObserver(() => metricStore.instance);
|
||||
const loading = useObserver(() => metricStore.isLoading);
|
||||
const isFunnel = widget.metricType === 'funnel';
|
||||
const [expanded, setExpanded] = useState(!metricId || metricId === 'create');
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
@ -63,7 +65,8 @@ function WidgetView(props: Props) {
|
|||
</div>
|
||||
|
||||
<WidgetPreview className="mt-8" />
|
||||
<WidgetSessions className="mt-8" />
|
||||
{ !isFunnel && <WidgetSessions className="mt-8" /> }
|
||||
{ isFunnel && <FunnelIssues /> }
|
||||
</div>
|
||||
</Loader>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export default function({ plain = false, options, isSearchable = false, defaultV
|
|||
}
|
||||
if (plain) {
|
||||
obj['border'] = '1px solid transparent'
|
||||
obj['backgroundColor'] = 'transparent'
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
|
||||
import { funnelService } from "App/services"
|
||||
import Funnel, { IFunnel } from "./types/funnel";
|
||||
import FunnelIssue from './types/funnelIssue';
|
||||
import Period, { LAST_7_DAYS } from 'Types/app/period';
|
||||
|
||||
export default class FunnelStore {
|
||||
|
|
@ -13,6 +14,9 @@ export default class FunnelStore {
|
|||
|
||||
page: number = 1
|
||||
pageSize: number = 10
|
||||
|
||||
issues: any[] = []
|
||||
isLoadingIssues: boolean = false
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this, {
|
||||
|
|
@ -96,4 +100,20 @@ export default class FunnelStore {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
fetchIssues(funnelId?: string): Promise<any> {
|
||||
this.isLoadingIssues = true
|
||||
return new Promise((resolve, reject) => {
|
||||
funnelService.fetchIssues(funnelId, this.period)
|
||||
.then(response => {
|
||||
this.issues = response.map(i => new FunnelIssue().fromJSON(i))
|
||||
resolve(this.issues)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
}
|
||||
).finally(() => {
|
||||
this.isLoadingIssues = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
27
frontend/app/mstore/types/funnelIssue.ts
Normal file
27
frontend/app/mstore/types/funnelIssue.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
export default class FunnelIssue {
|
||||
issueId: string = ''
|
||||
title: string = ''
|
||||
type: string = ''
|
||||
affectedSessions: number = 0
|
||||
affectedUsers: number = 0
|
||||
unaffectedSessions: number = 0
|
||||
contextString: string = ''
|
||||
conversionImpact: number = 0
|
||||
lostConversions: number = 0
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
fromJSON(json: any) {
|
||||
this.issueId = json.issueId
|
||||
this.title = json.title
|
||||
this.type = json.type
|
||||
this.affectedSessions = json.affectedSessions
|
||||
this.affectedUsers = json.affectedUsers
|
||||
this.unaffectedSessions = json.unaffectedSessions
|
||||
this.contextString = json.contextString
|
||||
this.conversionImpact = json.conversionImpact
|
||||
this.lostConversions = json.lostConversions
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ export interface IFunnelService {
|
|||
delete(funnelId: string): Promise<any>
|
||||
|
||||
fetchInsights(funnelId: string, payload: any): Promise<any>
|
||||
fetchIssues(funnelId: string, payload: any): Promise<any>
|
||||
fetchIssues(funnelId?: string, payload?: any): Promise<any>
|
||||
fetchIssue(funnelId: string, issueId: string): Promise<any>
|
||||
}
|
||||
|
||||
|
|
@ -50,8 +50,9 @@ export default class FunnelService implements IFunnelService {
|
|||
.then(response => response.json())
|
||||
}
|
||||
|
||||
fetchIssues(funnelId: string, payload: any): Promise<any> {
|
||||
return this.client.post(`/funnels/${funnelId}/issues`, payload)
|
||||
fetchIssues(funnelId?: string, payload?: any): Promise<any> {
|
||||
const path = funnelId ? `/funnels/${funnelId}/issues` : '/funnels/issues';
|
||||
return this.client.post(path, payload)
|
||||
.then(response => response.json())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue