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] 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({