From a0d5fbe1e38b0d6b8c73aa7c1cb86ea705dce7e9 Mon Sep 17 00:00:00 2001 From: Ian Krieger <48930920+IanKrieger@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:13:14 -0500 Subject: [PATCH 1/6] feat: filter NTT by default (#1026) --- .../Datagrid/stringFilterOperators.tsx | 19 +++++++++++++++++++ src/user/campaignList/CampaignList.tsx | 9 +++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/components/Datagrid/stringFilterOperators.tsx diff --git a/src/components/Datagrid/stringFilterOperators.tsx b/src/components/Datagrid/stringFilterOperators.tsx new file mode 100644 index 00000000..30b4fed9 --- /dev/null +++ b/src/components/Datagrid/stringFilterOperators.tsx @@ -0,0 +1,19 @@ +import { + getGridStringOperators, + GridFilterInputValue, + GridFilterItem, +} from "@mui/x-data-grid"; + +export function stringFilterOperators() { + return [ + { + label: "is not", + value: "not", + getApplyFilterFn: (field: GridFilterItem) => (params: any) => + params.value !== field.value, + InputComponentProps: { type: "string" }, + InputComponent: GridFilterInputValue, + }, + ...getGridStringOperators(), + ]; +} diff --git a/src/user/campaignList/CampaignList.tsx b/src/user/campaignList/CampaignList.tsx index 75e68f03..a2e264a4 100644 --- a/src/user/campaignList/CampaignList.tsx +++ b/src/user/campaignList/CampaignList.tsx @@ -25,6 +25,7 @@ import { EditButton } from "user/campaignList/EditButton"; import { calculateMetric } from "user/analytics/analyticsOverview/lib/overview.library"; import { StatsMetric } from "user/analytics/analyticsOverview/types"; import { uiLabelsForCampaignFormat } from "util/campaign"; +import { stringFilterOperators } from "components/Datagrid/stringFilterOperators"; interface Props { advertiser?: AdvertiserCampaignsFragment | null; @@ -87,6 +88,7 @@ export function CampaignList({ advertiser }: Props) { align: "left", headerAlign: "left", width: 150, + filterOperators: stringFilterOperators(), }, { field: "state", @@ -256,6 +258,13 @@ export function CampaignList({ advertiser }: Props) { pageSize: 10, }, }, + filter: { + filterModel: { + items: [ + { field: "format", operator: "not", value: "New tab takeover" }, + ], + }, + }, }} /> ); From 384f2ccf7a2b68089d32c7acbea22b4f1254ddb1 Mon Sep 17 00:00:00 2001 From: Ian Krieger <48930920+IanKrieger@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:31:45 -0500 Subject: [PATCH 2/6] chore(deps): temporarily suppress warning (#1027) --- audit-resolve.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/audit-resolve.json b/audit-resolve.json index 0967ef42..d68ba48c 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -1 +1,11 @@ -{} +{ + "decisions": { + "1096292|vite-plugin-checker>lodash.pick": { + "decision": "ignore", + "madeAt": 1706203247319, + "expiresAt": 1708795235684 + } + }, + "rules": {}, + "version": 1 +} \ No newline at end of file From 050f13168cca7772971d54b812a67f23d523827a Mon Sep 17 00:00:00 2001 From: Ian Krieger <48930920+IanKrieger@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:54:38 -0500 Subject: [PATCH 3/6] feat: add viewthrough and clickthrough reporting (#1023) * feat: add viewthrough and clickthrough reporting * test: should only be 2 decimal places * fix: update overview to convert from bignumber * fix: remove filter from change * fix: future code * fix: make sure that overview are numbers * fix: make sure we have maximum digits * fix: rebase deleted file --- src/components/Datagrid/renderers.tsx | 14 +- src/graphql/analytics-overview.generated.tsx | 49 ++++- src/graphql/analytics-overview.graphql | 13 +- src/user/adSet/AdSetList.tsx | 12 +- src/user/ads/AdList.tsx | 12 +- .../analyticsOverview/components/LiveFeed.tsx | 33 ++- .../components/MetricFilter.tsx | 3 +- .../components/MetricSelect.tsx | 12 + .../analyticsOverview/lib/ads.library.ts | 4 + .../analyticsOverview/lib/creative.library.ts | 4 + .../analyticsOverview/lib/library.spec.ts | 208 ++++++++++++------ .../analyticsOverview/lib/os.library.ts | 10 +- .../analyticsOverview/lib/overview.library.ts | 86 ++++---- .../reports/campaign/EngagementsOverview.tsx | 18 +- .../analyticsOverview/types/index.ts | 34 +-- src/user/analytics/renderers/index.tsx | 11 +- src/user/campaignList/CampaignList.tsx | 14 +- src/user/views/user/AdDetailTable.tsx | 31 ++- src/util/bignumber.ts | 10 + 19 files changed, 367 insertions(+), 211 deletions(-) create mode 100644 src/util/bignumber.ts diff --git a/src/components/Datagrid/renderers.tsx b/src/components/Datagrid/renderers.tsx index 78efd319..e770a212 100644 --- a/src/components/Datagrid/renderers.tsx +++ b/src/components/Datagrid/renderers.tsx @@ -17,6 +17,8 @@ import { CampaignExtras } from "user/adSet/AdSetList"; import { FilterContext } from "state/context"; import { refetchAdvertiserCampaignsQuery } from "graphql/advertiser.generated"; import { UpdateAdSetInput } from "graphql/types"; +import { toLocaleString } from "util/bignumber"; +import BigNumber from "bignumber.js"; export type CellValueRenderer = (value: any) => ReactNode; const ADS_DEFAULT_TIMEZONE = "America/New_York"; @@ -78,16 +80,14 @@ export const StandardRenderers: Record = { }; export function renderMonetaryAmount( - value: number, + value: BigNumber | number, currency: string, -): ReactNode { +) { + const val = BigNumber(value); if (currency === "USD") { - return `$${value.toLocaleString("en", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })}`; + return `$${toLocaleString(val)}`; } else { - return {value.toLocaleString("en")} BAT; + return `${toLocaleString(val)} ${currency}`; } } diff --git a/src/graphql/analytics-overview.generated.tsx b/src/graphql/analytics-overview.generated.tsx index 5719f678..c53f9a70 100644 --- a/src/graphql/analytics-overview.generated.tsx +++ b/src/graphql/analytics-overview.generated.tsx @@ -14,9 +14,17 @@ export type EngagementFragment = { creativeid: string; creativestate: string; creativepayload: string; - count: number; + view: string; + click: string; + viewthroughConversion: string; + clickthroughConversion: string; + conversion: string; + dismiss: string; + downvote: string; + landed: string; + spend: string; + upvote: string; price: number; - cost: number; android: number; ios: number; linux: number; @@ -51,9 +59,17 @@ export type CampaignWithEngagementsFragment = { creativeid: string; creativestate: string; creativepayload: string; - count: number; + view: string; + click: string; + viewthroughConversion: string; + clickthroughConversion: string; + conversion: string; + dismiss: string; + downvote: string; + landed: string; + spend: string; + upvote: string; price: number; - cost: number; android: number; ios: number; linux: number; @@ -94,9 +110,17 @@ export type AnalyticOverviewQuery = { creativeid: string; creativestate: string; creativepayload: string; - count: number; + view: string; + click: string; + viewthroughConversion: string; + clickthroughConversion: string; + conversion: string; + dismiss: string; + downvote: string; + landed: string; + spend: string; + upvote: string; price: number; - cost: number; android: number; ios: number; linux: number; @@ -134,9 +158,18 @@ export const EngagementFragmentDoc = gql` creativeid creativestate creativepayload - count + view + click + viewthroughConversion + clickthroughConversion + conversion + dismiss + downvote + landed + spend + upvote + downvote price - cost android ios linux diff --git a/src/graphql/analytics-overview.graphql b/src/graphql/analytics-overview.graphql index 45190b1a..85ab0ba4 100644 --- a/src/graphql/analytics-overview.graphql +++ b/src/graphql/analytics-overview.graphql @@ -9,9 +9,18 @@ fragment Engagement on Engagement { creativeid creativestate creativepayload - count + view + click + viewthroughConversion + clickthroughConversion + conversion + dismiss + downvote + landed + spend + upvote + downvote price - cost android ios linux diff --git a/src/user/adSet/AdSetList.tsx b/src/user/adSet/AdSetList.tsx index bdb2031e..5a793ad6 100644 --- a/src/user/adSet/AdSetList.tsx +++ b/src/user/adSet/AdSetList.tsx @@ -1,10 +1,7 @@ import { Chip } from "@mui/material"; import { Status } from "components/Campaigns/Status"; import _ from "lodash"; -import { - adSetOnOffState, - StandardRenderers, -} from "components/Datagrid/renderers"; +import { adSetOnOffState } from "components/Datagrid/renderers"; import { CampaignAdsFragment } from "graphql/campaign.generated"; import { CampaignSource } from "graphql/types"; import { StatsMetric } from "user/analytics/analyticsOverview/types"; @@ -85,13 +82,6 @@ export function AdSetList({ campaign, loading, engagements }: Props) { filterable: false, width: 100, }, - { - field: "createdAt", - headerName: "Created", - valueGetter: ({ row }) => row.createdAt, - renderCell: ({ row }) => StandardRenderers.date(row.createdAt), - width: 120, - }, { field: "name", headerName: "Name", diff --git a/src/user/ads/AdList.tsx b/src/user/ads/AdList.tsx index 8b33c476..d1eb2a06 100644 --- a/src/user/ads/AdList.tsx +++ b/src/user/ads/AdList.tsx @@ -7,7 +7,6 @@ import { StatsMetric } from "user/analytics/analyticsOverview/types"; import { AdDetailTable } from "user/views/user/AdDetailTable"; import { GridColDef } from "@mui/x-data-grid"; import { CreativeFragment } from "graphql/creative.generated"; -import { StandardRenderers } from "components/Datagrid/renderers"; import { Box } from "@mui/material"; interface Props { @@ -48,16 +47,13 @@ export function AdList({ campaign, loading, engagements }: Props) { const ads: AdDetails[] = _.flatMap(adSets, "ads"); const columns: GridColDef[] = [ - { - field: "createdAt", - headerName: "Created", - valueGetter: ({ row }) => row.creative.createdAt, - renderCell: ({ row }) => StandardRenderers.date(row.creative.createdAt), - }, { field: "name", headerName: "Ad Name", - valueGetter: ({ row }) => row.creative.name, + valueGetter: ({ row }) => + row.adState !== "deleted" + ? row.creative.name + : `(DELETED) ${row.creative.name}`, renderCell: ({ row }) => ( {row.adState === "deleted" && (DELETED) } diff --git a/src/user/analytics/analyticsOverview/components/LiveFeed.tsx b/src/user/analytics/analyticsOverview/components/LiveFeed.tsx index b6a69349..273a14e9 100644 --- a/src/user/analytics/analyticsOverview/components/LiveFeed.tsx +++ b/src/user/analytics/analyticsOverview/components/LiveFeed.tsx @@ -1,5 +1,6 @@ import { Box, Chip, Typography } from "@mui/material"; import { OverviewDetail, StatsMetric } from "../types"; +import { toLocaleString } from "util/bignumber"; interface OverviewProps extends OverviewDetail { currency: string; @@ -18,46 +19,54 @@ interface Feed { export default function LiveFeed({ overview, processed }: LiveFeedProps) { const { budget, currency } = overview; - const realSpend = processed.spend > budget ? budget : processed.spend; + const realSpend = processed.spend.gte(budget) ? budget : processed.spend; const feedValues: Feed[] = [ { label: "Click-through rate", - value: `${processed.ctr.toFixed(2)}%`, + value: `${toLocaleString(processed.ctr)}%`, }, { label: "Site visit rate", - value: `${processed.visitRate.toFixed(2)}%`, + value: `${toLocaleString(processed.visitRate)}%`, }, { label: "Dismissal rate", - value: `${processed.dismissRate.toFixed(2)}%`, + value: `${toLocaleString(processed.dismissRate)}%`, }, { label: "Click to site visit rate", - value: `${processed.landingRate.toFixed(2)}%`, + value: `${toLocaleString(processed.landingRate)}%`, }, - { label: "Upvotes", value: `${processed.upvotes}` }, - { label: "Downvotes", value: `${processed.downvotes}` }, + { label: "Upvotes", value: toLocaleString(processed.upvotes) }, + { label: "Downvotes", value: toLocaleString(processed.downvotes) }, { label: "Spend", - value: `${realSpend.toLocaleString()} ${currency}`, + value: `$${toLocaleString(realSpend)} ${currency}`, }, { label: "Budget", - value: `${budget.toLocaleString()} ${currency}`, + value: `$${toLocaleString(budget)} ${currency}`, }, ]; - if (processed.conversions > 0) { + if (processed.conversions.gt(0)) { feedValues.push( { label: "Conversions", - value: `${processed.conversions}`, + value: toLocaleString(processed.conversions), + }, + { + label: "View-through Conversions", + value: toLocaleString(processed.viewthroughConversion), + }, + { + label: "Click-through Conversions", + value: toLocaleString(processed.clickthroughConversion), }, { label: "CPA", - value: `$${processed.cpa.toLocaleString()}`, + value: `$${toLocaleString(processed.cpa)} ${currency}`, }, ); } diff --git a/src/user/analytics/analyticsOverview/components/MetricFilter.tsx b/src/user/analytics/analyticsOverview/components/MetricFilter.tsx index a3a37a98..7ca69300 100644 --- a/src/user/analytics/analyticsOverview/components/MetricFilter.tsx +++ b/src/user/analytics/analyticsOverview/components/MetricFilter.tsx @@ -2,6 +2,7 @@ import MetricSelect from "user/analytics/analyticsOverview/components/MetricSele import { Box, Stack, Switch, Tooltip, Typography } from "@mui/material"; import { decideValueAttribute } from "user/analytics/analyticsOverview/lib/overview.library"; import { Metrics, StatsMetric } from "user/analytics/analyticsOverview/types"; +import { toLocaleString } from "util/bignumber"; type FilterMetric = { key: keyof StatsMetric; @@ -56,7 +57,7 @@ const FilterBox = ({ width="100%" > - {attrs.prefix ?? ""} {displayVal} {attrs.suffix ?? ""} + {attrs.prefix ?? ""} {toLocaleString(displayVal)} {attrs.suffix ?? ""} { processed.conversion.macos, ); - expect(landed).toBe(300); - expect(ctr).toBe(4); + expect(landed.toNumber()).toBe(300); + expect(ctr.toNumber()).toBe(4); const fixed = cpa.toPrecision(2); expect(fixed).toBe("0.83"); }); @@ -234,44 +304,48 @@ it("should calculate metrics per creative id", () => { expect(creatives).toMatchInlineSnapshot(` [ { - "clicks": 2, - "convRate": 150, - "conversions": 3, - "cpa": 0, + "clicks": "2", + "clickthroughConversion": "1", + "convRate": "150", + "conversions": "3", + "cpa": "0", "creativePayload": { "body": "be cool", "title": "name one", }, - "ctr": 5.405405405405405, - "dismissRate": 0, - "dismissals": 0, - "downvotes": 0, - "landingRate": 100, - "landings": 2, - "spend": 0, - "upvotes": 0, - "views": 37, - "visitRate": 5.405405405405405, + "ctr": "3.226", + "dismissRate": "0", + "dismissals": "0", + "downvotes": "0", + "landingRate": "100", + "landings": "2", + "spend": "0", + "upvotes": "0", + "views": "62", + "viewthroughConversion": "2", + "visitRate": "3.226", }, { - "clicks": 2, - "convRate": 0, - "conversions": 0, - "cpa": NaN, + "clicks": "2", + "clickthroughConversion": "0", + "convRate": "0", + "conversions": "0", + "cpa": "0", "creativePayload": { "body": "be uncool", "title": "name two", }, - "ctr": 20, - "dismissRate": 0, - "dismissals": 0, - "downvotes": 0, - "landingRate": 150, - "landings": 3, - "spend": 0, - "upvotes": 0, - "views": 10, - "visitRate": 30, + "ctr": "20", + "dismissRate": "0", + "dismissals": "0", + "downvotes": "0", + "landingRate": "150", + "landings": "3", + "spend": "0", + "upvotes": "0", + "views": "10", + "viewthroughConversion": "0", + "visitRate": "30", }, ] `); @@ -281,20 +355,22 @@ it("should calculate overall stats", () => { const stats = processStats(engagements); expect(stats).toMatchInlineSnapshot(` { - "clicks": 4, - "convRate": 75, - "conversions": 3, - "cpa": 0, - "ctr": 8.51063829787234, - "dismissRate": 0, - "dismissals": 0, - "downvotes": 0, - "landingRate": 125, - "landings": 5, - "spend": 0, - "upvotes": 0, - "views": 47, - "visitRate": 10.638297872340425, + "clicks": "4", + "clickthroughConversion": "1", + "convRate": "75", + "conversions": "3", + "cpa": "0", + "ctr": "5.556", + "dismissRate": "0", + "dismissals": "0", + "downvotes": "0", + "landingRate": "125", + "landings": "5", + "spend": "0", + "upvotes": "0", + "views": "72", + "viewthroughConversion": "2", + "visitRate": "6.944", } `); }); @@ -313,7 +389,7 @@ it("should calculate specific time series data by time range", () => { "metric1DataSet": [ [ 1677657600000, - 47, + 72, ], ], "metric2DataSet": [ diff --git a/src/user/analytics/analyticsOverview/lib/os.library.ts b/src/user/analytics/analyticsOverview/lib/os.library.ts index 24599836..49580384 100644 --- a/src/user/analytics/analyticsOverview/lib/os.library.ts +++ b/src/user/analytics/analyticsOverview/lib/os.library.ts @@ -52,11 +52,11 @@ export const mapOsStats = (stats: OSMetric) => { const calculateForOS = (n: OS, d: OS, isPercent: boolean = true) => { return { - android: calculateMetric(isPercent, n.android, d.android), - ios: calculateMetric(isPercent, n.ios, d.ios), - windows: calculateMetric(isPercent, n.windows, d.windows), - linux: calculateMetric(isPercent, n.linux, d.linux), - macos: calculateMetric(isPercent, n.macos, d.macos), + android: calculateMetric(isPercent, n.android, d.android).toNumber(), + ios: calculateMetric(isPercent, n.ios, d.ios).toNumber(), + windows: calculateMetric(isPercent, n.windows, d.windows).toNumber(), + linux: calculateMetric(isPercent, n.linux, d.linux).toNumber(), + macos: calculateMetric(isPercent, n.macos, d.macos).toNumber(), }; }; diff --git a/src/user/analytics/analyticsOverview/lib/overview.library.ts b/src/user/analytics/analyticsOverview/lib/overview.library.ts index 4a23f967..88723587 100644 --- a/src/user/analytics/analyticsOverview/lib/overview.library.ts +++ b/src/user/analytics/analyticsOverview/lib/overview.library.ts @@ -8,6 +8,7 @@ import { Tooltip, } from "user/analytics/analyticsOverview/types"; import { EngagementFragment } from "graphql/analytics-overview.generated"; +import BigNumber from "bignumber.js"; type MetricDataSet = { metric1DataSet: number[][]; @@ -176,54 +177,44 @@ const mapGroupingName = (grouping: string) => { }; export const mapMetric = (engagement: EngagementFragment): BaseMetric => { - const byType = (type: string, e: EngagementFragment) => - e.type === type ? e.count : 0; - return { - views: byType("view", engagement), - conversions: byType("conversion", engagement), - landings: byType("landed", engagement), - clicks: byType("click", engagement), - spend: engagement.cost, - upvotes: byType("upvote", engagement), - downvotes: byType("downvote", engagement), - dismissals: byType("dismiss", engagement), + views: BigNumber(engagement.view), + conversions: BigNumber(engagement.conversion), + landings: BigNumber(engagement.landed), + clicks: BigNumber(engagement.click), + spend: BigNumber(engagement.spend), + upvotes: BigNumber(engagement.upvote), + downvotes: BigNumber(engagement.downvote), + dismissals: BigNumber(engagement.dismiss), + clickthroughConversion: BigNumber(engagement.clickthroughConversion), + viewthroughConversion: BigNumber(engagement.viewthroughConversion), }; }; export const reduceMetric = (a: BaseMetric, b: BaseMetric) => { return { - views: a.views + b.views, - conversions: a.conversions + b.conversions, - landings: a.landings + b.landings, - clicks: a.clicks + b.clicks, - spend: a.spend + b.spend, - upvotes: a.upvotes + b.upvotes, - downvotes: a.downvotes + b.downvotes, - dismissals: a.dismissals + b.dismissals, + views: a.views.plus(b.views), + conversions: a.conversions.plus(b.conversions), + landings: a.landings.plus(b.landings), + clicks: a.clicks.plus(b.clicks), + spend: a.spend.plus(b.spend), + upvotes: a.upvotes.plus(b.upvotes), + downvotes: a.downvotes.plus(b.downvotes), + dismissals: a.dismissals.plus(b.dismissals), + clickthroughConversion: a.clickthroughConversion.plus( + b.clickthroughConversion, + ), + viewthroughConversion: a.viewthroughConversion.plus( + b.viewthroughConversion, + ), }; }; export const processStats = ( engagements: EngagementFragment[], -): StatsMetric => { +): StatsMetric | null => { if (engagements.length === 0) { - return { - clicks: 0, - convRate: 0, - conversions: 0, - cpa: 0, - ctr: 0, - dismissRate: 0, - dismissals: 0, - downvotes: 0, - landingRate: 0, - landings: 0, - spend: 0, - upvotes: 0, - views: 0, - visitRate: 0, - }; + return null; } const reduced = engagements.map(mapMetric).reduce(reduceMetric); @@ -241,17 +232,15 @@ export const processStats = ( export function calculateMetric( isPercent: boolean, - numerator: number, - denominator: number, + numerator: BigNumber | number, + denominator: BigNumber | number, ) { - let metric: number; + let metric = BigNumber(numerator).dividedBy(denominator); if (isPercent) { - metric = (numerator / denominator) * 100; - } else { - metric = numerator / denominator; + metric = metric.multipliedBy(100); } - return metric === Infinity ? 0 : metric; + return !metric.isFinite() ? BigNumber(0) : metric.dp(3); } export const processData = ( @@ -279,21 +268,24 @@ export const processData = ( const date = moment(key).valueOf(); const data = groupedData[key]; const processed = processStats(data); + if (!processed) { + continue; + } if (metric1) { - metric1DataSet.push([date, processed[metric1.key]]); + metric1DataSet.push([date, processed[metric1.key].toNumber()]); } if (metric2) { - metric2DataSet.push([date, processed[metric2.key]]); + metric2DataSet.push([date, processed[metric2.key].toNumber()]); } if (metric3) { - metric3DataSet.push([date, processed[metric3.key]]); + metric3DataSet.push([date, processed[metric3.key].toNumber()]); } if (metric4) { - metric4DataSet.push([date, processed[metric4.key]]); + metric4DataSet.push([date, processed[metric4.key].toNumber()]); } } diff --git a/src/user/analytics/analyticsOverview/reports/campaign/EngagementsOverview.tsx b/src/user/analytics/analyticsOverview/reports/campaign/EngagementsOverview.tsx index c778024a..94ee9b84 100644 --- a/src/user/analytics/analyticsOverview/reports/campaign/EngagementsOverview.tsx +++ b/src/user/analytics/analyticsOverview/reports/campaign/EngagementsOverview.tsx @@ -69,17 +69,17 @@ export function EngagementsOverview({ } if (!engagements || engagements.length === 0) { - return ( - - Reporting not available yet for {campaign.name}. - - ); + return ; } const processedData = processData(engagements, metrics, grouping); const processedStats = processStats(engagements); const options = prepareChart(metrics, processedData); + if (!processedStats) { + return ; + } + return ( ); } + +function ReportingNotReady(props: { campaignName: string }) { + return ( + + Reporting not available yet for {props.campaignName}. + + ); +} diff --git a/src/user/analytics/analyticsOverview/types/index.ts b/src/user/analytics/analyticsOverview/types/index.ts index 98f13ea6..ac2380d8 100644 --- a/src/user/analytics/analyticsOverview/types/index.ts +++ b/src/user/analytics/analyticsOverview/types/index.ts @@ -1,3 +1,5 @@ +import BigNumber from "bignumber.js"; + export type Metrics = { metric1?: { key: keyof StatsMetric; active: boolean }; metric2?: { key: keyof StatsMetric; active: boolean }; @@ -14,23 +16,25 @@ export type OS = { }; export type BaseMetric = { - views: number; - clicks: number; - conversions: number; - landings: number; - spend: number; - upvotes: number; - downvotes: number; - dismissals: number; + views: BigNumber; + clicks: BigNumber; + conversions: BigNumber; + landings: BigNumber; + spend: BigNumber; + upvotes: BigNumber; + downvotes: BigNumber; + dismissals: BigNumber; + clickthroughConversion: BigNumber; + viewthroughConversion: BigNumber; }; export type StatsMetric = BaseMetric & { - ctr: number; - convRate: number; - landingRate: number; - dismissRate: number; - cpa: number; - visitRate: number; + ctr: BigNumber; + convRate: BigNumber; + landingRate: BigNumber; + dismissRate: BigNumber; + cpa: BigNumber; + visitRate: BigNumber; }; export type Tooltip = { @@ -44,8 +48,6 @@ export type CreativeMetric = StatsMetric & { creativePayload: { title: string; body: string }; }; -export type EngagementChartType = "campaign" | "creative" | "creativeset"; - export interface Option { value: string; label: string; diff --git a/src/user/analytics/renderers/index.tsx b/src/user/analytics/renderers/index.tsx index 6c4ce40d..51f943fa 100644 --- a/src/user/analytics/renderers/index.tsx +++ b/src/user/analytics/renderers/index.tsx @@ -3,6 +3,7 @@ import { renderMonetaryAmount } from "components/Datagrid/renderers"; import { CampaignSummaryFragment } from "graphql/campaign.generated"; import { CampaignFormat } from "graphql/types"; import { StatsMetric } from "user/analytics/analyticsOverview/types"; +import { toLocaleString } from "util/bignumber"; export type EngagementOverview = { campaignId: string; @@ -61,7 +62,11 @@ export const renderStatsCell = ( return ; } - if (!val || val[type] <= 0) { + if (!val || !val[type]) { + return -; + } + + if (val[type].lte(0) || val[type].isNaN()) { return -; } @@ -71,11 +76,11 @@ export const renderStatsCell = ( case "dismissRate": case "landingRate": case "visitRate": - return {val[type].toLocaleString()}%; + return {toLocaleString(val[type])}%; case "spend": case "cpa": return renderMonetaryAmount(val.spend, currency ?? "USD"); default: - return {val[type].toLocaleString()}; + return {toLocaleString(val[type])}; } }; diff --git a/src/user/campaignList/CampaignList.tsx b/src/user/campaignList/CampaignList.tsx index a2e264a4..b994fd60 100644 --- a/src/user/campaignList/CampaignList.tsx +++ b/src/user/campaignList/CampaignList.tsx @@ -124,7 +124,9 @@ export function CampaignList({ advertiser }: Props) { { field: "view", headerName: "Impressions", - valueGetter: ({ row }) => engagementData?.get(row.id)?.["view"] ?? "N/A", + type: "number", + valueGetter: ({ row }) => + engagementData?.get(row.id)?.["view"]?.toString(), renderCell: ({ row }) => renderEngagementCell(loading, row, "view", engagementData), align: "right", @@ -135,7 +137,9 @@ export function CampaignList({ advertiser }: Props) { { field: "click", headerName: "Clicks", - valueGetter: ({ row }) => engagementData?.get(row.id)?.["click"] ?? "N/A", + type: "number", + valueGetter: ({ row }) => + engagementData?.get(row.id)?.["click"]?.toString(), renderCell: ({ row }) => renderEngagementCell(loading, row, "click", engagementData), align: "right", @@ -146,8 +150,9 @@ export function CampaignList({ advertiser }: Props) { { field: "landed", headerName: "Site visits", + type: "number", valueGetter: ({ row }) => - engagementData?.get(row.id)?.["landed"] ?? "N/A", + engagementData?.get(row.id)?.["landed"]?.toString(), renderCell: ({ row }) => renderEngagementCell(loading, row, "landed", engagementData), align: "right", @@ -158,8 +163,9 @@ export function CampaignList({ advertiser }: Props) { { field: "ctr", headerName: "CTR", + type: "number", valueGetter: ({ row }) => - getStatFromEngagement(row, "click", "view", engagementData), + getStatFromEngagement(row, "click", "view", engagementData)?.toString(), renderCell: ({ row }) => renderStatsCell( loading, diff --git a/src/user/views/user/AdDetailTable.tsx b/src/user/views/user/AdDetailTable.tsx index c64e6c1c..58ade9b3 100644 --- a/src/user/views/user/AdDetailTable.tsx +++ b/src/user/views/user/AdDetailTable.tsx @@ -26,7 +26,7 @@ export function AdDetailTable({ { field: "spend", headerName: "Spend", - valueGetter: ({ row }) => engagements.get(row.id)?.spend ?? "N/A", + valueGetter: ({ row }) => engagements.get(row.id)?.spend?.toNumber(), renderCell: ({ row }) => renderStatsCell( loading, @@ -36,52 +36,51 @@ export function AdDetailTable({ ), align: "right", headerAlign: "right", - minWidth: 100, - maxWidth: 250, + width: 100, }, { field: "view", + type: "number", headerName: "Impressions", - valueGetter: ({ row }) => engagements.get(row.id)?.views ?? "N/A", + valueGetter: ({ row }) => engagements.get(row.id)?.views?.toString(), renderCell: ({ row }) => renderStatsCell(loading, "views", engagements.get(row.id)), align: "right", headerAlign: "right", - minWidth: 100, - maxWidth: 250, + width: 155, }, { field: "click", + type: "number", headerName: "Clicks", - valueGetter: ({ row }) => engagements.get(row.id)?.clicks, + valueGetter: ({ row }) => engagements.get(row.id)?.clicks?.toString(), renderCell: ({ row }) => renderStatsCell(loading, "clicks", engagements.get(row.id)), align: "right", headerAlign: "right", - minWidth: 100, - maxWidth: 250, + width: 125, }, { field: "landed", + type: "number", headerName: "Site Visits", - valueGetter: ({ row }) => engagements.get(row.id)?.landings, + valueGetter: ({ row }) => engagements.get(row.id)?.landings?.toString(), renderCell: ({ row }) => renderStatsCell(loading, "landings", engagements.get(row.id)), align: "right", headerAlign: "right", - minWidth: 100, - maxWidth: 250, + width: 125, }, { field: "ctr", + type: "number", headerName: "CTR", - valueGetter: ({ row }) => engagements.get(row.id)?.ctr, + valueGetter: ({ row }) => engagements.get(row.id)?.ctr?.toString(), renderCell: ({ row }) => renderStatsCell(loading, "ctr", engagements.get(row.id)), align: "right", headerAlign: "right", - minWidth: 100, - maxWidth: 250, + width: 100, }, ); } @@ -98,7 +97,7 @@ export function AdDetailTable({ sx={{ borderStyle: "none" }} initialState={{ sorting: { - sortModel: [{ field: "createdAt", sort: "desc" }], + sortModel: [{ field: "name", sort: "desc" }], }, pagination: { paginationModel: { diff --git a/src/util/bignumber.ts b/src/util/bignumber.ts new file mode 100644 index 00000000..3fa7b8a1 --- /dev/null +++ b/src/util/bignumber.ts @@ -0,0 +1,10 @@ +import BigNumber from "bignumber.js"; + +export const toLocaleString = (b?: BigNumber | number | string) => { + if (!b) return "0"; + + return BigNumber(b).dp(2).toNumber().toLocaleString("en", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); +}; From 27ac03d331d7fd62e98d00c623b2888b5bc73e43 Mon Sep 17 00:00:00 2001 From: Ian Krieger <48930920+IanKrieger@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:14:06 -0500 Subject: [PATCH 4/6] feat: use new logo (#1032) --- branding.svg | 12 ---------- brave-ads-black.svg | 12 ---------- brave-logotype-full-color.png | Bin 20494 -> 0 bytes logo.svg | 23 ++++++++++++++++++++ src/components/AppBar/LandingPageAppBar.tsx | 2 +- src/components/Navigation/Navbar.tsx | 2 +- 6 files changed, 25 insertions(+), 26 deletions(-) delete mode 100644 branding.svg delete mode 100644 brave-ads-black.svg delete mode 100644 brave-logotype-full-color.png create mode 100644 logo.svg diff --git a/branding.svg b/branding.svg deleted file mode 100644 index 6299e205..00000000 --- a/branding.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/brave-ads-black.svg b/brave-ads-black.svg deleted file mode 100644 index a89dd54b..00000000 --- a/brave-ads-black.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/brave-logotype-full-color.png b/brave-logotype-full-color.png deleted file mode 100644 index 0b9efd3d9f90f0b214f7b133dd07d7084777e619..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20494 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 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/AppBar/LandingPageAppBar.tsx b/src/components/AppBar/LandingPageAppBar.tsx index c4419235..e7b73060 100644 --- a/src/components/AppBar/LandingPageAppBar.tsx +++ b/src/components/AppBar/LandingPageAppBar.tsx @@ -7,7 +7,7 @@ import { Toolbar, Typography, } from "@mui/material"; -import ads from "../../../branding.svg"; +import ads from "../../../logo.svg"; import { Link as RouterLink, useRouteMatch } from "react-router-dom"; import { useIsAuthenticated } from "auth/hooks/queries/useIsAuthenticated"; import { useSignOut } from "auth/hooks/mutations/useSignOut"; diff --git a/src/components/Navigation/Navbar.tsx b/src/components/Navigation/Navbar.tsx index 2ca6ed18..0e7c7fbd 100644 --- a/src/components/Navigation/Navbar.tsx +++ b/src/components/Navigation/Navbar.tsx @@ -1,7 +1,7 @@ import { AppBar, Button, Divider, Stack, Toolbar } from "@mui/material"; import { DraftMenu } from "components/Navigation/DraftMenu"; -import ads from "../../../branding.svg"; +import ads from "../../../logo.svg"; import { useAdvertiser } from "auth/hooks/queries/useAdvertiser"; import { useSignOut } from "auth/hooks/mutations/useSignOut"; import { NewCampaignButton } from "components/Navigation/NewCampaignButton"; From f65373b02ffe58163d3aad946a0916efc80b7e85 Mon Sep 17 00:00:00 2001 From: Ian Krieger <48930920+IanKrieger@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:24:30 -0500 Subject: [PATCH 5/6] feat: organize campaign creation better (#1031) * feat: re-organize campaign configuration * fix: update min campaign * fix: add await --- audit-resolve.json | 2 +- .../Campaigns/CampaignDateRange.tsx | 12 +-- src/components/Segment/SegmentPicker.tsx | 9 ++- src/user/ads/ShowAdsButton.tsx | 6 +- src/user/views/adsManager/types/index.ts | 4 +- .../adSet/fields/ConversionField.tsx | 25 +++--- .../components/adSet/fields/PickerFields.tsx | 21 ++--- .../components/campaign/BudgetSettings.tsx | 12 --- .../components/campaign/CampaignSettings.tsx | 17 ++++- .../components/BillingModelSelect.tsx | 3 +- .../campaign/fields/BudgetField.tsx | 76 ++++++++----------- .../components/form/components/BaseForm.tsx | 6 -- .../advanced/components/review/Review.tsx | 3 + src/validation/CampaignSchema.tsx | 2 +- 14 files changed, 91 insertions(+), 107 deletions(-) delete mode 100644 src/user/views/adsManager/views/advanced/components/campaign/BudgetSettings.tsx diff --git a/audit-resolve.json b/audit-resolve.json index d68ba48c..d5eb2afd 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -8,4 +8,4 @@ }, "rules": {}, "version": 1 -} \ No newline at end of file +} diff --git a/src/components/Campaigns/CampaignDateRange.tsx b/src/components/Campaigns/CampaignDateRange.tsx index 76deb961..b5c27afa 100644 --- a/src/components/Campaigns/CampaignDateRange.tsx +++ b/src/components/Campaigns/CampaignDateRange.tsx @@ -23,9 +23,9 @@ export const CampaignDateRange = () => { value={parseISO(startMeta.value)} error={!!startMeta.error} helperText={startMeta.error} - onChange={(dt) => { - startHelper.setValue(formatISO(dt)); - startHelper.setTouched(true); + onChange={async (dt) => { + await startHelper.setValue(formatISO(dt)); + await startHelper.setTouched(true); }} disabled={!isDraft} /> @@ -38,9 +38,9 @@ export const CampaignDateRange = () => { value={parseISO(endMeta.value)} error={!!endMeta.error} helperText={endMeta.error} - onChange={(dt) => { - endHelper.setValue(formatISO(dt)); - endHelper.setTouched(true); + onChange={async (dt) => { + await endHelper.setValue(formatISO(dt)); + await endHelper.setTouched(true); }} /> diff --git a/src/components/Segment/SegmentPicker.tsx b/src/components/Segment/SegmentPicker.tsx index 248e4e53..d44b08de 100644 --- a/src/components/Segment/SegmentPicker.tsx +++ b/src/components/Segment/SegmentPicker.tsx @@ -41,7 +41,7 @@ export const SegmentPicker = ({ idx }: Props) => { {!targetMeta.value && ( @@ -71,10 +71,11 @@ export const SegmentPicker = ({ idx }: Props) => { {...params} label="Audiences" helperText={ - meta.error ?? - "Select the audience segments to target. Brave will decide if left untargeted." + meta.touched && !!meta.error + ? meta.error + : "Select the audience segments to target. Brave will decide if left untargeted." } - error={!!meta.error} + error={meta.touched && !!meta.error} /> )} isOptionEqualToValue={(option, value) => option.code === value.code} diff --git a/src/user/ads/ShowAdsButton.tsx b/src/user/ads/ShowAdsButton.tsx index 00415e17..6a1faea8 100644 --- a/src/user/ads/ShowAdsButton.tsx +++ b/src/user/ads/ShowAdsButton.tsx @@ -13,12 +13,12 @@ export function ShowAdsButton() { underline="none" variant="subtitle1" sx={{ cursor: "pointer" }} - onClick={() => { + onClick={async () => { setIsShowingAds(true); - helper.setValue(false); + await helper.setValue(false); }} > - Use previously created Ads + Use existing Ads ); } diff --git a/src/user/views/adsManager/types/index.ts b/src/user/views/adsManager/types/index.ts index e69b53cf..c0e1b80e 100644 --- a/src/user/views/adsManager/types/index.ts +++ b/src/user/views/adsManager/types/index.ts @@ -101,8 +101,8 @@ export const initialCreative: Creative = { export const initialAdSet: AdSetForm = { name: "", - isNotTargeting: true, - segments: [{ code: "Svp7l-zGN", name: "untargeted" }], + isNotTargeting: false, + segments: [], conversions: [], oses: [], creatives: [], diff --git a/src/user/views/adsManager/views/advanced/components/adSet/fields/ConversionField.tsx b/src/user/views/adsManager/views/advanced/components/adSet/fields/ConversionField.tsx index f345c5a0..9f5bfb0f 100644 --- a/src/user/views/adsManager/views/advanced/components/adSet/fields/ConversionField.tsx +++ b/src/user/views/adsManager/views/advanced/components/adSet/fields/ConversionField.tsx @@ -1,8 +1,9 @@ -import { Link, Stack, Typography } from "@mui/material"; +import { Button, Link, Stack, Typography } from "@mui/material"; import { ConversionFields } from "components/Conversion/ConversionFields"; import { FieldArray, FieldArrayRenderProps, useField } from "formik"; import { Conversion, initialConversion } from "../../../../../types"; import { CardContainer } from "components/Card/CardContainer"; +import { Add } from "@mui/icons-material"; interface Props { index: number; @@ -11,27 +12,31 @@ interface Props { export function ConversionField({ index }: Props) { const [, meta] = useField(`adSets.${index}.conversions`); const conversions = meta.value ?? []; + const hasConversions = conversions.length > 0; return ( {(helper: FieldArrayRenderProps) => ( <> - + Define post-engagement analytics. - {conversions.length === 0 && ( - helper.push(initialConversion)} - sx={{ cursor: "pointer" }} + sx={{ + maxWidth: 300, + borderRadius: "16px", + }} + endIcon={} > - Add Conversion Tracking + - + Add Conversion tracking + )} - {conversions.length === 1 && ( + {hasConversions && ( ("format"); return ( - <> + + + Select the interest segments and platforms you would like to target. + {format.value !== CampaignFormat.NewsDisplayAd && ( - - - Select the audience you would like to advertise to by interests. - - - + )} - - - Select the devices and platforms you would like to advertise to. - - - - + + ); } diff --git a/src/user/views/adsManager/views/advanced/components/campaign/BudgetSettings.tsx b/src/user/views/adsManager/views/advanced/components/campaign/BudgetSettings.tsx deleted file mode 100644 index b4175ea0..00000000 --- a/src/user/views/adsManager/views/advanced/components/campaign/BudgetSettings.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { BudgetField } from "user/views/adsManager/views/advanced/components/campaign/fields/BudgetField"; -import { PaymentMethodField } from "user/views/adsManager/views/advanced/components/campaign/fields/PaymentMethodField"; - -export function BudgetSettings() { - return ( - <> - - - - - ); -} diff --git a/src/user/views/adsManager/views/advanced/components/campaign/CampaignSettings.tsx b/src/user/views/adsManager/views/advanced/components/campaign/CampaignSettings.tsx index 4c233333..dcfacce0 100644 --- a/src/user/views/adsManager/views/advanced/components/campaign/CampaignSettings.tsx +++ b/src/user/views/adsManager/views/advanced/components/campaign/CampaignSettings.tsx @@ -5,24 +5,39 @@ import { LocationField } from "user/views/adsManager/views/advanced/components/c import { Typography } from "@mui/material"; import { FormatField } from "user/views/adsManager/views/advanced/components/campaign/fields/FormatField"; import { AdvertiserPrice } from "user/hooks/useAdvertiserWithPrices"; +import { BudgetField } from "user/views/adsManager/views/advanced/components/campaign/fields/BudgetField"; +import { BillingModelSelect } from "user/views/adsManager/views/advanced/components/campaign/components/BillingModelSelect"; +import { CustomPriceSelect } from "user/views/adsManager/views/advanced/components/campaign/components/CustomPriceSelect"; +import { useAdvertiser } from "auth/hooks/queries/useAdvertiser"; export function CampaignSettings(props: { prices: AdvertiserPrice[] }) { const { isDraft } = useIsEdit(); + const { advertiser } = useAdvertiser(); return ( <> - Define when you want your campaign to run. + Define how you want your campaign to run. + + + + {!advertiser.selfServiceSetPrice && ( + + )} + + {advertiser.selfServiceSetPrice && } + + {isDraft && } ); diff --git a/src/user/views/adsManager/views/advanced/components/campaign/components/BillingModelSelect.tsx b/src/user/views/adsManager/views/advanced/components/campaign/components/BillingModelSelect.tsx index 731a46c0..171a1b04 100644 --- a/src/user/views/adsManager/views/advanced/components/campaign/components/BillingModelSelect.tsx +++ b/src/user/views/adsManager/views/advanced/components/campaign/components/BillingModelSelect.tsx @@ -17,7 +17,8 @@ export function BillingModelSelect(props: { prices: AdvertiserPrice[] }) { return ( - {uiLabelsForCampaignFormat(format.value)} billing configuration + {uiLabelsForCampaignFormat(format.value)} pricing configuration + option(s) {props.prices diff --git a/src/user/views/adsManager/views/advanced/components/campaign/fields/BudgetField.tsx b/src/user/views/adsManager/views/advanced/components/campaign/fields/BudgetField.tsx index eab3b405..4cdcd40b 100644 --- a/src/user/views/adsManager/views/advanced/components/campaign/fields/BudgetField.tsx +++ b/src/user/views/adsManager/views/advanced/components/campaign/fields/BudgetField.tsx @@ -1,20 +1,17 @@ -import { InputAdornment, Stack, Typography } from "@mui/material"; -import { FormikTextField, useIsEdit } from "form/FormikHelpers"; -import { useEffect, useState } from "react"; -import { useField, useFormikContext } from "formik"; -import { CampaignForm } from "../../../../../types"; -import { differenceInHours } from "date-fns"; -import { MIN_PER_CAMPAIGN, MIN_PER_DAY } from "validation/CampaignSchema"; -import { CardContainer } from "components/Card/CardContainer"; -import { useAdvertiserWithPrices } from "user/hooks/useAdvertiserWithPrices"; -import { BillingModelSelect } from "../components/BillingModelSelect"; -import { CustomPriceSelect } from "../components/CustomPriceSelect"; +import {InputAdornment} from "@mui/material"; +import {FormikTextField, useIsEdit} from "form/FormikHelpers"; +import {useEffect, useState} from "react"; +import {useField, useFormikContext} from "formik"; +import {CampaignForm} from "../../../../../types"; +import {differenceInHours} from "date-fns"; +import {MIN_PER_CAMPAIGN, MIN_PER_DAY} from "validation/CampaignSchema"; +import {useAdvertiserWithPrices} from "user/hooks/useAdvertiserWithPrices"; export function BudgetField() { const [, , dailyBudget] = useField("dailyBudget"); - const { isDraft } = useIsEdit(); - const { data } = useAdvertiserWithPrices(); - const { values, errors } = useFormikContext(); + const {isDraft} = useIsEdit(); + const {data} = useAdvertiserWithPrices(); + const {values, errors} = useFormikContext(); const [minBudget, setMinBudget] = useState(MIN_PER_CAMPAIGN); const campaignRuntime = Math.floor( differenceInHours(new Date(values.endAt), new Date(values.startAt)) / 24, @@ -38,37 +35,24 @@ export function BudgetField() { }, [campaignRuntime, values.budget, minBudget]); return ( - - - Set a limit on how much your campaign will spend. - - - $, - endAdornment: ( - {values.currency} - ), - }} - helperText={ - errors.budget || errors.dailyBudget - ? `${errors.dailyBudget}. Minimum $${minBudget}.` - : undefined - } - error={!!errors.budget || !!errors.dailyBudget} - disabled={!isDraft && !data.selfServiceSetPrice} - /> - - {!data.selfServiceSetPrice && ( - - )} - - {data.selfServiceSetPrice && } - - + $, + endAdornment: ( + {values.currency} + ), + }} + helperText={ + errors.budget || errors.dailyBudget + ? `${errors.dailyBudget}. Minimum $${minBudget}.` + : undefined + } + error={!!errors.budget || !!errors.dailyBudget} + disabled={!isDraft && !data.selfServiceSetPrice} + /> ); } diff --git a/src/user/views/adsManager/views/advanced/components/form/components/BaseForm.tsx b/src/user/views/adsManager/views/advanced/components/form/components/BaseForm.tsx index 2bdd6335..d7b3ac81 100644 --- a/src/user/views/adsManager/views/advanced/components/form/components/BaseForm.tsx +++ b/src/user/views/adsManager/views/advanced/components/form/components/BaseForm.tsx @@ -6,7 +6,6 @@ import { PaymentButton } from "user/views/adsManager/views/advanced/components/f import { AdSetFields } from "user/views/adsManager/views/advanced/components/adSet/AdSetFields"; import { NewAdSet } from "user/views/adsManager/views/advanced/components/adSet/NewAdSet"; import { Route, Switch, useRouteMatch } from "react-router-dom"; -import { BudgetSettings } from "user/views/adsManager/views/advanced/components/campaign/BudgetSettings"; import { FormContext } from "state/context"; import { useState } from "react"; import { AdvertiserPrice } from "user/hooks/useAdvertiserWithPrices"; @@ -26,11 +25,6 @@ export function BaseForm({ hasPaymentIntent, prices }: Props) { path: `${url}/settings`, component: , }, - { - label: "Budget", - path: `${url}/budget`, - component: , - }, { label: "Ad Sets", path: `${url}/adSets`, diff --git a/src/user/views/adsManager/views/advanced/components/review/Review.tsx b/src/user/views/adsManager/views/advanced/components/review/Review.tsx index 80bc4b13..6e3de461 100644 --- a/src/user/views/adsManager/views/advanced/components/review/Review.tsx +++ b/src/user/views/adsManager/views/advanced/components/review/Review.tsx @@ -4,6 +4,7 @@ import { Box } from "@mui/material"; import { useEffect } from "react"; import { CampaignReview } from "./components/CampaignReview"; import { AdSetReview } from "./components/AdSetReview"; +import { PaymentMethodField } from "user/views/adsManager/views/advanced/components/campaign/fields/PaymentMethodField"; export function Review() { const { values, errors, setTouched } = useFormikContext(); @@ -28,6 +29,8 @@ export function Review() { errors={errors.adSets?.[adSetIdx]} /> ))} + + ); } diff --git a/src/validation/CampaignSchema.tsx b/src/validation/CampaignSchema.tsx index e42eed7e..bd0962f0 100644 --- a/src/validation/CampaignSchema.tsx +++ b/src/validation/CampaignSchema.tsx @@ -20,7 +20,7 @@ import { Billing } from "user/views/adsManager/types"; import { uiLabelsForCampaignFormat } from "util/campaign"; export const MIN_PER_DAY = 33; -export const MIN_PER_CAMPAIGN = 100; +export const MIN_PER_CAMPAIGN = 500; export const CampaignSchema = (prices: AdvertiserPrice[]) => object().shape({ From bfc3e91667ed08d3c0a72a7e5dcb5b0a3d600e8c Mon Sep 17 00:00:00 2001 From: Ian Krieger <48930920+IanKrieger@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:35:22 -0500 Subject: [PATCH 6/6] fix: lodash warning issue changed (#1033) --- audit-resolve.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audit-resolve.json b/audit-resolve.json index d5eb2afd..bd3cea1d 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -1,9 +1,9 @@ { "decisions": { - "1096292|vite-plugin-checker>lodash.pick": { + "1096303|vite-plugin-checker>lodash.pick": { "decision": "ignore", - "madeAt": 1706203247319, - "expiresAt": 1708795235684 + "madeAt": 1706290095658, + "expiresAt": 1708882081719 } }, "rules": {},