Skip to content

Commit

Permalink
feat: organize campaign creation better (#1031)
Browse files Browse the repository at this point in the history
* feat: re-organize campaign configuration

* fix: update min campaign

* fix: add await
  • Loading branch information
IanKrieger authored Jan 26, 2024
1 parent 27ac03d commit f65373b
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 107 deletions.
2 changes: 1 addition & 1 deletion audit-resolve.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
},
"rules": {},
"version": 1
}
}
12 changes: 6 additions & 6 deletions src/components/Campaigns/CampaignDateRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
Expand All @@ -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);
}}
/>

Expand Down
9 changes: 5 additions & 4 deletions src/components/Segment/SegmentPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const SegmentPicker = ({ idx }: Props) => {
<Box marginTop={3} marginLeft={1}>
<FormikSwitch
name={`adSets.${idx}.isNotTargeting`}
label="Let Brave pick categories for me."
label="Automatic interest targeting"
/>
</Box>
{!targetMeta.value && (
Expand Down Expand Up @@ -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}
Expand Down
6 changes: 3 additions & 3 deletions src/user/ads/ShowAdsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
</Link>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/user/views/adsManager/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -11,27 +12,31 @@ interface Props {
export function ConversionField({ index }: Props) {
const [, meta] = useField<Conversion[]>(`adSets.${index}.conversions`);
const conversions = meta.value ?? [];
const hasConversions = conversions.length > 0;

return (
<CardContainer header="Conversion">
<FieldArray name={`adSets.${index}.conversions`}>
{(helper: FieldArrayRenderProps) => (
<>
<Stack direction="row" spacing={1}>
<Stack direction={hasConversions ? "row" : "column"} spacing={1}>
<Typography variant="body2" sx={{ mb: 2 }}>
Define post-engagement analytics.
</Typography>
{conversions.length === 0 && (
<Link
underline="none"
variant="body2"
{!hasConversions && (
<Button
variant="contained"
onClick={() => helper.push(initialConversion)}
sx={{ cursor: "pointer" }}
sx={{
maxWidth: 300,
borderRadius: "16px",
}}
endIcon={<Add />}
>
Add Conversion Tracking +
</Link>
Add Conversion tracking
</Button>
)}
{conversions.length === 1 && (
{hasConversions && (
<Link
underline="none"
variant="body2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,15 @@ export function PickerFields({ index }: Props) {
const [, format] = useField<CampaignFormat>("format");

return (
<>
<CardContainer header="Targeting">
<Typography variant="body2" sx={{ mb: 2 }}>
Select the interest segments and platforms you would like to target.
</Typography>
{format.value !== CampaignFormat.NewsDisplayAd && (
<CardContainer header="Categories">
<Typography variant="body2" sx={{ mb: 2 }}>
Select the audience you would like to advertise to by interests.
</Typography>
<SegmentPicker idx={index} />
</CardContainer>
<SegmentPicker idx={index} />
)}

<CardContainer header="Platforms">
<Typography variant="body2" sx={{ mb: 2 }}>
Select the devices and platforms you would like to advertise to.
</Typography>
<PlatformPicker idx={index} />
</CardContainer>
</>
<PlatformPicker idx={index} />
</CardContainer>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<CardContainer header="Campaign Settings">
<Typography variant="body2" gutterBottom>
Define when you want your campaign to run.
Define how you want your campaign to run.
</Typography>

<FormikTextField name="name" label="Campaign Name" sx={{ mb: 1 }} />

<BudgetField />

<CampaignDateRange />
</CardContainer>

<FormatField prices={props.prices} />

<CardContainer header="Pricing">
{!advertiser.selfServiceSetPrice && (
<BillingModelSelect prices={props.prices} />
)}

{advertiser.selfServiceSetPrice && <CustomPriceSelect />}
</CardContainer>

{isDraft && <LocationField />}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export function BillingModelSelect(props: { prices: AdvertiserPrice[] }) {
return (
<Stack maxWidth={500}>
<Typography variant="body2">
{uiLabelsForCampaignFormat(format.value)} billing configuration
{uiLabelsForCampaignFormat(format.value)} pricing configuration
option(s)
</Typography>
<List sx={{ display: "flex", flexDirection: "row", gap: "20px" }}>
{props.prices
Expand Down
Original file line number Diff line number Diff line change
@@ -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<number>("dailyBudget");
const { isDraft } = useIsEdit();
const { data } = useAdvertiserWithPrices();
const { values, errors } = useFormikContext<CampaignForm>();
const {isDraft} = useIsEdit();
const {data} = useAdvertiserWithPrices();
const {values, errors} = useFormikContext<CampaignForm>();
const [minBudget, setMinBudget] = useState(MIN_PER_CAMPAIGN);
const campaignRuntime = Math.floor(
differenceInHours(new Date(values.endAt), new Date(values.startAt)) / 24,
Expand All @@ -38,37 +35,24 @@ export function BudgetField() {
}, [campaignRuntime, values.budget, minBudget]);

return (
<CardContainer header="Budget">
<Typography variant="body2" gutterBottom>
Set a limit on how much your campaign will spend.
</Typography>
<Stack direction="column" spacing={2}>
<FormikTextField
name="budget"
label="Lifetime Budget"
margin="normal"
type="number"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
endAdornment: (
<InputAdornment position="end">{values.currency}</InputAdornment>
),
}}
helperText={
errors.budget || errors.dailyBudget
? `${errors.dailyBudget}. Minimum $${minBudget}.`
: undefined
}
error={!!errors.budget || !!errors.dailyBudget}
disabled={!isDraft && !data.selfServiceSetPrice}
/>

{!data.selfServiceSetPrice && (
<BillingModelSelect prices={data.prices} />
)}

{data.selfServiceSetPrice && <CustomPriceSelect />}
</Stack>
</CardContainer>
<FormikTextField
name="budget"
label="Lifetime Budget"
margin="normal"
type="number"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
endAdornment: (
<InputAdornment position="end">{values.currency}</InputAdornment>
),
}}
helperText={
errors.budget || errors.dailyBudget
? `${errors.dailyBudget}. Minimum $${minBudget}.`
: undefined
}
error={!!errors.budget || !!errors.dailyBudget}
disabled={!isDraft && !data.selfServiceSetPrice}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -26,11 +25,6 @@ export function BaseForm({ hasPaymentIntent, prices }: Props) {
path: `${url}/settings`,
component: <CampaignSettings prices={prices} />,
},
{
label: "Budget",
path: `${url}/budget`,
component: <BudgetSettings />,
},
{
label: "Ad Sets",
path: `${url}/adSets`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<CampaignForm>();
Expand All @@ -28,6 +29,8 @@ export function Review() {
errors={errors.adSets?.[adSetIdx]}
/>
))}

<PaymentMethodField />
</Box>
);
}
2 changes: 1 addition & 1 deletion src/validation/CampaignSchema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down

0 comments on commit f65373b

Please sign in to comment.