From 42defcad7c1b62b2d473eaa6c5361d60189836b7 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Fri, 26 Jul 2024 16:15:39 +0300 Subject: [PATCH 01/13] show only archived when show archived is pressed --- src/components/Dashboard/Dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index f11e53b7..dbd8a236 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -95,7 +95,7 @@ const Dashboard = ({ isAdminPanel, accountId }: { isAdminPanel?: boolean; accoun const isArchived = x.campaign.archived if (isArchived) archivedCount++ - return matchFilter && (!isArchived || showArchived) + return matchFilter && ((!showArchived && !isArchived) || (showArchived && isArchived)) }) .sort((a, b) => { const statusOrderDiff = From 6ff789b593896107916ed265e2cec13bfc0a7981 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 10:31:22 +0300 Subject: [PATCH 02/13] remove redundant campaign update at details actions --- .../CampaignDetails/CampaignDetails.tsx | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/components/CampaignDetails/CampaignDetails.tsx b/src/components/CampaignDetails/CampaignDetails.tsx index a07a3f6d..52266e32 100644 --- a/src/components/CampaignDetails/CampaignDetails.tsx +++ b/src/components/CampaignDetails/CampaignDetails.tsx @@ -140,18 +140,10 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { confirmProps: { color: campaign?.archived ? 'blue' : 'red' }, onConfirm: () => { toggleArchived(campaign?.id || '') - updateCampaignDataById(campaign?.id) showNotification('info', `Campaign ${campaign?.archived ? 'Unarchived' : 'Archived'}`) } }) - }, [ - campaign?.archived, - campaign?.id, - campaign?.title, - showNotification, - toggleArchived, - updateCampaignDataById - ]) + }, [campaign?.archived, campaign?.id, campaign?.title, showNotification, toggleArchived]) const handleStopOrDelete = useCallback(() => { if (!campaign?.id || !campaign?.status) { @@ -169,7 +161,6 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { } : () => { changeCampaignStatus(CampaignStatus.closedByUser, campaign.id) - updateCampaignDataById(campaign?.id) showNotification('info', 'Campaign stopped!') } @@ -192,8 +183,7 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { changeCampaignStatus, deleteDraftCampaign, navigate, - showNotification, - updateCampaignDataById + showNotification ]) useEffect(() => { @@ -202,8 +192,6 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { } }, [id, updateCampaignDataById]) - const onAfterEditSubmit = () => updateCampaignDataById(id) - const canArchive = useMemo(() => { return ( !isEditMode && @@ -347,7 +335,7 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { {isEditMode ? ( - + ) : ( {isAdminPanel && } From 864c2b92db790461657072264b41f4be428816b2 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 10:33:14 +0300 Subject: [PATCH 03/13] fix dashboard edit action check --- src/components/Dashboard/Dashboard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index b5e4553f..5e12cbe7 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -236,7 +236,7 @@ const Dashboard = ({ isAdminPanel, accountId }: { isAdminPanel?: boolean; accoun const handleEdit = useCallback( (data: DashboardTableElement['actionData']) => - navigate(`/dashboard/campaign-details/${data.campaign.id}?edit=true`), + navigate(`/dashboard/campaign-details/${data.campaign.id}?edit=true`, { replace: true }), [navigate] ) @@ -325,7 +325,7 @@ const Dashboard = ({ isAdminPanel, accountId }: { isAdminPanel?: boolean; accoun { action: handleEdit, disabled: (ada: DashboardTableElement['actionData']) => - ada.campaign.status === CampaignStatus.closedByUser, + ![CampaignStatus.active, CampaignStatus.paused].includes(ada.campaign.status), label: 'Edit', icon: , hide: (ada: DashboardTableElement['actionData']) => ada.isDraft From 1a4eb0439c164772c6e287bfa2962fb97a98ed9e Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 10:58:52 +0300 Subject: [PATCH 04/13] wip: fixing and cleanup campaign edit --- src/components/EditCampaign/EditCampaign.tsx | 127 +++++++++---------- 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/src/components/EditCampaign/EditCampaign.tsx b/src/components/EditCampaign/EditCampaign.tsx index 6764f9a7..188bbc8d 100644 --- a/src/components/EditCampaign/EditCampaign.tsx +++ b/src/components/EditCampaign/EditCampaign.tsx @@ -17,7 +17,6 @@ import { TargetingInputApplyProp, TargetingInputSingle } from 'adex-common' -import { CustomConfirmModal } from 'components/common/Modals' import MultiSelectAndRadioButtons from 'components/CreateCampaign/StepTwo/MultiSelectAndRadioButtons' import { CAT_GROUPS, CATEGORIES, COUNTRIES, REGION_GROUPS } from 'constants/createCampaign' import { parseBigNumTokenAmountToDecimal, parseToBigNumPrecision } from 'helpers' @@ -29,13 +28,16 @@ import { import useAccount from 'hooks/useAccount' import { useAdExApi } from 'hooks/useAdexServices' import useCustomNotifications from 'hooks/useCustomNotifications' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo } from 'react' +import { useCampaignsData } from 'hooks/useCampaignsData' import type { unstable_Blocker as Blocker, unstable_BlockerFunction as BlockerFunction } from 'react-router-dom' import { unstable_useBlocker as useBlocker } from 'react-router-dom' import InfoFilledIcon from 'resources/icons/InfoFilled' +import throttle from 'lodash.throttle' +import { CustomConfirmModalBody } from 'components/common/Modals/CustomConfirmModal/CustomConfirmModalBody' type TargetingInputEdit = { version: string @@ -54,19 +56,13 @@ type FormProps = { targetingInput: TargetingInputEdit } -const EditCampaign = ({ - campaign, - onAfterSubmit -}: { - campaign: Campaign - onAfterSubmit?: () => void -}) => { +const EditCampaign = ({ campaign }: { campaign: Campaign }) => { const { adexServicesRequest } = useAdExApi() const { showNotification } = useCustomNotifications() const { adexAccount: { balanceToken } } = useAccount() - const [openedModal, setOpenedModal] = useState(false) + const { updateCampaignDataById } = useCampaignsData() const recommendedPaymentBounds = { min: '0.10', max: '0.5' } @@ -81,17 +77,6 @@ const EditCampaign = ({ const blockerProceed = useCallback(() => blocker.proceed?.(), [blocker]) - const handleConfirmBtnClicked = useCallback(async () => { - modals.closeAll() - blockerProceed() - }, [blockerProceed]) - - const handleCancelBtnClicked = useCallback(() => { - modals.closeAll() - blocker.reset?.() - setOpenedModal(false) - }, [blocker]) - const form = useForm({ initialValues: { pricingBounds: { @@ -145,24 +130,20 @@ const EditCampaign = ({ targetingInput: { inputs: { location: ({ apply, in: isin, nin }) => { - if (apply === 'in') { - if (!isin.length) return 'Countries list cannot be empty' - return null + if (apply === 'in' && !isin.length) { + return 'Countries list cannot be empty' } - if (apply === 'nin') { - if (!nin.length) return 'Countries list cannot be empty' - return null + if (apply === 'nin' && !nin.length) { + return 'Countries list cannot be empty' } return null }, categories: ({ apply, in: isin, nin }) => { - if (apply === 'in') { - if (!isin.length) return 'Categories list cannot be empty' - return null + if (apply === 'in' && !isin.length) { + return 'Categories list cannot be empty' } - if (apply === 'nin') { - if (!nin.length) return 'Categories list cannot be empty' - return null + if (apply === 'nin' && !nin.length) { + return 'Categories list cannot be empty' } return null }, @@ -180,10 +161,20 @@ const EditCampaign = ({ useEffect(() => { if (blocker.state === 'blocked' && form.isDirty()) { - setOpenedModal(true) - return + return modals.openConfirmModal({ + title: 'Unsaved changes!', + children: ( + + ), + labels: { confirm: 'Leave the page', cancel: 'Cancel' }, + confirmProps: { color: 'warning' }, + onConfirm: () => { + console.log('confirm') + blockerProceed() + }, + onAbort: () => blocker.reset?.() + }) } - blockerProceed() }, [blocker, form, blockerProceed]) const catSelectedRadioAndValuesArray = useMemo( @@ -204,7 +195,9 @@ const EditCampaign = ({ ) form.validateField('targetingInput.inputs.categories') }, - [form] + // TODO: fic this and countries update + // eslint-disable-next-line react-hooks/exhaustive-deps + [] ) const handleCountries = useCallback( @@ -215,11 +208,12 @@ const EditCampaign = ({ ) form.validateField('targetingInput.inputs.location') }, - [form] + // eslint-disable-next-line react-hooks/exhaustive-deps + [] ) const editCampaign = useCallback( - (values: FormProps) => { + async (values: FormProps) => { const impression = { min: Number( parseToBigNumPrecision( @@ -256,28 +250,40 @@ const EditCampaign = ({ } } - return adexServicesRequest('backend', { - route: `/dsp/campaigns/edit/${campaign.id}`, - method: 'PUT', - body, - headers: { - 'Content-Type': 'application/json' - } - }) - .then(() => { - form.resetDirty() - showNotification('info', 'Successfully updated Campaign data!') - onAfterSubmit && onAfterSubmit() + try { + await adexServicesRequest('backend', { + route: `/dsp/campaigns/edit/${campaign.id}`, + method: 'PUT', + body, + headers: { + 'Content-Type': 'application/json' + } }) - .catch(() => showNotification('error', "Couldn't update the Campaign data!")) + form.resetDirty() + showNotification('info', 'Successfully updated Campaign data!') + updateCampaignDataById(campaign.id) + } catch { + return showNotification('error', "Couldn't update the Campaign data!") + } }, - [balanceToken.decimals, adexServicesRequest, campaign.id, form, showNotification, onAfterSubmit] + [ + balanceToken.decimals, + adexServicesRequest, + campaign.id, + form, + showNotification, + updateCampaignDataById + ] ) + const throttledSbm = useMemo(() => { + return throttle(editCampaign, 3000, { leading: true }) + }, [editCampaign]) + if (!campaign) return
Invalid Campaign ID
return ( -
+ @@ -385,19 +391,6 @@ const EditCampaign = ({ - - You did not save your changes. Are you sure you want to leave this page? - - } - opened={openedModal} - />
) } From 0decf246d1f90bb9261e020baa14a3ab5584e95d Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 11:53:52 +0300 Subject: [PATCH 05/13] fix campaign details action btns --- .../CampaignDetails/CampaignDetails.tsx | 165 +++++++++--------- src/components/common/GoBack/GoBack.tsx | 5 +- 2 files changed, 84 insertions(+), 86 deletions(-) diff --git a/src/components/CampaignDetails/CampaignDetails.tsx b/src/components/CampaignDetails/CampaignDetails.tsx index 52266e32..120b68d1 100644 --- a/src/components/CampaignDetails/CampaignDetails.tsx +++ b/src/components/CampaignDetails/CampaignDetails.tsx @@ -232,105 +232,102 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { return ( <> - - - - - + + + + + - + + + + {campaign.status === CampaignStatus.draft ? ( - - {campaign.status === CampaignStatus.draft ? ( - - ) : ( - - )} - + ) : ( - - - - Campaign Analytics - } - action={() => navigate(`/dashboard/campaign-analytics/${campaign.id}`)} - /> + )} + + - + + + Campaign Analytics + } + action={() => navigate(`/dashboard/campaign-analytics/${campaign.id}`)} + /> + diff --git a/src/components/common/GoBack/GoBack.tsx b/src/components/common/GoBack/GoBack.tsx index 676c7284..2cfe7a46 100644 --- a/src/components/common/GoBack/GoBack.tsx +++ b/src/components/common/GoBack/GoBack.tsx @@ -24,11 +24,12 @@ const useStyles = createStyles((theme) => ({ const GoBack = ({ title, fixed, + path, children -}: { title: string; fixed?: boolean } & PropsWithChildren) => { +}: { title: string; fixed?: boolean; path?: string } & PropsWithChildren) => { const { classes, cx } = useStyles() const navigate = useNavigate() - const handleClick = () => navigate(-1) + const handleClick = () => (path ? navigate(path, { replace: true }) : navigate(-1)) return ( From 9f687d7155a9c2d765744edc02252783baacf15d Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 11:54:12 +0300 Subject: [PATCH 06/13] fix campaign edit blocker --- src/components/EditCampaign/EditCampaign.tsx | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/EditCampaign/EditCampaign.tsx b/src/components/EditCampaign/EditCampaign.tsx index 188bbc8d..8a3fa6f0 100644 --- a/src/components/EditCampaign/EditCampaign.tsx +++ b/src/components/EditCampaign/EditCampaign.tsx @@ -66,17 +66,6 @@ const EditCampaign = ({ campaign }: { campaign: Campaign }) => { const recommendedPaymentBounds = { min: '0.10', max: '0.5' } - const blocker: Blocker = useBlocker( - useCallback( - ({ currentLocation, nextLocation }) => - currentLocation.pathname !== nextLocation.pathname || - currentLocation.search !== nextLocation.search, - [] - ) - ) - - const blockerProceed = useCallback(() => blocker.proceed?.(), [blocker]) - const form = useForm({ initialValues: { pricingBounds: { @@ -159,8 +148,18 @@ const EditCampaign = ({ campaign }: { campaign: Campaign }) => { } }) + const shouldBlock = useCallback( + ({ currentLocation, nextLocation }) => + form.isDirty() && + (currentLocation.pathname !== nextLocation.pathname || + currentLocation.search !== nextLocation.search), + [form] + ) + + const blocker: Blocker = useBlocker(shouldBlock) + useEffect(() => { - if (blocker.state === 'blocked' && form.isDirty()) { + if (blocker.state === 'blocked') { return modals.openConfirmModal({ title: 'Unsaved changes!', children: ( @@ -169,13 +168,14 @@ const EditCampaign = ({ campaign }: { campaign: Campaign }) => { labels: { confirm: 'Leave the page', cancel: 'Cancel' }, confirmProps: { color: 'warning' }, onConfirm: () => { - console.log('confirm') - blockerProceed() + blocker.proceed() }, - onAbort: () => blocker.reset?.() + onAbort: () => { + blocker.reset() + } }) } - }, [blocker, form, blockerProceed]) + }, [blocker]) const catSelectedRadioAndValuesArray = useMemo( () => campaign && findArrayWithLengthInObjectAsValue(campaign.targetingInput.inputs.categories), From 1385cadf8f38cf309276f9bea89d10b04a5fa138 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 12:01:09 +0300 Subject: [PATCH 07/13] fix campaign actions navigates --- src/components/Dashboard/Dashboard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index 5e12cbe7..f231610b 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -226,7 +226,7 @@ const Dashboard = ({ isAdminPanel, accountId }: { isAdminPanel?: boolean; accoun status: CampaignStatus.created }) }) - navigate('/dashboard/create-campaign', { replace: true }) + navigate('/dashboard/create-campaign', {}) } else { showNotification('error', 'Editing draft campaign failed', 'Editing draft campaign failed') } @@ -236,7 +236,7 @@ const Dashboard = ({ isAdminPanel, accountId }: { isAdminPanel?: boolean; accoun const handleEdit = useCallback( (data: DashboardTableElement['actionData']) => - navigate(`/dashboard/campaign-details/${data.campaign.id}?edit=true`, { replace: true }), + navigate(`/dashboard/campaign-details/${data.campaign.id}?edit=true`, {}), [navigate] ) From 7e1902d67e36707702c06833a0f6b139c65affd6 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 12:12:01 +0300 Subject: [PATCH 08/13] better cmp details action btns --- .../CampaignDetails/CampaignDetails.tsx | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/components/CampaignDetails/CampaignDetails.tsx b/src/components/CampaignDetails/CampaignDetails.tsx index 120b68d1..e5a213e7 100644 --- a/src/components/CampaignDetails/CampaignDetails.tsx +++ b/src/components/CampaignDetails/CampaignDetails.tsx @@ -1,6 +1,16 @@ import { useCallback, useEffect, useMemo } from 'react' import { useNavigate, useParams, useSearchParams } from 'react-router-dom' -import { Container, Grid, createStyles, Text, Flex, Box, Button, Paper, Stack } from '@mantine/core' +import { + Container, + Grid, + createStyles, + Text, + Box, + Button, + Paper, + Stack, + Group +} from '@mantine/core' import { modals } from '@mantine/modals' import BadgeStatusCampaign from 'components/Dashboard/BadgeStatusCampaign' import { formatCatsAndLocsData } from 'helpers/createCampaignHelpers' @@ -232,10 +242,10 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { return ( <> - + - - + + - + - - Campaign Analytics + + + Campaign Analytics{' '} + } action={() => navigate(`/dashboard/campaign-analytics/${campaign.id}`)} /> - - + + {isEditMode ? ( From e12c82aca124d62ee33fb68f7d343ae66e7e3383 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 12:36:37 +0300 Subject: [PATCH 09/13] move supply stats to campaigns data --- .../CampaignsContext/CampaignsDataContext.tsx | 51 +++++++++++++++---- .../defaultData.ts | 2 +- .../CreateCampaignContext.tsx | 49 ++---------------- 3 files changed, 45 insertions(+), 57 deletions(-) rename src/contexts/{CreateCampaignContext => CampaignsContext}/defaultData.ts (97%) diff --git a/src/contexts/CampaignsContext/CampaignsDataContext.tsx b/src/contexts/CampaignsContext/CampaignsDataContext.tsx index cc3db185..8e11e6d1 100644 --- a/src/contexts/CampaignsContext/CampaignsDataContext.tsx +++ b/src/contexts/CampaignsContext/CampaignsDataContext.tsx @@ -11,14 +11,10 @@ import { import { useAdExApi } from 'hooks/useAdexServices' import useAccount from 'hooks/useAccount' import useCustomNotifications from 'hooks/useCustomNotifications' -import { - BaseData, - CampaignData, - // EventAggregatesDataRes, - EvAggrData -} from 'types/campaignsData' +import { BaseData, CampaignData, EvAggrData, SupplyStats } from 'types' import { CREATE_CAMPAIGN_DEFAULT_VALUE } from 'constants/createCampaign' import { parseBigNumTokenAmountToDecimal } from 'helpers/balances' +import { defaultSupplyStats } from './defaultData' const defaultCampaignData: CampaignData = { campaignId: '', @@ -111,10 +107,12 @@ const getURLSubRouteByCampaignStatus = (status: CampaignStatus) => { } interface ICampaignsDataContext { campaignsData: Map + supplyStats: SupplyStats // TODO: all campaigns event aggregations by account // eventAggregates: Map updateCampaignDataById: (params: Campaign['id']) => void updateAllCampaignsData: (updateAdvanced?: boolean) => void + updateSupplyStats: () => void // updateEventAggregates: (params: Campaign['id']) => void initialDataLoading: boolean changeCampaignStatus: (status: CampaignStatus, campaignId: Campaign['id']) => void @@ -133,6 +131,7 @@ const CampaignsDataProvider: FC const { authenticated } = useAccount() const [initialDataLoading, setInitialDataLoading] = useState(true) + const [supplyStats, setSupplyStats] = useState(defaultSupplyStats) const [campaignsData, setCampaignData] = useState( new Map() @@ -292,9 +291,37 @@ const CampaignsDataProvider: FC [adexServicesRequest, showNotification, type] ) + const updateSupplyStats = useCallback(async () => { + let result + + try { + result = await adexServicesRequest('backend', { + route: '/dsp/stats/common', + method: 'GET' + }) + + if (!result) { + throw new Error('Getting banner sizes failed.') + } + + const hasEmptyValueResponse = Object.values(result).every( + (value) => Array.isArray(value) && value.length === 0 + ) + + if (hasEmptyValueResponse) { + throw new Error('Supply stats cna not available') + } + + setSupplyStats(result as SupplyStats) + } catch (e) { + console.error(e) + showNotification('error', 'Getting banner sizes failed', 'Getting banner sizes failed') + } + }, [adexServicesRequest, showNotification]) + useEffect(() => { - console.log({ type }) - }, [type]) + updateSupplyStats() + }, [updateSupplyStats]) useEffect(() => { if (authenticated) { @@ -354,21 +381,25 @@ const CampaignsDataProvider: FC const contextValue = useMemo( () => ({ campaignsData, + supplyStats, updateCampaignDataById, updateAllCampaignsData, initialDataLoading, changeCampaignStatus, deleteDraftCampaign, - toggleArchived + toggleArchived, + updateSupplyStats }), [ campaignsData, + supplyStats, updateCampaignDataById, updateAllCampaignsData, initialDataLoading, changeCampaignStatus, deleteDraftCampaign, - toggleArchived + toggleArchived, + updateSupplyStats ] ) diff --git a/src/contexts/CreateCampaignContext/defaultData.ts b/src/contexts/CampaignsContext/defaultData.ts similarity index 97% rename from src/contexts/CreateCampaignContext/defaultData.ts rename to src/contexts/CampaignsContext/defaultData.ts index d5f301c4..a4273f42 100644 --- a/src/contexts/CreateCampaignContext/defaultData.ts +++ b/src/contexts/CampaignsContext/defaultData.ts @@ -1,4 +1,4 @@ -export const mockData = { +export const defaultSupplyStats = { appBannerFormats: [ { value: '320x50', diff --git a/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx b/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx index 0eec6f90..9043fac0 100644 --- a/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx +++ b/src/contexts/CreateCampaignContext/CreateCampaignContext.tsx @@ -10,7 +10,6 @@ import { import { CREATE_CAMPAIGN_DEFAULT_VALUE, dateNowPlusThirtyDays } from 'constants/createCampaign' import superjson, { serialize } from 'superjson' import { - SupplyStats, CampaignUI, CreateCampaignType, SupplyStatsDetails, @@ -31,25 +30,15 @@ import { import { parseFromBigNumPrecision } from 'helpers/balances' import { AdUnit, Campaign, Placement } from 'adex-common' import dayjs from 'dayjs' -import useCustomNotifications from 'hooks/useCustomNotifications' import { formatDateTime } from 'helpers' import { isValidHttpUrl } from 'helpers/validators' -import { mockData } from './defaultData' - -const supplyStatsDefaultValue = { - appBannerFormats: [], - siteBannerFormatsDesktop: [], - siteBannerFormatsMobile: [], - appBidFloors: [], - siteDesktopBidFloors: [], - siteMobileBidFloors: [] -} +import { useCampaignsData } from 'hooks/useCampaignsData' const CreateCampaignContext = createContext(null) const CreateCampaignContextProvider: FC = ({ children }) => { const { adexServicesRequest } = useAdExApi() - const { showNotification } = useCustomNotifications() + const { supplyStats, updateSupplyStats } = useCampaignsData() // TODO: the address will be fixed and will always has a default value const { adexAccount, @@ -75,7 +64,6 @@ const CreateCampaignContextProvider: FC = ({ children }) => { ) const [campaign, setCampaign] = useState(defaultValue) - const [supplyStats, setSupplyStats] = useState(supplyStatsDefaultValue) const [selectedBannerSizes, setSelectedBannerSizes] = useState< SupplyStatsDetails[] | SupplyStatsDetails[][] >([]) @@ -108,39 +96,8 @@ const CreateCampaignContextProvider: FC = ({ children }) => { } }, [defaultValue]) - const getSupplyStats = useCallback(async () => { - let result - - try { - result = await adexServicesRequest('backend', { - route: '/dsp/stats/common', - method: 'GET' - }) - - if (!result) { - throw new Error('Getting banner sizes failed.') - } - - const hasEmptyValueResponse = Object.values(result).every( - (value) => Array.isArray(value) && value.length === 0 - ) - - if (hasEmptyValueResponse) { - result = mockData - } - - setSupplyStats(result as SupplyStats) - } catch (e) { - console.error(e) - showNotification('error', 'Getting banner sizes failed', 'Getting banner sizes failed') - // TODO: add fallback or just use mock data - result = mockData - setSupplyStats(result) - } - }, [adexServicesRequest, showNotification]) - useEffect(() => { - getSupplyStats() + updateSupplyStats() }, []) // eslint-disable-line useEffect(() => { From 3e9954b4827b37cb9cbd86142953b8f6a415b23d Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 13:56:00 +0300 Subject: [PATCH 10/13] edit campaign rcmd cpm --- src/components/EditCampaign/EditCampaign.tsx | 10 +++++--- src/helpers/createCampaignHelpers.ts | 24 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/components/EditCampaign/EditCampaign.tsx b/src/components/EditCampaign/EditCampaign.tsx index 8a3fa6f0..e10db40a 100644 --- a/src/components/EditCampaign/EditCampaign.tsx +++ b/src/components/EditCampaign/EditCampaign.tsx @@ -23,7 +23,8 @@ import { parseBigNumTokenAmountToDecimal, parseToBigNumPrecision } from 'helpers import { findArrayWithLengthInObjectAsValue, - updateCatsLocsObject + updateCatsLocsObject, + getRecommendedCPMRange } from 'helpers/createCampaignHelpers' import useAccount from 'hooks/useAccount' import { useAdExApi } from 'hooks/useAdexServices' @@ -62,9 +63,12 @@ const EditCampaign = ({ campaign }: { campaign: Campaign }) => { const { adexAccount: { balanceToken } } = useAccount() - const { updateCampaignDataById } = useCampaignsData() + const { updateCampaignDataById, supplyStats } = useCampaignsData() - const recommendedPaymentBounds = { min: '0.10', max: '0.5' } + const recommendedPaymentBounds = useMemo( + () => getRecommendedCPMRange(supplyStats, campaign), + [campaign, supplyStats] + ) const form = useForm({ initialValues: { diff --git a/src/helpers/createCampaignHelpers.ts b/src/helpers/createCampaignHelpers.ts index e1d5dfe1..9d108854 100644 --- a/src/helpers/createCampaignHelpers.ts +++ b/src/helpers/createCampaignHelpers.ts @@ -374,3 +374,27 @@ export const parseRange = (str: string): { min: number; max: number } => { return { min, max } } + +export const getRecommendedCPMRange = (supplyStats: SupplyStats, campaign: Campaign) => { + if (!supplyStats || !campaign) { + return { min: 0, max: 0 } + } + const mostRequests = campaign.targetingInput.inputs.placements.in + .map((placement) => { + switch (placement) { + case 'app': + return supplyStats.appBidFloors + + case 'site': + return [...supplyStats.siteDesktopBidFloors, ...supplyStats.siteMobileBidFloors] + default: + return [] + } + }) + .flat() + .sort((a, b) => b.count - a.count)?.[0] + + return { + ...parseRange(mostRequests?.value || '0_42-0_69') + } +} From f988a6b13e428718310895c7398634e217d20b0e Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 14:39:06 +0300 Subject: [PATCH 11/13] fix custom table actions --- src/components/common/CustomTable/CustomTable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/common/CustomTable/CustomTable.tsx b/src/components/common/CustomTable/CustomTable.tsx index c7378cfa..a6fd7ded 100644 --- a/src/components/common/CustomTable/CustomTable.tsx +++ b/src/components/common/CustomTable/CustomTable.tsx @@ -209,14 +209,14 @@ export const CustomTable = ({ {cols} - {actionsMenu} + {!!actionsMenu} ) } return ( {cols} - {actionsMenu && {actionsMenu}} + {!!actionsMenu && {actionsMenu}} ) }) From 4409fdbd20df250355e4abba62ab8d7e51c05a6a Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 14:39:51 +0300 Subject: [PATCH 12/13] format ssp stats bid floors --- src/components/AdminPanel/SspStats.tsx | 63 ++++++-------------------- src/helpers/createCampaignHelpers.ts | 10 ++-- 2 files changed, 20 insertions(+), 53 deletions(-) diff --git a/src/components/AdminPanel/SspStats.tsx b/src/components/AdminPanel/SspStats.tsx index d4b06ce7..6d9ff82b 100644 --- a/src/components/AdminPanel/SspStats.tsx +++ b/src/components/AdminPanel/SspStats.tsx @@ -1,57 +1,24 @@ -import { useCallback, useState, useEffect, useMemo } from 'react' +import { useMemo } from 'react' import { SimpleGrid, Box, Tabs, Paper, Loader } from '@mantine/core' -import { useAdExApi } from 'hooks/useAdexServices' -import useCustomNotifications from 'hooks/useCustomNotifications' - -import { SupplyStats, SupplyStatsDetails } from 'types' +import { useCampaignsData } from 'hooks/useCampaignsData' +import { SupplyStatsDetails } from 'types' import CustomTable from 'components/common/CustomTable' +import { parseRange } from 'helpers/createCampaignHelpers' -const supplyStatsDefaultValue = { - appBannerFormats: [], - siteBannerFormatsDesktop: [], - siteBannerFormatsMobile: [], - appBidFloors: [], - siteDesktopBidFloors: [], - siteMobileBidFloors: [] -} -const toTableDta = (stats: SupplyStatsDetails[], title: string) => { +const toTableDta = (stats: SupplyStatsDetails[], title: string, isCpmRange?: boolean) => { return { headings: [title, 'count'], - elements: stats - .slice(0, 200) - .map(({ value, count }) => ({ value, count: count.toLocaleString() })) + elements: stats.slice(0, 200).map(({ value, count }) => ({ + value: isCpmRange + ? JSON.stringify(parseRange(value), null, 4).replace(/\{|\}|"/g, '') + : value, + count: count.toLocaleString() + })) } } function SspStats() { - const { adexServicesRequest } = useAdExApi() - const { showNotification } = useCustomNotifications() - const [supplyStats, setSupplyStats] = useState(supplyStatsDefaultValue) - - const getSupplyStats = useCallback(async () => { - let result - - try { - result = await adexServicesRequest('backend', { - route: '/dsp/stats/common', - method: 'GET' - }) - - if (!result) { - throw new Error('Getting banner sizes failed.') - } - - setSupplyStats(result as SupplyStats) - } catch (e) { - console.error(e) - showNotification('error', 'Error getting supply stats') - } - }, [adexServicesRequest, showNotification]) - - useEffect(() => { - getSupplyStats() - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + const { supplyStats } = useCampaignsData() const data = useMemo(() => { const { @@ -67,9 +34,9 @@ function SspStats() { appBannerFormats: toTableDta(appBannerFormats, 'App Format'), siteBannerFormatsMobile: toTableDta(siteBannerFormatsMobile, 'Mobile site Format'), siteBannerFormatsDesktop: toTableDta(siteBannerFormatsDesktop, 'Desktop site Format'), - appBidFloors: toTableDta(appBidFloors, 'App Bid Floor'), - siteDesktopBidFloors: toTableDta(siteDesktopBidFloors, 'Desktop site bid floor'), - siteMobileBidFloors: toTableDta(siteMobileBidFloors, 'Mobil site bid floor') + appBidFloors: toTableDta(appBidFloors, 'App Bid Floor', true), + siteDesktopBidFloors: toTableDta(siteDesktopBidFloors, 'Desktop site bid floor', true), + siteMobileBidFloors: toTableDta(siteMobileBidFloors, 'Mobil site bid floor', true) } }, [supplyStats]) diff --git a/src/helpers/createCampaignHelpers.ts b/src/helpers/createCampaignHelpers.ts index 9d108854..fda67775 100644 --- a/src/helpers/createCampaignHelpers.ts +++ b/src/helpers/createCampaignHelpers.ts @@ -365,12 +365,12 @@ export const parseRange = (str: string): { min: number; max: number } => { const pattern = /^(\d+)_(\d+)-(\d+)_(\d+)$/ const match = str.match(pattern) - if (!match) { - throw new Error('Invalid input format. Expected format: "0_20-0_30"') - } + // if (!match) { + // throw new Error('Invalid input format. Expected format: "0_20-0_30"') + // } - const min = parseFloat(`${match[1]}.${match[2]}`) - const max = parseFloat(`${match[3]}.${match[4]}`) + const min = parseFloat(`${match?.[1]}.${match?.[2]}`) + const max = parseFloat(`${match?.[3]}.${match?.[4]}`) return { min, max } } From 5607a893516dcffb34763f8d7252da25cb56b723 Mon Sep 17 00:00:00 2001 From: IvoPaunov Date: Tue, 30 Jul 2024 14:53:45 +0300 Subject: [PATCH 13/13] fix details when admin supply stats update --- src/components/CampaignDetails/CampaignDetails.tsx | 1 + src/contexts/CampaignsContext/CampaignsDataContext.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/CampaignDetails/CampaignDetails.tsx b/src/components/CampaignDetails/CampaignDetails.tsx index e5a213e7..4b7fa5d0 100644 --- a/src/components/CampaignDetails/CampaignDetails.tsx +++ b/src/components/CampaignDetails/CampaignDetails.tsx @@ -291,6 +291,7 @@ const CampaignDetails = ({ isAdminPanel }: { isAdminPanel?: boolean }) => { })} rightIcon={} onClick={handleStopOrDelete} + disabled={isAdminPanel} variant="subtle" > Delete draft diff --git a/src/contexts/CampaignsContext/CampaignsDataContext.tsx b/src/contexts/CampaignsContext/CampaignsDataContext.tsx index 8e11e6d1..ef8baa14 100644 --- a/src/contexts/CampaignsContext/CampaignsDataContext.tsx +++ b/src/contexts/CampaignsContext/CampaignsDataContext.tsx @@ -309,7 +309,7 @@ const CampaignsDataProvider: FC ) if (hasEmptyValueResponse) { - throw new Error('Supply stats cna not available') + throw new Error('Supply stats not available') } setSupplyStats(result as SupplyStats) @@ -320,8 +320,10 @@ const CampaignsDataProvider: FC }, [adexServicesRequest, showNotification]) useEffect(() => { + console.log('updateSupplyStats') updateSupplyStats() - }, [updateSupplyStats]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) useEffect(() => { if (authenticated) {