diff --git a/frontend/app/components/Dashboard/components/FunnelIssues/FunnelIssues.tsx b/frontend/app/components/Dashboard/components/FunnelIssues/FunnelIssues.tsx
new file mode 100644
index 000000000..9467029b2
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FunnelIssues/FunnelIssues.tsx
@@ -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 (
+
+
+
Most significant issues identified in this funnel
+
+
+
+
+ Issues
+
+
+
+ );
+}
+
+export default FunnelIssues;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/FunnelIssues/index.ts b/frontend/app/components/Dashboard/components/FunnelIssues/index.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/app/components/Dashboard/components/FunnelIssuesSort/FunnelIssuesSort.tsx b/frontend/app/components/Dashboard/components/FunnelIssuesSort/FunnelIssuesSort.tsx
new file mode 100644
index 000000000..b3ab876d4
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FunnelIssuesSort/FunnelIssuesSort.tsx
@@ -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 (
+
+
+
+ );
+}
+
+export default FunnelIssuesSort;
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/FunnelIssuesSort/index.ts b/frontend/app/components/Dashboard/components/FunnelIssuesSort/index.ts
new file mode 100644
index 000000000..895b1eddd
--- /dev/null
+++ b/frontend/app/components/Dashboard/components/FunnelIssuesSort/index.ts
@@ -0,0 +1 @@
+export { default } from './FunnelIssuesSort';
\ No newline at end of file
diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx
index fb22728d3..ddc45a5ac 100644
--- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx
+++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx
@@ -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) {
-
+ { !isFunnel && }
+ { isFunnel && }
));
diff --git a/frontend/app/components/shared/Select/Select.tsx b/frontend/app/components/shared/Select/Select.tsx
index 307fbb0ea..313b9c98d 100644
--- a/frontend/app/components/shared/Select/Select.tsx
+++ b/frontend/app/components/shared/Select/Select.tsx
@@ -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;
},
diff --git a/frontend/app/mstore/funnelStore.ts b/frontend/app/mstore/funnelStore.ts
index 1913b2506..87ac2e735 100644
--- a/frontend/app/mstore/funnelStore.ts
+++ b/frontend/app/mstore/funnelStore.ts
@@ -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 {
+ 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
+ })
+ })
+ }
}
\ No newline at end of file
diff --git a/frontend/app/mstore/types/funnelIssue.ts b/frontend/app/mstore/types/funnelIssue.ts
new file mode 100644
index 000000000..c5bafbfaf
--- /dev/null
+++ b/frontend/app/mstore/types/funnelIssue.ts
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/services/FunnelService.ts b/frontend/app/services/FunnelService.ts
index f5f9e4706..099ad8d1f 100644
--- a/frontend/app/services/FunnelService.ts
+++ b/frontend/app/services/FunnelService.ts
@@ -9,7 +9,7 @@ export interface IFunnelService {
delete(funnelId: string): Promise
fetchInsights(funnelId: string, payload: any): Promise
- fetchIssues(funnelId: string, payload: any): Promise
+ fetchIssues(funnelId?: string, payload?: any): Promise
fetchIssue(funnelId: string, issueId: string): Promise
}
@@ -50,8 +50,9 @@ export default class FunnelService implements IFunnelService {
.then(response => response.json())
}
- fetchIssues(funnelId: string, payload: any): Promise {
- return this.client.post(`/funnels/${funnelId}/issues`, payload)
+ fetchIssues(funnelId?: string, payload?: any): Promise {
+ const path = funnelId ? `/funnels/${funnelId}/issues` : '/funnels/issues';
+ return this.client.post(path, payload)
.then(response => response.json())
}