Skip to content

Commit 6feedfa

Browse files
authored
Merge pull request #305 from AmbireTech/advanced-stats
wip: Advanced stats
2 parents f5a375a + 4bbe656 commit 6feedfa

File tree

10 files changed

+430
-10
lines changed

10 files changed

+430
-10
lines changed

src/Router.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import useAccount from 'hooks/useAccount'
1212
import Deposit from 'components/Deposit'
1313
import CreateCampaign from 'components/CreateCampaign'
1414
import { CreateCampaignContextProvider } from 'contexts/CreateCampaignContext/CreateCampaignContext'
15-
import { CampaignsDataProvider, CampaignsAnalyticsProvider } from 'contexts/CampaignsContext'
15+
import {
16+
CampaignsDataProvider,
17+
CampaignsAnalyticsProvider,
18+
SSPsAnalyticsProvider
19+
} from 'contexts/CampaignsContext'
1620
import NotFound404 from 'components/404/404'
1721
// import AdminPanel from './admin/Admin'
1822
import { AccountDetails } from 'components/AdminPanel/AccountDetails'
@@ -78,9 +82,11 @@ export const router = createBrowserRouter(
7882
<RequireAuth>
7983
<CampaignsDataProvider type="user">
8084
<CampaignsAnalyticsProvider>
81-
<CreateCampaignContextProvider>
82-
<UserPanel />
83-
</CreateCampaignContextProvider>
85+
<SSPsAnalyticsProvider>
86+
<CreateCampaignContextProvider>
87+
<UserPanel />
88+
</CreateCampaignContextProvider>
89+
</SSPsAnalyticsProvider>
8490
</CampaignsAnalyticsProvider>
8591
</CampaignsDataProvider>
8692
</RequireAuth>

src/components/AdminPanel/AdminPanel.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { StickyPanel } from 'components/TopBar/TopBarStickyPanel'
66
import Invoices from 'components/Billing/Invoices'
77
import AdminAnalytics from './AdminAnalytics'
88
import Accounts from './Accounts'
9+
import SSPsAnalytics from './SSPsAnalytics'
910
import { SspStats } from './SspStats'
1011
// import { AccountDetails } from './AccountDetails'
1112

@@ -29,6 +30,7 @@ const AdminPanel = () => {
2930
<Tabs.Tab value="campaigns">All Campaigns</Tabs.Tab>
3031
<Tabs.Tab value="invoices">Invoices</Tabs.Tab>
3132
<Tabs.Tab value="validatorAnalytics">Validator Analytics</Tabs.Tab>
33+
<Tabs.Tab value="sspAnalytics">SSPs Analytics</Tabs.Tab>
3234
<Tabs.Tab value="sspStats">SSP stats</Tabs.Tab>
3335
<Tabs.Tab value="accounts">Accounts</Tabs.Tab>
3436
<Tabs.Tab value="user-account" disabled>
@@ -49,6 +51,10 @@ const AdminPanel = () => {
4951
<AdminAnalytics />
5052
</Tabs.Panel>
5153

54+
<Tabs.Panel value="sspAnalytics" pt="xs">
55+
<SSPsAnalytics />
56+
</Tabs.Panel>
57+
5258
<Tabs.Panel value="sspStats" pt="xs">
5359
<SspStats />
5460
</Tabs.Panel>
+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { useEffect, useState, useMemo } from 'react'
2+
import { Select, Stack, Group, Badge, Text, Loader, Code, NumberFormatter } from '@mantine/core'
3+
import { SSPs, RequestStatPlacement, SSPsAnalyticsDataQuery } from 'types'
4+
import useSSPsAnalytics from 'hooks/useCampaignAnalytics/useSSPsAnalytics'
5+
import CustomTable, { DataElement } from 'components/common/CustomTable'
6+
import { removeOptionalEmptyStringProps } from 'helpers'
7+
import DownloadCSV from 'components/common/DownloadCSV'
8+
9+
const sspsData: Array<{ value: SSPs | ''; label: string }> = [
10+
{ value: '', label: 'All SSPs' },
11+
{ value: 'Eskimi', label: 'Eskimi' },
12+
{ value: 'Epom', label: 'Epom' },
13+
{ value: 'Qortex', label: 'Qortex' }
14+
]
15+
16+
const placementsData: Array<{ value: string; label: string }> = [
17+
{ value: '', label: 'All placements' },
18+
{ value: RequestStatPlacement.app.toString(), label: 'App' },
19+
{ value: RequestStatPlacement.siteMobile.toString(), label: 'Site - Mobile' },
20+
{ value: RequestStatPlacement.siteDesktop.toString(), label: 'Site - Desktop' },
21+
{ value: RequestStatPlacement.other.toString(), label: 'Site - other' }
22+
]
23+
24+
const groupByData: Array<{ value: string; label: string }> = [
25+
{ value: 'bidfloor', label: 'bid Floor' },
26+
{ value: 'date', label: 'date' },
27+
{ value: 'category', label: 'category' },
28+
{ value: 'placement', label: 'placement' },
29+
{ value: 'country', label: 'country' },
30+
{ value: 'ssp', label: 'ssp' },
31+
{ value: 'format', label: 'format' }
32+
]
33+
34+
const SSPsAnalytics = ({
35+
country,
36+
category,
37+
format
38+
}: {
39+
category?: SSPsAnalyticsDataQuery['category']
40+
country?: SSPsAnalyticsDataQuery['country']
41+
format?: string[]
42+
}) => {
43+
const [analyticsKey, setAnalyticsKey] = useState<
44+
| {
45+
key: string
46+
}
47+
| undefined
48+
>()
49+
50+
const [ssp, setSsp] = useState<SSPs>('')
51+
const [groupBy, setGrop] = useState<SSPsAnalyticsDataQuery['groupBy']>('country')
52+
const [placement, setPlacement] = useState<RequestStatPlacement | ''>('')
53+
const { analyticsData, getAnalyticsKeyAndUpdate } = useSSPsAnalytics()
54+
55+
const analytics = useMemo(
56+
() => analyticsData.get(analyticsKey?.key || ''),
57+
[analyticsData, analyticsKey]
58+
)
59+
60+
useEffect(() => {
61+
console.log({ analytics })
62+
}, [analytics])
63+
64+
useEffect(() => {
65+
setAnalyticsKey(undefined)
66+
67+
const checkAnalytics = async () => {
68+
const key = await getAnalyticsKeyAndUpdate({
69+
...removeOptionalEmptyStringProps({
70+
ssp,
71+
placement,
72+
category,
73+
country,
74+
format
75+
}),
76+
groupBy
77+
})
78+
setAnalyticsKey(key)
79+
console.log('key', key)
80+
}
81+
82+
checkAnalytics()
83+
}, [category, country, format, getAnalyticsKeyAndUpdate, groupBy, placement, ssp])
84+
85+
const loading = useMemo(() => analytics?.status === 'loading', [analytics])
86+
87+
const data: { elements: DataElement[]; totalRequests: number } = useMemo(() => {
88+
return {
89+
elements:
90+
analytics?.data.map(({ count, value }) => {
91+
return {
92+
id: value.toString() + count.toString(),
93+
columns: [
94+
{ value: value.toString(), label: value.toString() },
95+
{ value: count, element: <NumberFormatter value={count} thousandSeparator /> }
96+
]
97+
}
98+
}) || [],
99+
totalRequests: analytics?.data.reduce((sum, i) => sum + i.count, 0) || 0
100+
}
101+
}, [analytics])
102+
103+
return (
104+
<Stack gap="xs">
105+
<Text size="sm" inline c="purple">
106+
* This analytics are for the actual processed request from our SSRs (oRtb: BidRequest) for
107+
the <strong>48 hours</strong>
108+
</Text>
109+
<Group align="start" justify="left" gap="xs">
110+
<Select
111+
label="Group by"
112+
value={groupBy}
113+
onChange={(val) => setGrop(val as SSPsAnalyticsDataQuery['groupBy'])}
114+
data={groupByData}
115+
size="md"
116+
/>
117+
<Select
118+
label="SSP"
119+
value={ssp}
120+
onChange={(val) => setSsp(val as SSPs)}
121+
data={sspsData}
122+
size="md"
123+
/>
124+
<Select
125+
label="Placement"
126+
value={placement?.toString()}
127+
// @ts-ignore
128+
onChange={(val) => setPlacement(val !== '' ? Number(val) : val)}
129+
data={placementsData}
130+
size="md"
131+
/>
132+
</Group>
133+
<Group align="center" justify="left" gap="xs" pos="relative">
134+
<Badge size="lg" leftSection="Total requests">
135+
{loading ? <Loader type="dots" color="white" /> : data.totalRequests.toLocaleString()}
136+
</Badge>
137+
138+
<DownloadCSV
139+
// TODO: fix anal type
140+
// @ts-ignore
141+
data={analytics?.data?.map((x) => ({
142+
value: x.count,
143+
segment: x.value.toString()
144+
}))}
145+
// mapHeadersToDataProperties={{ [analType]: 'segment', ...csvHeaders }}
146+
filename={`${analyticsKey?.key || 'admin-data-export'}.csv`}
147+
// disabled={loading}
148+
disabled
149+
/>
150+
</Group>
151+
<Stack>
152+
<CustomTable
153+
pageSize={10}
154+
headings={[groupBy?.toString() || 'data', 'count']}
155+
data={data.elements}
156+
loading={loading}
157+
/>
158+
<Code block>
159+
{JSON.stringify({ ssp, placement, category, country, format, groupBy }, null, 2)}
160+
</Code>
161+
</Stack>
162+
</Stack>
163+
)
164+
}
165+
166+
export default SSPsAnalytics

src/components/CampaignAnalytics/CampaignAnalytics.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import GoBack from 'components/common/GoBack/GoBack'
77
import { StickyPanel } from 'components/TopBar/TopBarStickyPanel'
88
import { AdminBadge } from 'components/common/AdminBadge'
99
import { useCampaignsData } from 'hooks/useCampaignsData'
10+
import SSPsAnalytics from 'components/AdminPanel/SSPsAnalytics'
11+
import { IabTaxonomyV3 } from 'adex-common'
1012
import Placements from './Placements'
1113
import Creatives from './Creatives'
1214
import SSPs from './SSPs'
@@ -70,6 +72,7 @@ const CampaignAnalytics = ({ isAdminPanel = false }: { isAdminPanel?: boolean })
7072
<Tabs.Tab value="country">REGIONS</Tabs.Tab>
7173
<Tabs.Tab value="adUnit">CREATIVES</Tabs.Tab>
7274
{isAdminPanel && <Tabs.Tab value="ssp">SSPs</Tabs.Tab>}
75+
{isAdminPanel && <Tabs.Tab value="sspAnalytics">SSPs request analytics</Tabs.Tab>}
7376
</Tabs.List>
7477
</Flex>
7578
<Tabs.Panel value="timeframe">
@@ -87,6 +90,35 @@ const CampaignAnalytics = ({ isAdminPanel = false }: { isAdminPanel?: boolean })
8790
<Tabs.Panel value="ssp">
8891
<SSPs campaignId={id} forAdmin={isAdminPanel} />
8992
</Tabs.Panel>
93+
<Tabs.Panel value="sspAnalytics">
94+
<SSPsAnalytics
95+
category={{
96+
values:
97+
campaign?.targetingInput.inputs.categories.apply === 'all'
98+
? []
99+
: (campaign?.targetingInput.inputs.categories[
100+
campaign?.targetingInput.inputs.categories.apply
101+
] as IabTaxonomyV3[]),
102+
operator:
103+
campaign?.targetingInput.inputs.categories.apply === 'all'
104+
? undefined
105+
: campaign?.targetingInput.inputs.categories.apply
106+
}}
107+
country={{
108+
values:
109+
campaign?.targetingInput.inputs.location.apply === 'all'
110+
? []
111+
: campaign?.targetingInput.inputs.location[
112+
campaign?.targetingInput.inputs.location.apply
113+
],
114+
operator:
115+
campaign?.targetingInput.inputs.location.apply === 'all'
116+
? undefined
117+
: campaign?.targetingInput.inputs.location.apply
118+
}}
119+
format={campaign?.adUnits.map((x) => `${x.banner?.format.h}x${x.banner?.format.w}`)}
120+
/>
121+
</Tabs.Panel>
90122
</Tabs>
91123
</Container>
92124
)

src/components/CampaignAnalytics/SSPs.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ const SSPs = ({ forAdmin, campaignId }: { forAdmin: boolean; campaignId: string
2727
[campaignMappedAnalytics, currencyName]
2828
)
2929

30-
if (!campaignMappedAnalytics?.length) {
31-
return <div>No placement found</div>
32-
}
3330
return <CustomTable headings={headings} data={elements} error={error} loading={loading} />
3431
}
3532

src/contexts/CampaignsContext/CampaignsAnalyticsContext.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,10 @@ const CampaignsAnalyticsProvider: FC<PropsWithChildren> = ({ children }) => {
202202

203203
setAnalyticsData((prev) => {
204204
const next = new Map(prev)
205-
const nextAggr: { status: DataStatus; data: AnalyticsData[] } =
206-
{ status: 'processed', data: analyticsDataRes?.aggr } || prev.get(dataKey)
205+
const nextAggr: { status: DataStatus; data: AnalyticsData[] } = {
206+
status: 'processed',
207+
data: analyticsDataRes?.aggr || prev.get(dataKey)?.data
208+
}
207209
next.set(dataKey, nextAggr)
208210
return next
209211
})

0 commit comments

Comments
 (0)