From 45e39c8749cabe2f17e9aeb6d2891e23801178f4 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 18:05:10 +0200 Subject: [PATCH] feat(ui) - funnels - wip --- frontend/app/mstore/funnelStore.ts | 90 ++++++++++++++++++++++ frontend/app/mstore/types/funnel.ts | 45 +++++++++++ frontend/app/mstore/types/funnelStage.ts | 18 +++++ frontend/app/mstore/types/funnelnsights.ts | 12 +++ frontend/app/services/FunnelService.ts | 62 +++++++++++++++ frontend/app/services/index.ts | 4 +- 6 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 frontend/app/mstore/funnelStore.ts create mode 100644 frontend/app/mstore/types/funnel.ts create mode 100644 frontend/app/mstore/types/funnelStage.ts create mode 100644 frontend/app/mstore/types/funnelnsights.ts create mode 100644 frontend/app/services/FunnelService.ts diff --git a/frontend/app/mstore/funnelStore.ts b/frontend/app/mstore/funnelStore.ts new file mode 100644 index 000000000..cd43065b3 --- /dev/null +++ b/frontend/app/mstore/funnelStore.ts @@ -0,0 +1,90 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import { funnelService } from "App/services" +import Funnel, { IFunnel } from "./types/funnel"; +import Period, { LAST_7_DAYS } from 'Types/app/period'; + +export default class FunnelStore { + isLoading: boolean = false + isSaving: boolean = false + list: IFunnel[] = [] + instance: IFunnel | null = null + period: Period = Period({ rangeName: LAST_7_DAYS }) + + constructor() { + makeAutoObservable(this, { + fetchFunnels: action, + fetchFunnel: action, + saveFunnel: action, + deleteFunnel: action + }) + } + + fetchFunnels(): Promise { + this.isLoading = true + return new Promise((resolve, reject) => { + funnelService.all() + .then(response => { + this.list = response + resolve(response) + }).catch(error => { + reject(error) + }).finally(() => { + this.isLoading = false + } + ) + }) + } + + fetchFunnel(funnelId: string): Promise { + this.isLoading = true + return new Promise((resolve, reject) => { + funnelService.one(funnelId) + .then(response => { + const _funnel = new Funnel().fromJSON(response) + this.instance = _funnel + resolve(_funnel) + }).catch(error => { + reject(error) + }).finally(() => { + this.isLoading = false + } + ) + }) + } + + saveFunnel(funnel: IFunnel): Promise { + this.isSaving = true + const wasCreating = !funnel.funnelId + return new Promise((resolve, reject) => { + funnelService.save(funnel) + .then(response => { + const _funnel = new Funnel().fromJSON(response) + if (wasCreating) { + this.list.push(_funnel) + } + resolve(_funnel) + }).catch(error => { + reject(error) + }).finally(() => { + this.isSaving = false + } + ) + }) + } + + deleteFunnel(funnelId: string): Promise { + this.isSaving = true + return new Promise((resolve, reject) => { + funnelService.delete(funnelId) + .then(response => { + this.list = this.list.filter(funnel => funnel.funnelId !== funnelId) + resolve(funnelId) + }).catch(error => { + reject(error) + }).finally(() => { + this.isSaving = false + } + ) + }) + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/funnel.ts b/frontend/app/mstore/types/funnel.ts new file mode 100644 index 000000000..058587a66 --- /dev/null +++ b/frontend/app/mstore/types/funnel.ts @@ -0,0 +1,45 @@ +import Filter, { IFilter } from "./filter" + +export interface IFunnel { + funnelId: string + name: string + filter: IFilter + sessionsCount: number + conversionRate: number + totalConversations: number + lostConversations: number + fromJSON: (json: any) => void + toJSON: () => any +} + +export default class Funnel implements IFunnel { + funnelId: string = '' + name: string = '' + filter: IFilter = new Filter() + sessionsCount: number = 0 + conversionRate: number = 0 + totalConversations: number = 0 + lostConversations: number = 0 + + constructor() { + } + + fromJSON(json: any) { + this.funnelId = json.funnelId + this.name = json.name + this.filter = new Filter().fromJson(json.filter) + this.sessionsCount = json.sessionsCount + this.conversionRate = json.conversionRate + return this + } + + toJSON(): any { + return { + funnelId: this.funnelId, + name: this.name, + filter: this.filter.toJson(), + sessionsCount: this.sessionsCount, + conversionRate: this.conversionRate, + } + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/funnelStage.ts b/frontend/app/mstore/types/funnelStage.ts new file mode 100644 index 000000000..8714324f6 --- /dev/null +++ b/frontend/app/mstore/types/funnelStage.ts @@ -0,0 +1,18 @@ +export default class FunnelStage { + dropDueToIssues: number = 0; + dropPct: number = 0; + operator: string = ""; + sessionsCount: number = 0; + usersCount: number = 0; + value: string[] = []; + + fromJSON(json: any) { + this.dropDueToIssues = json.dropDueToIssues; + this.dropPct = json.dropPct; + this.operator = json.operator; + this.sessionsCount = json.sessionsCount; + this.usersCount = json.usersCount; + this.value = json.value; + return this; + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/funnelnsights.ts b/frontend/app/mstore/types/funnelnsights.ts new file mode 100644 index 000000000..8b1e22e6e --- /dev/null +++ b/frontend/app/mstore/types/funnelnsights.ts @@ -0,0 +1,12 @@ +import FunnelStage from "./funnelStage"; + +export default class FunnelInsights { + stages: FunnelStage[] = []; + totalDropDueToIssues: number = 0; + + fromJSON(json: any) { + this.stages = json.stages.map(stage => new FunnelStage().fromJSON(stage)); + this.totalDropDueToIssues = json.totalDropDueToIssues; + return this; + } +} \ No newline at end of file diff --git a/frontend/app/services/FunnelService.ts b/frontend/app/services/FunnelService.ts new file mode 100644 index 000000000..f5f9e4706 --- /dev/null +++ b/frontend/app/services/FunnelService.ts @@ -0,0 +1,62 @@ +import { IFunnel } from "App/mstore/types/funnel" +import APIClient from 'App/api_client'; + +export interface IFunnelService { + initClient(client?: APIClient) + all(): Promise + one(funnelId: string): Promise + save(funnel: IFunnel): Promise + delete(funnelId: string): Promise + + fetchInsights(funnelId: string, payload: any): Promise + fetchIssues(funnelId: string, payload: any): Promise + fetchIssue(funnelId: string, issueId: string): Promise +} + +export default class FunnelService implements IFunnelService { + private client: APIClient; + + constructor(client?: APIClient) { + this.client = client ? client : new APIClient(); + } + + initClient(client?: APIClient) { + this.client = client || new APIClient(); + } + + all(): Promise { + return this.client.get('/funnels') + .then(response => response.json()) + .then(response => response.data || []); + } + + one(funnelId: string): Promise { + return this.client.get(`/funnels/${funnelId}`) + .then(response => response.json()) + } + + save(funnel: IFunnel): Promise { + return this.client.post('/funnels', funnel) + .then(response => response.json()) + } + + delete(funnelId: string): Promise { + return this.client.delete(`/funnels/${funnelId}`) + .then(response => response.json()) + } + + fetchInsights(funnelId: string, payload: any): Promise { + return this.client.post(`/funnels/${funnelId}/insights`, payload) + .then(response => response.json()) + } + + fetchIssues(funnelId: string, payload: any): Promise { + return this.client.post(`/funnels/${funnelId}/issues`, payload) + .then(response => response.json()) + } + + fetchIssue(funnelId: string, issueId: string): Promise { + return this.client.get(`/funnels/${funnelId}/issues/${issueId}`) + .then(response => response.json()) + } +} \ No newline at end of file diff --git a/frontend/app/services/index.ts b/frontend/app/services/index.ts index 944877c16..2d7261f76 100644 --- a/frontend/app/services/index.ts +++ b/frontend/app/services/index.ts @@ -1,5 +1,7 @@ import DashboardService, { IDashboardService } from "./DashboardService"; import MetricService, { IMetricService } from "./MetricService"; +import FunnelService, { IFunnelService } from "./FunnelService"; export const dashboardService: IDashboardService = new DashboardService(); -export const metricService: IMetricService = new MetricService(); \ No newline at end of file +export const metricService: IMetricService = new MetricService(); +export const funnelService: IFunnelService = new FunnelService(); \ No newline at end of file