diff --git a/package.json b/package.json index 8a72b6de..a7c54108 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adex-interface", - "version": "0.69.71", + "version": "0.69.73", "private": true, "dependencies": { "@ambire/login-sdk-core": "^0.0.21", @@ -36,7 +36,7 @@ "@visx/scale": "^3.3.0", "@visx/xychart": "^3.3.0", "@visx/zoom": "^3.3.0", - "adex-common": "github:AmbireTech/adex-common#eb2d0bd0c10ef2baed38391d59dec5055646bce4", + "adex-common": "github:AmbireTech/adex-common#119affb121179d181a9e4dc219121dc7a68cac04", "blockies-ts": "^1.0.0", "d3-geo": "^3.1.0", "dayjs": "^1.11.10", diff --git a/src/components/AdminPanel/AdminPanel.tsx b/src/components/AdminPanel/AdminPanel.tsx index d31bf23f..6e1b2683 100644 --- a/src/components/AdminPanel/AdminPanel.tsx +++ b/src/components/AdminPanel/AdminPanel.tsx @@ -7,6 +7,7 @@ import Invoices from 'components/Billing/Invoices' import AdminAnalytics from './AdminAnalytics' import Accounts from './Accounts' import SSPsAnalytics from './SSPsAnalytics' +import DspStats from './DspStats' // import { AccountDetails } from './AccountDetails' const AdminPanel = () => { @@ -28,6 +29,7 @@ const AdminPanel = () => { Invoices Validator Analytics SSPs Analytics + DSP info Accounts Account details @@ -51,6 +53,10 @@ const AdminPanel = () => { + + + + diff --git a/src/components/AdminPanel/DspStats.tsx b/src/components/AdminPanel/DspStats.tsx new file mode 100644 index 00000000..89453014 --- /dev/null +++ b/src/components/AdminPanel/DspStats.tsx @@ -0,0 +1,120 @@ +import { NumberFormatter, Stack, SimpleGrid, Fieldset } from '@mantine/core' +import CustomTable from 'components/common/CustomTable' +import DetailsRow from 'components/common/DetailsRow' +import useAdmin from 'hooks/useAdmin' +import { useEffect } from 'react' +import { BaseDSPStats, SSPQPSStats } from 'types/dspStats' + +const getPercent = (total: number, part: number): string => { + return ` (${((part / total) * 100).toFixed(2)}%) ` +} + +const BaseStats = ({ dspStats }: { dspStats: BaseDSPStats }) => { + return ( + + } + /> + + + {getPercent(dspStats.totalRequests, dspStats.ortbRequests)} + + + } + /> + + + {getPercent(dspStats.ortbRequests, dspStats.throttledRequests)} + + + } + /> + + + {getPercent(dspStats.ortbRequests, dspStats.bidRequestsWithNoBids)} + + + } + /> + + {getPercent(dspStats.ortbRequests, dspStats.bidRequestsBidsInTime)} + + + } + /> + + + {getPercent(dspStats.ortbRequests, dspStats.bidRequestsWithBidsLate)} + + + } + /> + + ) +} + +const DspStats = () => { + const { dspStats, getDspStats } = useAdmin() + + useEffect(() => { + getDspStats() + }, [getDspStats]) + + return ( + +
+ +
+ +
+ {dspStats.last24h && } +
+
+ + } + /> + + } + /> + +
+
+ ({ + id: ssp.name, + columns: [ + { value: ssp.name }, + { value: ssp.qpsConfig }, + { value: ssp.qpsCurrent }, + { value: ssp.qpsDropped } + ] + }))} + /> +
+
+ ) +} + +export default DspStats diff --git a/src/components/AdminPanel/SSPsAnalytics.tsx b/src/components/AdminPanel/SSPsAnalytics.tsx index 27b4bb15..c2a39f10 100644 --- a/src/components/AdminPanel/SSPsAnalytics.tsx +++ b/src/components/AdminPanel/SSPsAnalytics.tsx @@ -248,7 +248,7 @@ const SSPsAnalytics = ({ size="sm" /> - + { {isAdminPanel && ( - { } /> )} - - - + + { } /> {/* TODO: Add data for it */} - {/* TODO: Add data for it */} - { /> } /> - - { : 'N/A' } /> - { : 'N/A' } /> - { ) } /> - { ) } /> - { : 'No' } /> - - - { inputValues={campaign.targetingInput.inputs.location} selectData={COUNTRIES} /> + {campaign.targetingInput.inputs.ssp && ( + + )} @@ -443,7 +450,7 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { {campaign.adUnits.map((item: AdUnit, index: number) => { const isLast = index === campaign.adUnits.length - 1 return ( - void }) => { @@ -29,61 +30,55 @@ const CampaignSummary = ({ onLaunchClick }: { onLaunchClick: () => void }) => { priceBoundsFormatted, formattedCats, formattedLocs, + formattedSSPs, adFormats, campaignBudgetFormatted, advancedTargeInput, estimatedImpressions } = useCreateCampaignData() + const { isAdmin } = useAccount() + const loading = useMemo(() => !allowedBannerSizes.length, [allowedBannerSizes.length]) return ( - - - + + {placement === 'site' && ( - + )} - - - - + + + {isAdmin && } + - - - void }) => { noBorder mb="xs" /> - + {step === 3 ? ( diff --git a/src/components/common/CampainDetailsRow/index.ts b/src/components/common/CampainDetailsRow/index.ts deleted file mode 100644 index 61ca631e..00000000 --- a/src/components/common/CampainDetailsRow/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import CampaignDetailsRow from './CampaignDetailsRow' - -export default CampaignDetailsRow diff --git a/src/components/common/CampainDetailsRow/CampaignDetailsRow.tsx b/src/components/common/DetailsRow/DetailsRow.tsx similarity index 86% rename from src/components/common/CampainDetailsRow/CampaignDetailsRow.tsx rename to src/components/common/DetailsRow/DetailsRow.tsx index e341c396..812394f8 100644 --- a/src/components/common/CampainDetailsRow/CampaignDetailsRow.tsx +++ b/src/components/common/DetailsRow/DetailsRow.tsx @@ -1,6 +1,6 @@ import { Divider, Flex, Text, FlexProps, MantineFontSize } from '@mantine/core' -export type CampaignDetailsRowProps = FlexProps & { +export type DetailsRowProps = FlexProps & { title: string value: any | undefined lighterColor?: boolean | undefined @@ -9,7 +9,7 @@ export type CampaignDetailsRowProps = FlexProps & { column?: boolean } -const CampaignDetailsRow = ({ +const DetailsRow = ({ title, value, lighterColor, @@ -17,7 +17,7 @@ const CampaignDetailsRow = ({ noBorder = false, column = false, ...flexProps -}: CampaignDetailsRowProps) => { +}: DetailsRowProps) => { return ( <> getAllAccounts: () => void + dspStats: DspStats + getDspStats: () => void initialDataLoading: boolean makeTransfer: ( values: AdminTransfer, @@ -20,12 +23,33 @@ interface IAdminContext { ) => Promise } +const dspStatsDefault: DspStats = { + totalRequests: 0, + ortbRequests: 0, + throttledRequests: 0, + ortbRequestsPerSecond: 0, + throttledRequestsPerSecond: 0, + bidRequestsWithNoBids: 0, + bidRequestsBidsInTime: 0, + bidRequestsWithBidsLate: 0, + last24h: { + totalRequests: 0, + ortbRequests: 0, + throttledRequests: 0, + bidRequestsWithNoBids: 0, + bidRequestsBidsInTime: 0, + bidRequestsWithBidsLate: 0 + }, + ssp: [] +} + const AdminContext = createContext(null) const AdminProvider: FC = ({ children }) => { const { adexServicesRequest } = useAdExApi() const [initialDataLoading, setLoading] = useState(true) const [accounts, setAccounts] = useState>(new Map()) + const [dspStats, setDspStats] = useState(dspStatsDefault) const getAllAccounts = useCallback(async () => { try { @@ -131,15 +155,43 @@ const AdminProvider: FC = ({ children }) => { [adexServicesRequest] ) + const getDspStats = useCallback(async () => { + try { + const res = await adexServicesRequest('backend', { + route: '/dsp/admin/sysinfo', + method: 'GET', + headers: { + 'content-type': 'application/json' + } + }) + + if (res) { + setDspStats(res) + } + } catch (err) { + console.log({ err }) + } + }, [adexServicesRequest]) + const contextValue = useMemo( () => ({ accounts, getAllAccounts, + getDspStats, + dspStats, initialDataLoading, makeTransfer, updateAccountInfo }), - [accounts, initialDataLoading, makeTransfer, getAllAccounts, updateAccountInfo] + [ + accounts, + getAllAccounts, + getDspStats, + dspStats, + initialDataLoading, + makeTransfer, + updateAccountInfo + ] ) return {children} diff --git a/src/contexts/CampaignsContext/CampaignsDataContext.tsx b/src/contexts/CampaignsContext/CampaignsDataContext.tsx index 68c3de2b..9b706210 100644 --- a/src/contexts/CampaignsContext/CampaignsDataContext.tsx +++ b/src/contexts/CampaignsContext/CampaignsDataContext.tsx @@ -350,6 +350,7 @@ const CampaignsDataProvider: FC ...(inputs?.categories && { categories: inputs.categories }), + ...(inputs?.ssp && { ssp: inputs.ssp }), ...(inputs?.publishers && { publishers: inputs.publishers }), diff --git a/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx b/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx index ad3ffe9b..04e214a6 100644 --- a/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx +++ b/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx @@ -180,6 +180,18 @@ const CreateCampaignContextProvider: FC = ({ children }) => { } } }, + ssp: (rule) => { + if (!rule) return + if (!isAdmin) return 'Invalid filter - available only for admins' + if (step === 1) { + if (rule.apply === 'in' && !rule.in.length) { + return 'Ssp list cannot be empty' + } + if (rule.apply === 'nin' && !rule.nin.length) { + return 'Ssp list cannot be empty' + } + } + }, advanced: { limitDailyAverageSpending: (value) => step === 2 && typeof value !== 'boolean' ? 'Invalid value' : undefined diff --git a/src/hooks/useCreateCampaignData/useCreateCampaignData.tsx b/src/hooks/useCreateCampaignData/useCreateCampaignData.tsx index 66790772..910a709b 100644 --- a/src/hooks/useCreateCampaignData/useCreateCampaignData.tsx +++ b/src/hooks/useCreateCampaignData/useCreateCampaignData.tsx @@ -5,7 +5,7 @@ import useCreateCampaignContext from 'hooks/useCreateCampaignContext' import DesktopIcon from 'resources/icons/Desktop' import MobileIcon from 'resources/icons/Mobile' import { AdUnit } from 'adex-common/dist/types' -import { CATEGORIES, COUNTRIES } from 'constants/createCampaign' +import { CATEGORIES, COUNTRIES, SSPs } from 'constants/createCampaign' import ImageUrlInput from 'components/CreateCampaign/StepOne/ImageUrlInput' import RangeText from 'components/common/RangeText' import dayjs from 'dayjs' @@ -20,6 +20,7 @@ const useCreateCampaignData = () => { advanced, location, categories, + ssp, placements: { in: [placement] } @@ -104,6 +105,11 @@ const useCreateCampaignData = () => { [location] ) + const formattedSSPs = useMemo( + () => ssp && , + [ssp] + ) + const sizes = useMemo( () => adUnits.map((adUnit: AdUnit) => `${adUnit.banner?.format.w}x${adUnit.banner?.format.h}`), [adUnits] @@ -157,6 +163,7 @@ const useCreateCampaignData = () => { priceBoundsFormatted, formattedCats, formattedLocs, + formattedSSPs, adFormats, advancedTargeInput: advanced, campaignBudgetFormatted, diff --git a/src/types/dspStats.ts b/src/types/dspStats.ts new file mode 100644 index 00000000..f2be0fc5 --- /dev/null +++ b/src/types/dspStats.ts @@ -0,0 +1,21 @@ +export type SSPQPSStats = { + name: string + qpsConfig?: number + qpsCurrent: number + qpsDropped: number +} + +export type BaseDSPStats = { + totalRequests: number + ortbRequests: number + throttledRequests: number + bidRequestsWithNoBids: number + bidRequestsBidsInTime: number + bidRequestsWithBidsLate: number +} +export type DspStats = BaseDSPStats & { + ortbRequestsPerSecond: number + throttledRequestsPerSecond: number + last24h: BaseDSPStats + ssp: SSPQPSStats[] +} diff --git a/yarn.lock b/yarn.lock index 73a5b16e..2f35705b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3190,9 +3190,9 @@ address@^1.0.1, address@^1.1.2: resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== -"adex-common@github:AmbireTech/adex-common#eb2d0bd0c10ef2baed38391d59dec5055646bce4": +"adex-common@github:AmbireTech/adex-common#119affb121179d181a9e4dc219121dc7a68cac04": version "1.0.1" - resolved "https://codeload.github.com/AmbireTech/adex-common/tar.gz/eb2d0bd0c10ef2baed38391d59dec5055646bce4" + resolved "https://codeload.github.com/AmbireTech/adex-common/tar.gz/119affb121179d181a9e4dc219121dc7a68cac04" dependencies: i18n-iso-countries "^7.11.0"