@antelopejs/dms-marketing 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -15
- package/dist/config.d.ts +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/data-api/funnels.d.ts +2 -0
- package/dist/data-api/funnels.js +8 -1
- package/dist/data-api/funnels.js.map +1 -1
- package/dist/db/models/funnels.model.d.ts +2 -0
- package/dist/db/models/funnels.model.js +9 -0
- package/dist/db/models/funnels.model.js.map +1 -1
- package/dist/db/models/index.d.ts +1 -0
- package/dist/db/models/index.js +1 -0
- package/dist/db/models/index.js.map +1 -1
- package/dist/db/models/marketing_events.model.d.ts +22 -0
- package/dist/db/models/marketing_events.model.js +70 -5
- package/dist/db/models/marketing_events.model.js.map +1 -1
- package/dist/db/models/marketing_preferences.model.d.ts +25 -0
- package/dist/db/models/marketing_preferences.model.js +29 -0
- package/dist/db/models/marketing_preferences.model.js.map +1 -0
- package/dist/db/models/marketing_sessions.model.d.ts +2 -1
- package/dist/db/models/marketing_sessions.model.js +11 -1
- package/dist/db/models/marketing_sessions.model.js.map +1 -1
- package/dist/db/models/page_snapshots.model.d.ts +2 -1
- package/dist/db/models/page_snapshots.model.js +7 -7
- package/dist/db/models/page_snapshots.model.js.map +1 -1
- package/dist/db/models/website_statistics.model.d.ts +4 -1
- package/dist/db/models/website_statistics.model.js +15 -1
- package/dist/db/models/website_statistics.model.js.map +1 -1
- package/dist/db/tables/index.d.ts +1 -0
- package/dist/db/tables/index.js +1 -0
- package/dist/db/tables/index.js.map +1 -1
- package/dist/db/tables/marketing_preferences.table.d.ts +6 -0
- package/dist/db/tables/marketing_preferences.table.js +31 -0
- package/dist/db/tables/marketing_preferences.table.js.map +1 -0
- package/dist/hooks/index.js +8 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.js +18 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/acquisition/page.d.ts +9 -0
- package/dist/pages/acquisition/page.js +181 -0
- package/dist/pages/acquisition/page.js.map +1 -0
- package/dist/pages/all.d.ts +5 -1
- package/dist/pages/all.js +5 -1
- package/dist/pages/all.js.map +1 -1
- package/dist/pages/blocks.d.ts +18 -0
- package/dist/pages/blocks.js +56 -0
- package/dist/pages/blocks.js.map +1 -0
- package/dist/pages/funnel/page.d.ts +9 -0
- package/dist/pages/funnel/page.js +36 -0
- package/dist/pages/funnel/page.js.map +1 -0
- package/dist/pages/funnel-builder/page.d.ts +9 -0
- package/dist/pages/funnel-builder/page.js +29 -0
- package/dist/pages/funnel-builder/page.js.map +1 -0
- package/dist/pages/funnels/page.d.ts +1 -1
- package/dist/pages/funnels/page.js +200 -3
- package/dist/pages/funnels/page.js.map +1 -1
- package/dist/pages/index.d.ts +1 -0
- package/dist/pages/index.js +1 -0
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/{campaigns → install}/page.d.ts +3 -3
- package/dist/pages/{campaigns → install}/page.js +14 -12
- package/dist/pages/install/page.js.map +1 -0
- package/dist/pages/module.d.ts +3 -0
- package/dist/pages/module.js +30 -3
- package/dist/pages/module.js.map +1 -1
- package/dist/pages/overview/page.d.ts +1 -1
- package/dist/pages/overview/page.js +122 -3
- package/dist/pages/overview/page.js.map +1 -1
- package/dist/pages/pages/page.d.ts +1 -1
- package/dist/pages/pages/page.js +9 -4
- package/dist/pages/pages/page.js.map +1 -1
- package/dist/pages/quick-actions.d.ts +1 -0
- package/dist/pages/quick-actions.js +25 -0
- package/dist/pages/quick-actions.js.map +1 -0
- package/dist/pages/settings/form.d.ts +2 -1
- package/dist/pages/settings/form.js +62 -20
- package/dist/pages/settings/form.js.map +1 -1
- package/dist/pages/settings/page.d.ts +4 -1
- package/dist/pages/settings/page.js +50 -4
- package/dist/pages/settings/page.js.map +1 -1
- package/dist/pages/websites/page.d.ts +11 -0
- package/dist/pages/websites/page.js +62 -0
- package/dist/pages/websites/page.js.map +1 -0
- package/dist/routes/blocks.d.ts +50 -0
- package/dist/routes/blocks.js +269 -0
- package/dist/routes/blocks.js.map +1 -0
- package/dist/routes/collect.d.ts +1 -2
- package/dist/routes/collect.js +19 -12
- package/dist/routes/collect.js.map +1 -1
- package/dist/routes/context.d.ts +8 -0
- package/dist/routes/context.js +54 -0
- package/dist/routes/context.js.map +1 -0
- package/dist/routes/experiments.js +2 -1
- package/dist/routes/experiments.js.map +1 -1
- package/dist/routes/funnels.d.ts +21 -3
- package/dist/routes/funnels.js +150 -7
- package/dist/routes/funnels.js.map +1 -1
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.js +2 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/settings.d.ts +6 -1
- package/dist/routes/settings.js +65 -4
- package/dist/routes/settings.js.map +1 -1
- package/dist/routes/snapshot.d.ts +1 -1
- package/dist/routes/snapshot.js +24 -25
- package/dist/routes/snapshot.js.map +1 -1
- package/dist/routes/stats.d.ts +14 -23
- package/dist/routes/stats.js +73 -97
- package/dist/routes/stats.js.map +1 -1
- package/dist/routes/tracker.js +6 -4
- package/dist/routes/tracker.js.map +1 -1
- package/dist/routes/websites.d.ts +4 -0
- package/dist/routes/websites.js +111 -12
- package/dist/routes/websites.js.map +1 -1
- package/dist/services/acquisition-report.d.ts +62 -0
- package/dist/services/acquisition-report.js +181 -0
- package/dist/services/acquisition-report.js.map +1 -0
- package/dist/services/acquisition.d.ts +9 -3
- package/dist/services/acquisition.js +35 -5
- package/dist/services/acquisition.js.map +1 -1
- package/dist/services/catalog.d.ts +3 -0
- package/dist/services/catalog.js +17 -0
- package/dist/services/catalog.js.map +1 -0
- package/dist/services/collect.d.ts +1 -2
- package/dist/services/collect.js +49 -24
- package/dist/services/collect.js.map +1 -1
- package/dist/services/composed.d.ts +9 -0
- package/dist/services/composed.js +62 -0
- package/dist/services/composed.js.map +1 -0
- package/dist/services/context.d.ts +20 -0
- package/dist/services/context.js +76 -0
- package/dist/services/context.js.map +1 -0
- package/dist/services/dashboard.d.ts +74 -0
- package/dist/services/dashboard.js +343 -0
- package/dist/services/dashboard.js.map +1 -0
- package/dist/services/experiments/results.d.ts +12 -1
- package/dist/services/experiments/results.js +19 -2
- package/dist/services/experiments/results.js.map +1 -1
- package/dist/services/experiments/stats.d.ts +7 -1
- package/dist/services/experiments/stats.js +43 -5
- package/dist/services/experiments/stats.js.map +1 -1
- package/dist/services/funnel-report.d.ts +64 -0
- package/dist/services/funnel-report.js +280 -0
- package/dist/services/funnel-report.js.map +1 -0
- package/dist/services/funnels/compute.d.ts +2 -0
- package/dist/services/funnels/compute.js +25 -13
- package/dist/services/funnels/compute.js.map +1 -1
- package/dist/services/funnels/results.js +4 -1
- package/dist/services/funnels/results.js.map +1 -1
- package/dist/services/funnels/step-path.d.ts +4 -0
- package/dist/services/funnels/step-path.js +47 -0
- package/dist/services/funnels/step-path.js.map +1 -0
- package/dist/services/funnels-table.d.ts +31 -0
- package/dist/services/funnels-table.js +230 -0
- package/dist/services/funnels-table.js.map +1 -0
- package/dist/services/heatmap.d.ts +15 -3
- package/dist/services/heatmap.js +54 -11
- package/dist/services/heatmap.js.map +1 -1
- package/dist/services/hourly-traffic.d.ts +4 -0
- package/dist/services/hourly-traffic.js +35 -0
- package/dist/services/hourly-traffic.js.map +1 -0
- package/dist/services/origin-guard.d.ts +9 -1
- package/dist/services/origin-guard.js +46 -1
- package/dist/services/origin-guard.js.map +1 -1
- package/dist/services/period.d.ts +26 -1
- package/dist/services/period.js +99 -9
- package/dist/services/period.js.map +1 -1
- package/dist/services/prune.d.ts +6 -0
- package/dist/services/prune.js +35 -0
- package/dist/services/prune.js.map +1 -1
- package/dist/services/request-body.d.ts +1 -2
- package/dist/services/request-body.js +10 -36
- package/dist/services/request-body.js.map +1 -1
- package/dist/services/request-meta.d.ts +0 -1
- package/dist/services/request-meta.js +0 -11
- package/dist/services/request-meta.js.map +1 -1
- package/dist/services/rollups.d.ts +25 -0
- package/dist/services/rollups.js +102 -0
- package/dist/services/rollups.js.map +1 -0
- package/dist/services/sessionize.d.ts +5 -1
- package/dist/services/sessionize.js +52 -10
- package/dist/services/sessionize.js.map +1 -1
- package/dist/services/settings.d.ts +41 -2
- package/dist/services/settings.js +219 -6
- package/dist/services/settings.js.map +1 -1
- package/dist/services/snapshots.d.ts +3 -2
- package/dist/services/snapshots.js +110 -9
- package/dist/services/snapshots.js.map +1 -1
- package/dist/services/tenant-website.js +1 -0
- package/dist/services/tenant-website.js.map +1 -1
- package/dist/services/url.js +12 -4
- package/dist/services/url.js.map +1 -1
- package/dist/services/website-hosts.d.ts +15 -0
- package/dist/services/website-hosts.js +67 -0
- package/dist/services/website-hosts.js.map +1 -0
- package/dist/services/website-report.d.ts +40 -0
- package/dist/services/website-report.js +84 -0
- package/dist/services/website-report.js.map +1 -0
- package/dist/types/constants.d.ts +14 -2
- package/dist/types/constants.js +17 -6
- package/dist/types/constants.js.map +1 -1
- package/dist/types/events.d.ts +221 -68
- package/dist/types/events.js +60 -11
- package/dist/types/events.js.map +1 -1
- package/dist/types/snapshots.d.ts +6 -0
- package/dist/types/snapshots.js +2 -0
- package/dist/types/snapshots.js.map +1 -1
- package/dist/types/statistics.d.ts +2 -1
- package/dist/types/statistics.js +9 -2
- package/dist/types/statistics.js.map +1 -1
- package/dist/utils/experiment-type.js +1 -1
- package/dist/utils/experiment-type.js.map +1 -1
- package/dist/utils/funnel-steps-type.d.ts +21 -3
- package/dist/utils/funnel-steps-type.js +7 -2
- package/dist/utils/funnel-steps-type.js.map +1 -1
- package/frontend-vue/app/components/ChannelsCard.vue +265 -0
- package/frontend-vue/app/components/Context.vue +479 -0
- package/frontend-vue/app/components/ExperimentInput.vue +3 -1
- package/frontend-vue/app/components/ExperimentReport.vue +736 -0
- package/frontend-vue/app/components/FirstRun.vue +60 -0
- package/frontend-vue/app/components/FunnelBuilder.vue +1595 -0
- package/frontend-vue/app/components/FunnelFigure.vue +640 -0
- package/frontend-vue/app/components/FunnelReport.vue +532 -0
- package/frontend-vue/app/components/FunnelStepsInput.vue +3 -1
- package/frontend-vue/app/components/FunnelTemplates.vue +161 -0
- package/frontend-vue/app/components/HeatmapCanvas.vue +11 -4
- package/frontend-vue/app/components/InstallGuide.vue +753 -0
- package/frontend-vue/app/components/PagePreview.vue +889 -416
- package/frontend-vue/app/components/PagesExplorer.vue +552 -0
- package/frontend-vue/app/components/ScrollDepthOverlay.vue +48 -20
- package/frontend-vue/app/components/TopListTabs.vue +317 -0
- package/frontend-vue/app/components/TrackerSnippet.vue +50 -16
- package/frontend-vue/app/components/VariationsInput.vue +15 -5
- package/frontend-vue/app/components/WebsitesGrid.vue +799 -0
- package/frontend-vue/app/composables/useContextLink.ts +16 -0
- package/frontend-vue/app/composables/useExperiment.ts +1 -2
- package/frontend-vue/app/composables/useFunnelSteps.ts +1 -2
- package/frontend-vue/app/composables/useLatestRequest.ts +2 -4
- package/frontend-vue/app/composables/useMarketingApi.ts +298 -133
- package/frontend-vue/app/composables/useMarketingContext.ts +55 -0
- package/frontend-vue/app/composables/useMarketingRoutes.ts +80 -27
- package/frontend-vue/app/composables/useSnapshotFrame.ts +26 -4
- package/frontend-vue/app/constants.ts +22 -0
- package/frontend-vue/app/plugins/funnel-steps-data-type.client.ts +2 -1
- package/frontend-vue/app/utils/channels.ts +37 -0
- package/frontend-vue/app/utils/format.ts +132 -0
- package/frontend-vue/app/utils/funnel-draft.ts +264 -0
- package/frontend-vue/app/utils/heatmap.ts +82 -0
- package/frontend-vue/app/utils/heatmapPalette.ts +10 -0
- package/frontend-vue/app/utils/host-conflict.ts +48 -0
- package/frontend-vue/app/utils/sample-size.ts +17 -0
- package/frontend-vue/app/utils/snapshotViewportUnits.ts +15 -5
- package/frontend-vue/app/utils/snippet.ts +53 -0
- package/frontend-vue/app/utils/step-path.ts +32 -0
- package/frontend-vue/dms.frontend.ts +4 -3
- package/frontend-vue/i18n/locales/marketing-en-GB.json +1083 -316
- package/frontend-vue/i18n/locales/marketing-fr-FR.json +1087 -320
- package/frontend-vue/package.json +3 -0
- package/frontend-vue/tests/drafts.test.ts +3 -3
- package/frontend-vue/tests/format.test.ts +73 -0
- package/frontend-vue/tests/funnel-draft.test.ts +194 -0
- package/frontend-vue/tests/heatmap.test.ts +32 -0
- package/frontend-vue/tests/i18n.test.ts +87 -4
- package/frontend-vue/tests/latest-request.test.ts +6 -3
- package/frontend-vue/tests/locale-typography.test.ts +115 -0
- package/frontend-vue/tests/registration.test.ts +21 -11
- package/frontend-vue/tests/routes.test.ts +67 -7
- package/frontend-vue/tests/server-parity.test.ts +77 -0
- package/frontend-vue/tests/stubs/frontend-module.ts +18 -3
- package/package.json +9 -7
- package/static/snapshot.js +46 -16
- package/static/tracker.js +139 -48
- package/dist/pages/campaigns/page.js.map +0 -1
- package/frontend-vue/app/components/CampaignsView.vue +0 -330
- package/frontend-vue/app/components/FunnelDetail.vue +0 -1026
- package/frontend-vue/app/components/FunnelsView.vue +0 -493
- package/frontend-vue/app/components/NoWebsiteState.vue +0 -18
- package/frontend-vue/app/components/OverviewView.vue +0 -493
- package/frontend-vue/app/components/PagesView.vue +0 -391
- package/frontend-vue/app/components/TextKpiCard.vue +0 -28
- package/frontend-vue/app/components/TopListTabsCard.vue +0 -162
- package/frontend-vue/app/composables/useChildId.ts +0 -13
- package/frontend-vue/app/composables/useMarketingPeriod.ts +0 -65
- package/frontend-vue/app/composables/useMarketingWebsiteForm.ts +0 -69
- package/frontend-vue/app/composables/useMarketingWebsites.ts +0 -80
- package/frontend-vue/app/utils/dmsComponent.ts +0 -18
- package/frontend-vue/tests/period.test.ts +0 -35
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useDmsRoute } from '#dms/frontend-module'
|
|
2
|
+
import { PERIOD_SCOPE } from '../constants'
|
|
3
|
+
import { withWebsite } from './useMarketingRoutes'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A link out of the current page, in its context: the website the context
|
|
7
|
+
* bar keeps in the page URL ({@link withWebsite}) and the period selector's
|
|
8
|
+
* keys (the DMS `withPeriodQuery`), what the link names itself winning.
|
|
9
|
+
*/
|
|
10
|
+
export function useContextLink(): (link: string) => string {
|
|
11
|
+
const route = useDmsRoute()
|
|
12
|
+
return (link) => {
|
|
13
|
+
const query = route.query as Record<string, unknown>
|
|
14
|
+
return withPeriodQuery(withWebsite(link, query), query, PERIOD_SCOPE)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -67,16 +67,14 @@ export function useLatestRequest<T>(
|
|
|
67
67
|
return
|
|
68
68
|
}
|
|
69
69
|
data.value = result
|
|
70
|
-
}
|
|
71
|
-
catch (cause) {
|
|
70
|
+
} catch (cause) {
|
|
72
71
|
if (request !== latest) {
|
|
73
72
|
return
|
|
74
73
|
}
|
|
75
74
|
failed.value = true
|
|
76
75
|
error.value = cause
|
|
77
76
|
data.value = null
|
|
78
|
-
}
|
|
79
|
-
finally {
|
|
77
|
+
} finally {
|
|
80
78
|
// A superseded run owns none of the shared state — not even the flag
|
|
81
79
|
// that says the newest one is still in flight.
|
|
82
80
|
if (request === latest) {
|
|
@@ -6,8 +6,71 @@
|
|
|
6
6
|
* convention as dms-api's useApiIntrospection).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type {
|
|
10
|
+
ContextWebsite,
|
|
11
|
+
MarketingContextPayload,
|
|
12
|
+
} from './useMarketingContext'
|
|
13
|
+
|
|
9
14
|
// --- Shapes mirrored from the backend ---------------------------------------
|
|
10
15
|
|
|
16
|
+
export type {
|
|
17
|
+
ContextWebsite,
|
|
18
|
+
MarketingContextPayload,
|
|
19
|
+
} from './useMarketingContext'
|
|
20
|
+
|
|
21
|
+
export interface MarketingRejectedSource {
|
|
22
|
+
hostname: string
|
|
23
|
+
count: number
|
|
24
|
+
lastAt: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** One card of the Websites page (GET /api/marketing/websites/summary). */
|
|
28
|
+
export interface MarketingWebsiteCard extends ContextWebsite {
|
|
29
|
+
extraDomains: string[]
|
|
30
|
+
snapshotsEnabled: boolean
|
|
31
|
+
snapshotMaskText: boolean
|
|
32
|
+
createdAt: number
|
|
33
|
+
/** Over the last 30 days, like `pages` (distinct paths). */
|
|
34
|
+
sessions: number
|
|
35
|
+
pages: number
|
|
36
|
+
funnels: number
|
|
37
|
+
snapshots: number
|
|
38
|
+
sampleRate: number
|
|
39
|
+
rejected: MarketingRejectedSource[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** What deleting a website discards (GET /websites/:id/deletion). */
|
|
43
|
+
export interface MarketingWebsiteDeletionImpact {
|
|
44
|
+
/** Every stored day, not a window. */
|
|
45
|
+
sessions: number
|
|
46
|
+
pageviews: number
|
|
47
|
+
/** Oldest stored day; null when nothing was recorded. */
|
|
48
|
+
since: number | null
|
|
49
|
+
funnels: number
|
|
50
|
+
snapshots: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface MarketingConnectionStatus {
|
|
54
|
+
website: ContextWebsite
|
|
55
|
+
createdAt: number
|
|
56
|
+
/** Server time of the check: the `since` of a later "check again". */
|
|
57
|
+
checkedAt: number
|
|
58
|
+
/** Newest pageview since `since` (the site's creation by default). */
|
|
59
|
+
visit: MarketingConnectionVisit | null
|
|
60
|
+
lastActivityAt: number | null
|
|
61
|
+
rejected: MarketingRejectedSource[]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface MarketingFunnelSuggestion {
|
|
65
|
+
value: string
|
|
66
|
+
count: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface MarketingFunnelSuggestions {
|
|
70
|
+
pages: MarketingFunnelSuggestion[]
|
|
71
|
+
events: MarketingFunnelSuggestion[]
|
|
72
|
+
}
|
|
73
|
+
|
|
11
74
|
export interface MarketingWebsite {
|
|
12
75
|
_id: string
|
|
13
76
|
name: string
|
|
@@ -16,13 +79,38 @@ export interface MarketingWebsite {
|
|
|
16
79
|
/** Opt-in: page snapshots store page content. */
|
|
17
80
|
snapshotsEnabled: boolean
|
|
18
81
|
snapshotMaskText?: boolean
|
|
82
|
+
extraDomains?: string[]
|
|
19
83
|
}
|
|
20
84
|
|
|
21
85
|
/** Editable website fields; only the keys present are touched. */
|
|
22
86
|
export type MarketingWebsitePatch = Partial<
|
|
23
|
-
Pick<
|
|
87
|
+
Pick<
|
|
88
|
+
MarketingWebsite,
|
|
89
|
+
| 'name'
|
|
90
|
+
| 'domain'
|
|
91
|
+
| 'extraDomains'
|
|
92
|
+
| 'trackingEnabled'
|
|
93
|
+
| 'snapshotsEnabled'
|
|
94
|
+
| 'snapshotMaskText'
|
|
95
|
+
>
|
|
24
96
|
>
|
|
25
97
|
|
|
98
|
+
export interface MarketingWebsiteInput {
|
|
99
|
+
name: string
|
|
100
|
+
domain: string
|
|
101
|
+
extraDomains?: string[]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface MarketingConnectionVisit {
|
|
105
|
+
at: number
|
|
106
|
+
/** Path only. */
|
|
107
|
+
url: string
|
|
108
|
+
/** The host the beacons came from, when the backend saw it. */
|
|
109
|
+
host?: string
|
|
110
|
+
browser?: string
|
|
111
|
+
deviceType?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
26
114
|
export type MarketingSnapshotLayout = 'desktop' | 'tablet' | 'phone'
|
|
27
115
|
|
|
28
116
|
/**
|
|
@@ -35,8 +123,8 @@ export interface MarketingPageSnapshot {
|
|
|
35
123
|
exact: boolean
|
|
36
124
|
capturedAt: number
|
|
37
125
|
origin: string
|
|
38
|
-
viewport: { width: number
|
|
39
|
-
document: { width: number
|
|
126
|
+
viewport: { width: number; height: number }
|
|
127
|
+
document: { width: number; height: number }
|
|
40
128
|
colorScheme?: 'light' | 'dark'
|
|
41
129
|
bytes: number
|
|
42
130
|
html: string | null
|
|
@@ -51,104 +139,40 @@ export interface MarketingPageSnapshotResponse {
|
|
|
51
139
|
export interface MarketingPageEntry {
|
|
52
140
|
path: string
|
|
53
141
|
pageviews: number
|
|
142
|
+
clicks: number
|
|
143
|
+
/** Page loads sampled for clicks and scrolls, as counted. */
|
|
144
|
+
sampled: number
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The period a read covers: the DMS period scope's state (`usePeriodScope`),
|
|
149
|
+
* appended to the URL as its blocks do. Null reads the routes' default.
|
|
150
|
+
*/
|
|
151
|
+
export type MarketingPeriod = Parameters<typeof appendPeriodToUrl>[1]
|
|
152
|
+
|
|
153
|
+
export type MarketingPagesSort = 'views' | 'clicks'
|
|
154
|
+
|
|
155
|
+
/** Clicks and scrolls (`events`) and captures of a page or a whole site. */
|
|
156
|
+
export interface MarketingHeatmapData {
|
|
157
|
+
events: number
|
|
158
|
+
snapshots: number
|
|
54
159
|
}
|
|
55
160
|
|
|
56
161
|
export interface MarketingPages {
|
|
57
162
|
pages: MarketingPageEntry[]
|
|
163
|
+
/** Paths matching the search, listed or not. */
|
|
164
|
+
total: number
|
|
58
165
|
/** The inventory hit its cap; narrow the search to see the rest. */
|
|
59
166
|
truncated: boolean
|
|
60
|
-
/**
|
|
61
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Measured share of page loads sampled for clicks — what makes a heatmap
|
|
169
|
+
* sparse. Null while the site has too few pageviews to tell.
|
|
170
|
+
*/
|
|
171
|
+
sampleRate: number | null
|
|
62
172
|
/** Global collection switch; off means nothing new is being recorded. */
|
|
63
173
|
trackerEnabled: boolean
|
|
64
174
|
}
|
|
65
175
|
|
|
66
|
-
/** The five-way acquisition grouping the backend derives from referrer
|
|
67
|
-
* domain + utm_medium at rollup time. */
|
|
68
|
-
export type MarketingChannel = 'direct' | 'organic' | 'social' | 'referral' | 'paid'
|
|
69
|
-
|
|
70
|
-
export interface MarketingDayStatistics {
|
|
71
|
-
day: number
|
|
72
|
-
pageviews: number
|
|
73
|
-
sessions: number
|
|
74
|
-
newVisitors: number
|
|
75
|
-
customEvents: number
|
|
76
|
-
/** Counted as transitions: a day un-bouncing sessions opened the day
|
|
77
|
-
* before can go negative. Clamp before showing a rate. */
|
|
78
|
-
bouncedSessions: number
|
|
79
|
-
sessionDurationMs: number
|
|
80
|
-
topPages: Record<string, number>
|
|
81
|
-
topEntryPages?: Record<string, number>
|
|
82
|
-
topExitPages?: Record<string, number>
|
|
83
|
-
topReferrers: Record<string, number>
|
|
84
|
-
topUtmSources: Record<string, number>
|
|
85
|
-
topUtmMediums?: Record<string, number>
|
|
86
|
-
topUtmCampaigns?: Record<string, number>
|
|
87
|
-
topUtmTerms?: Record<string, number>
|
|
88
|
-
topUtmContents?: Record<string, number>
|
|
89
|
-
topChannels?: Record<string, number>
|
|
90
|
-
topDevices?: Record<string, number>
|
|
91
|
-
topBrowsers?: Record<string, number>
|
|
92
|
-
topCountries?: Record<string, number>
|
|
93
|
-
topEvents?: Record<string, number>
|
|
94
|
-
topLanguages?: Record<string, number>
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface MarketingOverviewTotals {
|
|
98
|
-
pageviews: number
|
|
99
|
-
sessions: number
|
|
100
|
-
newVisitors: number
|
|
101
|
-
customEvents: number
|
|
102
|
-
bouncedSessions: number
|
|
103
|
-
sessionDurationMs: number
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface MarketingOverview {
|
|
107
|
-
totals: MarketingOverviewTotals
|
|
108
|
-
days: MarketingDayStatistics[]
|
|
109
|
-
topPages: Record<string, number>
|
|
110
|
-
topEntryPages: Record<string, number>
|
|
111
|
-
topExitPages: Record<string, number>
|
|
112
|
-
topReferrers: Record<string, number>
|
|
113
|
-
topUtmSources: Record<string, number>
|
|
114
|
-
topUtmMediums: Record<string, number>
|
|
115
|
-
topUtmCampaigns: Record<string, number>
|
|
116
|
-
topUtmTerms: Record<string, number>
|
|
117
|
-
topUtmContents: Record<string, number>
|
|
118
|
-
topChannels: Record<string, number>
|
|
119
|
-
topDevices: Record<string, number>
|
|
120
|
-
topBrowsers: Record<string, number>
|
|
121
|
-
/** ISO 3166-1 alpha-2 keys; only populated when the deployment configures
|
|
122
|
-
* a GeoIP database — `geoipEnabled` says whether the card should show. */
|
|
123
|
-
topCountries: Record<string, number>
|
|
124
|
-
/** Custom event names by trigger count — which CTA gets clicked. */
|
|
125
|
-
topEvents: Record<string, number>
|
|
126
|
-
/** BCP 47 tags as the visitors' browsers report them (`fr`, `fr-FR`). */
|
|
127
|
-
topLanguages: Record<string, number>
|
|
128
|
-
geoipEnabled: boolean
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** One (source, medium, campaign) triple observed on new sessions; a blank
|
|
132
|
-
* slot is a UTM parameter the landing URL did not carry. */
|
|
133
|
-
export interface MarketingCampaignEntry {
|
|
134
|
-
source?: string
|
|
135
|
-
medium?: string
|
|
136
|
-
campaign?: string
|
|
137
|
-
sessions: number
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface MarketingCampaigns {
|
|
141
|
-
/** Exhaustive five-key split, not a top list. */
|
|
142
|
-
channels: Partial<Record<MarketingChannel, number>>
|
|
143
|
-
campaigns: MarketingCampaignEntry[]
|
|
144
|
-
/** The table hit its cap; narrow the search to see the rest. */
|
|
145
|
-
truncated: boolean
|
|
146
|
-
terms: Record<string, number>
|
|
147
|
-
contents: Record<string, number>
|
|
148
|
-
/** Referrer domains across all channels (search and social included). */
|
|
149
|
-
referrers?: Record<string, number>
|
|
150
|
-
}
|
|
151
|
-
|
|
152
176
|
export interface MarketingFunnelStep {
|
|
153
177
|
kind: 'url' | 'custom'
|
|
154
178
|
value: string
|
|
@@ -195,7 +219,7 @@ export interface MarketingFunnel {
|
|
|
195
219
|
* list's foreign join — the write path wants the bare id again. */
|
|
196
220
|
export interface MarketingFunnelListItem {
|
|
197
221
|
_id: string
|
|
198
|
-
websiteId: string | { _id: string
|
|
222
|
+
websiteId: string | { _id: string; name?: string; domain?: string }
|
|
199
223
|
name: string
|
|
200
224
|
steps: MarketingFunnelStep[] | string
|
|
201
225
|
conversionWindowHours: number
|
|
@@ -218,10 +242,15 @@ export interface MarketingFunnelStepResult {
|
|
|
218
242
|
step: MarketingFunnelStep
|
|
219
243
|
sessions: number
|
|
220
244
|
conversionRate: number
|
|
245
|
+
/** Events of the window matching the step, in order or not: 0 means the
|
|
246
|
+
* site never sent it. */
|
|
247
|
+
matches: number
|
|
221
248
|
}
|
|
222
249
|
|
|
223
250
|
export interface MarketingFunnelComputation {
|
|
224
251
|
totalSessions: number
|
|
252
|
+
/** Events read: 0 means the site sent nothing over the window. */
|
|
253
|
+
events: number
|
|
225
254
|
steps: MarketingFunnelStepResult[]
|
|
226
255
|
}
|
|
227
256
|
|
|
@@ -233,8 +262,13 @@ export interface MarketingVariationResult {
|
|
|
233
262
|
exposedSessions: number
|
|
234
263
|
computation: MarketingFunnelComputation
|
|
235
264
|
conversionRate: number
|
|
265
|
+
/** Exposed sessions that reached the goal. */
|
|
266
|
+
conversions: number
|
|
236
267
|
/** Relative to the control's conversion; null on the control itself. */
|
|
237
268
|
uplift: number | null
|
|
269
|
+
/** Bounds of `uplift` at the test's level; null wherever pValue is. */
|
|
270
|
+
upliftLow: number | null
|
|
271
|
+
upliftHigh: number | null
|
|
238
272
|
/** Two-sided p-value vs the control; null on the control, when the sample
|
|
239
273
|
* is too small, or when the window is truncated. */
|
|
240
274
|
pValue: number | null
|
|
@@ -253,8 +287,44 @@ export interface MarketingExperimentSrm {
|
|
|
253
287
|
export interface MarketingExperimentResults {
|
|
254
288
|
/** Key of the control arm — the experiment's first variation. */
|
|
255
289
|
control: string
|
|
290
|
+
/** Level each challenger is tested at: .05, split past two arms. */
|
|
291
|
+
alpha: number
|
|
256
292
|
variations: MarketingVariationResult[]
|
|
257
293
|
srm: MarketingExperimentSrm
|
|
294
|
+
/** A verdict waits for a full week and a conversions floor per arm. */
|
|
295
|
+
decision: {
|
|
296
|
+
days: number
|
|
297
|
+
minDays: number
|
|
298
|
+
minConversions: number
|
|
299
|
+
ready: boolean
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** What raw events cover of the period and its comparison. */
|
|
304
|
+
export interface MarketingFunnelCoverage {
|
|
305
|
+
/** First instant covered (epoch ms), null without any raw event. */
|
|
306
|
+
since: number | null
|
|
307
|
+
/** The period starts before it. */
|
|
308
|
+
period: boolean
|
|
309
|
+
/** The comparison starts before it: `previous` is left out. */
|
|
310
|
+
previous: boolean
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** A funnel the site's own journeys suggest, for the first-run state. */
|
|
314
|
+
export interface MarketingFunnelTemplate {
|
|
315
|
+
steps: MarketingFunnelStep[]
|
|
316
|
+
/** Sessions of the period that matched step 1. */
|
|
317
|
+
entering: number
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** What a started test says, read once by the server for the report, its
|
|
321
|
+
* stop dialog and the list: `early` is significant but short of the decision
|
|
322
|
+
* rule, `withheld` voided by a warning (truncated read, SRM, nothing scored). */
|
|
323
|
+
export interface MarketingExperimentVerdict {
|
|
324
|
+
kind: 'winner' | 'loser' | 'flat' | 'early' | 'withheld'
|
|
325
|
+
/** Index of the arm the verdict names, the control being 0; null when no
|
|
326
|
+
* challenger could be scored. */
|
|
327
|
+
leader: number | null
|
|
258
328
|
}
|
|
259
329
|
|
|
260
330
|
export interface MarketingFunnelResults {
|
|
@@ -265,10 +335,21 @@ export interface MarketingFunnelResults {
|
|
|
265
335
|
experiment?: MarketingExperimentResults
|
|
266
336
|
/** The raw-event window hit its cap; verdicts are withheld. */
|
|
267
337
|
truncated: boolean
|
|
338
|
+
/** Null on a funnel without a started experiment. */
|
|
339
|
+
verdict?: MarketingExperimentVerdict | null
|
|
268
340
|
/** Epoch ms bounds the numbers cover: the selected period on a plain
|
|
269
341
|
* funnel, the experiment's own run once one is started — a running split
|
|
270
342
|
* ends at "now", a stopped one at its stop. */
|
|
271
|
-
window: { since: number
|
|
343
|
+
window: { since: number; until: number }
|
|
344
|
+
/** The same funnel over the comparison window; null on a split, without
|
|
345
|
+
* one, or when raw events do not cover it. */
|
|
346
|
+
previous?: MarketingFunnelComputation | null
|
|
347
|
+
coverage?: MarketingFunnelCoverage
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface MarketingFunnelPreview {
|
|
351
|
+
computation: MarketingFunnelComputation
|
|
352
|
+
truncated: boolean
|
|
272
353
|
}
|
|
273
354
|
|
|
274
355
|
/**
|
|
@@ -300,10 +381,9 @@ export interface MarketingHeatmap {
|
|
|
300
381
|
}
|
|
301
382
|
|
|
302
383
|
/**
|
|
303
|
-
* Scroll-depth distribution of one path.
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* scrolled", never "of all views".
|
|
384
|
+
* Scroll-depth distribution of one path. Every sampled view reports one
|
|
385
|
+
* depth, scrolled or not, so `views` is the number of sampled views (events
|
|
386
|
+
* stored before the tracker did so only cover views that scrolled).
|
|
307
387
|
*/
|
|
308
388
|
export interface MarketingScrollDepth {
|
|
309
389
|
views: number
|
|
@@ -314,6 +394,10 @@ export interface MarketingScrollDepth {
|
|
|
314
394
|
|
|
315
395
|
// --- Facade -----------------------------------------------------------------
|
|
316
396
|
|
|
397
|
+
function periodUrl(url: string, period: MarketingPeriod): string {
|
|
398
|
+
return appendPeriodToUrl(url, period) ?? url
|
|
399
|
+
}
|
|
400
|
+
|
|
317
401
|
/** Far above any real tenant's funnel count — the data-api list answers 10
|
|
318
402
|
* rows unless told otherwise, and caps at 100 whatever is asked. */
|
|
319
403
|
const FUNNEL_LIST_LIMIT = 100
|
|
@@ -322,12 +406,76 @@ export function useMarketingApi() {
|
|
|
322
406
|
const { $authFetch } = useAuthFetch()
|
|
323
407
|
|
|
324
408
|
return {
|
|
325
|
-
listWebsites: () =>
|
|
409
|
+
listWebsites: () =>
|
|
410
|
+
$authFetch<MarketingWebsite[]>('/api/marketing/websites'),
|
|
326
411
|
|
|
327
|
-
|
|
412
|
+
getContext: () =>
|
|
413
|
+
$authFetch<MarketingContextPayload>('/api/marketing/context'),
|
|
414
|
+
|
|
415
|
+
createWebsite: (input: MarketingWebsiteInput) =>
|
|
328
416
|
$authFetch<MarketingWebsite>('/api/marketing/websites', {
|
|
329
417
|
method: 'POST',
|
|
330
|
-
body:
|
|
418
|
+
body: input,
|
|
419
|
+
}),
|
|
420
|
+
|
|
421
|
+
selectContextWebsite: (website: string) =>
|
|
422
|
+
$authFetch<MarketingContextPayload>('/api/marketing/context', {
|
|
423
|
+
method: 'PUT',
|
|
424
|
+
body: { website },
|
|
425
|
+
}),
|
|
426
|
+
|
|
427
|
+
listWebsiteCards: () =>
|
|
428
|
+
$authFetch<MarketingWebsiteCard[]>('/api/marketing/websites/summary'),
|
|
429
|
+
|
|
430
|
+
// `since` (epoch ms, server clock): only a pageview from then on counts.
|
|
431
|
+
getConnection: (website: string, since?: number) =>
|
|
432
|
+
$authFetch<MarketingConnectionStatus>(
|
|
433
|
+
`/api/marketing/websites/${encodeURIComponent(website)}/connection`,
|
|
434
|
+
{ query: { since } },
|
|
435
|
+
),
|
|
436
|
+
|
|
437
|
+
getWebsiteDeletionImpact: (website: string) =>
|
|
438
|
+
$authFetch<MarketingWebsiteDeletionImpact>(
|
|
439
|
+
`/api/marketing/websites/${encodeURIComponent(website)}/deletion`,
|
|
440
|
+
),
|
|
441
|
+
|
|
442
|
+
allowHost: (website: string, hostname: string) =>
|
|
443
|
+
$authFetch<MarketingWebsite>(
|
|
444
|
+
`/api/marketing/websites/${encodeURIComponent(website)}/allow-host`,
|
|
445
|
+
{ method: 'POST', body: { hostname } },
|
|
446
|
+
),
|
|
447
|
+
|
|
448
|
+
deleteWebsite: (website: string) =>
|
|
449
|
+
$authFetch<{ deleted: string }>(
|
|
450
|
+
`/api/marketing/websites/${encodeURIComponent(website)}`,
|
|
451
|
+
{ method: 'DELETE' },
|
|
452
|
+
),
|
|
453
|
+
|
|
454
|
+
getFunnelTemplates: (website: string, period: MarketingPeriod) =>
|
|
455
|
+
$authFetch<MarketingFunnelTemplate[]>(
|
|
456
|
+
periodUrl('/api/marketing/funnels/templates', period),
|
|
457
|
+
{
|
|
458
|
+
query: { website },
|
|
459
|
+
},
|
|
460
|
+
),
|
|
461
|
+
|
|
462
|
+
getFunnelSuggestions: (website: string) =>
|
|
463
|
+
$authFetch<MarketingFunnelSuggestions>(
|
|
464
|
+
'/api/marketing/funnels/suggestions',
|
|
465
|
+
{
|
|
466
|
+
query: { website },
|
|
467
|
+
},
|
|
468
|
+
),
|
|
469
|
+
|
|
470
|
+
previewFunnel: (
|
|
471
|
+
website: string,
|
|
472
|
+
steps: MarketingFunnelStep[],
|
|
473
|
+
conversionWindowHours: number,
|
|
474
|
+
) =>
|
|
475
|
+
$authFetch<MarketingFunnelPreview>('/api/marketing/funnels/preview', {
|
|
476
|
+
method: 'POST',
|
|
477
|
+
query: { website },
|
|
478
|
+
body: { steps, conversionWindowHours },
|
|
331
479
|
}),
|
|
332
480
|
|
|
333
481
|
updateWebsite: (id: string, patch: MarketingWebsitePatch) =>
|
|
@@ -337,55 +485,66 @@ export function useMarketingApi() {
|
|
|
337
485
|
),
|
|
338
486
|
|
|
339
487
|
// Query strings via ofetch's `query` option — encoding is its job.
|
|
340
|
-
listPages: (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
$authFetch<
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
query: { website, period, search },
|
|
353
|
-
}),
|
|
488
|
+
listPages: (
|
|
489
|
+
website: string,
|
|
490
|
+
period: MarketingPeriod,
|
|
491
|
+
search = '',
|
|
492
|
+
sort: MarketingPagesSort = 'views',
|
|
493
|
+
) =>
|
|
494
|
+
$authFetch<MarketingPages>(
|
|
495
|
+
periodUrl('/api/marketing/stats/pages', period),
|
|
496
|
+
{
|
|
497
|
+
query: { website, search, sort },
|
|
498
|
+
},
|
|
499
|
+
),
|
|
354
500
|
|
|
355
|
-
getHeatmap: (website: string, path: string, period
|
|
356
|
-
$authFetch<MarketingHeatmap>(
|
|
357
|
-
|
|
358
|
-
|
|
501
|
+
getHeatmap: (website: string, path: string, period: MarketingPeriod) =>
|
|
502
|
+
$authFetch<MarketingHeatmap>(
|
|
503
|
+
periodUrl('/api/marketing/stats/heatmap', period),
|
|
504
|
+
{
|
|
505
|
+
query: { website, path },
|
|
506
|
+
},
|
|
507
|
+
),
|
|
359
508
|
|
|
360
|
-
getScrollDepth: (website: string, path: string, period
|
|
361
|
-
$authFetch<MarketingScrollDepth>(
|
|
362
|
-
|
|
363
|
-
|
|
509
|
+
getScrollDepth: (website: string, path: string, period: MarketingPeriod) =>
|
|
510
|
+
$authFetch<MarketingScrollDepth>(
|
|
511
|
+
periodUrl('/api/marketing/stats/scroll', period),
|
|
512
|
+
{
|
|
513
|
+
query: { website, path },
|
|
514
|
+
},
|
|
515
|
+
),
|
|
364
516
|
|
|
365
|
-
getSnapshot: (
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
517
|
+
getSnapshot: (
|
|
518
|
+
website: string,
|
|
519
|
+
path: string,
|
|
520
|
+
layout: MarketingSnapshotLayout,
|
|
521
|
+
) =>
|
|
522
|
+
$authFetch<MarketingPageSnapshotResponse>(
|
|
523
|
+
'/api/marketing/stats/snapshot',
|
|
524
|
+
{
|
|
525
|
+
query: { website, path, layout },
|
|
526
|
+
},
|
|
527
|
+
),
|
|
369
528
|
|
|
370
529
|
// Whole click and scroll history, every period: without `path` the whole
|
|
371
530
|
// site's.
|
|
372
531
|
resetHeatmap: (website: string, path?: string) =>
|
|
373
|
-
$authFetch<
|
|
532
|
+
$authFetch<MarketingHeatmapData>('/api/marketing/stats/heatmap', {
|
|
374
533
|
method: 'DELETE',
|
|
375
534
|
query: { website, path },
|
|
376
535
|
}),
|
|
377
536
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
),
|
|
537
|
+
/** What `resetHeatmap` would delete with the same arguments. */
|
|
538
|
+
countHeatmap: (website: string, path?: string) =>
|
|
539
|
+
$authFetch<MarketingHeatmapData>('/api/marketing/stats/heatmap/count', {
|
|
540
|
+
query: { website, path },
|
|
541
|
+
}),
|
|
383
542
|
|
|
384
543
|
// Definitions CRUD over the funnels data-api routes. `is` is the DMS
|
|
385
544
|
// DataType compare mode — the data-api's own `eq` parses but matches
|
|
386
545
|
// nothing. The list defaults to 10 rows, hence the explicit limit.
|
|
387
546
|
listFunnels: (website: string) =>
|
|
388
|
-
$authFetch<{ results: MarketingFunnelListItem[]
|
|
547
|
+
$authFetch<{ results: MarketingFunnelListItem[]; total: number }>(
|
|
389
548
|
'/api/marketing/tables/funnels/list',
|
|
390
549
|
{
|
|
391
550
|
query: {
|
|
@@ -397,6 +556,12 @@ export function useMarketingApi() {
|
|
|
397
556
|
},
|
|
398
557
|
),
|
|
399
558
|
|
|
559
|
+
// One definition, as the builder edits it: no results computed.
|
|
560
|
+
getFunnel: (id: string) =>
|
|
561
|
+
$authFetch<MarketingFunnelListItem>('/api/marketing/tables/funnels/get', {
|
|
562
|
+
query: { id },
|
|
563
|
+
}),
|
|
564
|
+
|
|
400
565
|
// The insert answers the generated keys, an array — not the row.
|
|
401
566
|
createFunnel: (input: MarketingFunnelInput) =>
|
|
402
567
|
$authFetch<string[]>('/api/marketing/tables/funnels/new', {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
inject,
|
|
4
|
+
type ComputedRef,
|
|
5
|
+
type InjectionKey,
|
|
6
|
+
type Ref,
|
|
7
|
+
} from 'vue'
|
|
8
|
+
|
|
9
|
+
export type WebsiteState = 'live' | 'waiting' | 'paused'
|
|
10
|
+
|
|
11
|
+
export interface ContextWebsite {
|
|
12
|
+
id: string
|
|
13
|
+
name: string
|
|
14
|
+
domain: string
|
|
15
|
+
state: WebsiteState
|
|
16
|
+
lastActivityAt: number | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MarketingContextPayload {
|
|
20
|
+
websites: ContextWebsite[]
|
|
21
|
+
selectedId: string | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** What the context container hands the blocks it wraps. */
|
|
25
|
+
export interface MarketingContextState {
|
|
26
|
+
websites: ComputedRef<ContextWebsite[]>
|
|
27
|
+
selected: ComputedRef<ContextWebsite | null>
|
|
28
|
+
/** Bumped by the bar's refresh button. */
|
|
29
|
+
refreshToken: Ref<number>
|
|
30
|
+
select: (id: string) => Promise<void>
|
|
31
|
+
reload: () => Promise<void>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const MARKETING_CONTEXT_KEY: InjectionKey<MarketingContextState> =
|
|
35
|
+
Symbol('dms-marketing-context')
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The context of the page the block sits on. Outside a context container (a
|
|
39
|
+
* block dropped on another page) the website is unknown and the routes fall
|
|
40
|
+
* back to the caller's own selection. The period is the DMS period scope's
|
|
41
|
+
* (`usePeriodScope`), which the bar's selector publishes.
|
|
42
|
+
*/
|
|
43
|
+
export function useMarketingContext(): MarketingContextState {
|
|
44
|
+
return inject(MARKETING_CONTEXT_KEY, () => detachedContext(), true)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function detachedContext(): MarketingContextState {
|
|
48
|
+
return {
|
|
49
|
+
websites: computed(() => []),
|
|
50
|
+
selected: computed(() => null),
|
|
51
|
+
refreshToken: { value: 0 } as Ref<number>,
|
|
52
|
+
select: async () => {},
|
|
53
|
+
reload: async () => {},
|
|
54
|
+
}
|
|
55
|
+
}
|