diff --git a/all_sizes_frame.svg b/all_sizes_frame.svg
new file mode 100644
index 00000000..054ee9ea
--- /dev/null
+++ b/all_sizes_frame.svg
@@ -0,0 +1,43 @@
+
diff --git a/powerful_format_frame.svg b/powerful_format_frame.svg
new file mode 100644
index 00000000..fcc9b62f
--- /dev/null
+++ b/powerful_format_frame.svg
@@ -0,0 +1,37 @@
+
diff --git a/privacy_focused_frame.svg b/privacy_focused_frame.svg
new file mode 100644
index 00000000..72a362a4
--- /dev/null
+++ b/privacy_focused_frame.svg
@@ -0,0 +1,51 @@
+
diff --git a/src/auth/hooks/mutations/useGetLink.ts b/src/auth/hooks/mutations/useGetLink.ts
index 46039b7f..3e043260 100644
--- a/src/auth/hooks/mutations/useGetLink.ts
+++ b/src/auth/hooks/mutations/useGetLink.ts
@@ -11,6 +11,12 @@ export function useGetLink({ onError, onSuccess }: Options = {}) {
const [error, setError] = useState();
const requestLink = useCallback(async (email: string) => {
+ if (email.trim() === "") {
+ setError("Please enter an email.");
+ return;
+ }
+
+ setError(undefined);
setLoading(true);
await getLink({ email })
.then(() => {
diff --git a/src/auth/registration/AddressField.tsx b/src/auth/registration/AddressField.tsx
index 6bea8c2b..a128996f 100644
--- a/src/auth/registration/AddressField.tsx
+++ b/src/auth/registration/AddressField.tsx
@@ -1,6 +1,7 @@
-import { Box, Divider, Stack, Typography } from "@mui/material";
+import { Box, Stack, Typography } from "@mui/material";
import { FormikTextField } from "form/FormikHelpers";
import { CountryPicker } from "components/Country/CountryPicker";
+import { MarginedDivider } from "auth/registration/MarginedDivider";
export function AddressField() {
return (
@@ -10,7 +11,7 @@ export function AddressField() {
finished here, your account setup is complete.
-
+
-
+
In the meantime, check out our{" "}
-
+
advertiser resources
. If you have any questions, please reach out to{" "}
diff --git a/src/auth/registration/MarginedDivider.tsx b/src/auth/registration/MarginedDivider.tsx
new file mode 100644
index 00000000..522f46b7
--- /dev/null
+++ b/src/auth/registration/MarginedDivider.tsx
@@ -0,0 +1,9 @@
+import { Divider } from "@mui/material";
+
+export function MarginedDivider() {
+ return (
+
+ );
+}
diff --git a/src/auth/registration/NameField.tsx b/src/auth/registration/NameField.tsx
index b500af85..bce9d986 100644
--- a/src/auth/registration/NameField.tsx
+++ b/src/auth/registration/NameField.tsx
@@ -1,5 +1,6 @@
-import { Box, Divider, Typography } from "@mui/material";
+import { Box, Typography } from "@mui/material";
import { FormikTextField } from "form/FormikHelpers";
+import { MarginedDivider } from "auth/registration/MarginedDivider";
export function NameField() {
return (
@@ -9,7 +10,7 @@ export function NameField() {
setup with your account. First, we’ll need your info.
-
+
},
- { label: "Your business’s information", component: },
+ { label: "Your business's information", component: },
];
return (
-
-
+
+
{steps[activeStep].label}
diff --git a/src/auth/views/LandingPage.tsx b/src/auth/views/LandingPage.tsx
index 5a9850e6..67c6a8a1 100644
--- a/src/auth/views/LandingPage.tsx
+++ b/src/auth/views/LandingPage.tsx
@@ -1,9 +1,11 @@
import { Background } from "components/Background/Background";
import { LandingPageAppBar } from "components/AppBar/LandingPageAppBar";
-import { Box, Button, Link, Stack, Typography } from "@mui/material";
+import { Box, Button, Link, Stack, Toolbar, Typography } from "@mui/material";
import goals from "../../../images.svg";
import { useIsAuthenticated } from "auth/hooks/queries/useIsAuthenticated";
import { Link as RouterLink } from "react-router-dom";
+import { useIsMobile } from "hooks/useIsMobile";
+import { MobileAdsBenefits } from "auth/views/MobileAdsBenefits";
const GradientText = {
backgroundImage:
@@ -14,19 +16,21 @@ const GradientText = {
export function LandingPage() {
const isAuthenticated = useIsAuthenticated();
+ const isMobile = useIsMobile();
return (
+
-
-
-
+
+
+
Privacy-forward
{" "}
advertising made simple
@@ -42,17 +46,16 @@ export function LandingPage() {
variant="contained"
component={RouterLink}
sx={{
- maxWidth: "165px",
- height: "60px",
- padding: "18px 24px 18px 24px",
+ maxWidth: { md: "165px" },
+ maxHeight: { xs: "40px", md: "60px" },
+ padding: { xs: 2, md: "18px 24px 18px 24px" },
mb: 1,
}}
- size="large"
to={isAuthenticated ? "/user/main" : "/register"}
>
{isAuthenticated ? "Dashboard" : "Get Started"}
- {!isAuthenticated && (
+ {!isMobile && !isAuthenticated && (
Already have an account?
)}
+
+ {isMobile && }
-
+ {!isMobile && }
);
diff --git a/src/auth/views/MagicLink.tsx b/src/auth/views/MagicLink.tsx
index c8cabdb3..784d7ae5 100644
--- a/src/auth/views/MagicLink.tsx
+++ b/src/auth/views/MagicLink.tsx
@@ -1,6 +1,6 @@
import { useState } from "react";
-import { Link, Stack, TextField, Typography } from "@mui/material";
+import { Link, TextField, Typography } from "@mui/material";
import { Link as RouterLink } from "react-router-dom";
import { useGetLink } from "auth/hooks/mutations/useGetLink";
import { LoadingButton } from "@mui/lab";
@@ -26,20 +26,18 @@ export function MagicLink() {
Click on the secure login link in the email to access your Brave Ads
account.
-
-
- Don’t see the email? Check your spam folder or
-
+
+ Don’t see the email? Check your spam folder or{" "}
{
setRequested(false);
}}
>
try again.
-
+
);
}
diff --git a/src/auth/views/MobileAdsBenefits.tsx b/src/auth/views/MobileAdsBenefits.tsx
new file mode 100644
index 00000000..8c1763fc
--- /dev/null
+++ b/src/auth/views/MobileAdsBenefits.tsx
@@ -0,0 +1,66 @@
+import { Card, CardMedia, List, ListItem, Typography } from "@mui/material";
+
+import allSizes from "../../../all_sizes_frame.svg";
+import powerfulFormats from "../../../powerful_format_frame.svg";
+import privacyFocused from "../../../privacy_focused_frame.svg";
+
+export function MobileAdsBenefits() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const ItemCard = (props: {
+ primary: string;
+ secondary: string;
+ image: string;
+}) => {
+ return (
+
+
+
+ {props.primary}
+
+
+ {props.secondary}
+
+
+ );
+};
diff --git a/src/auth/views/components/AuthContainer.tsx b/src/auth/views/components/AuthContainer.tsx
index 07167df9..e865e50d 100644
--- a/src/auth/views/components/AuthContainer.tsx
+++ b/src/auth/views/components/AuthContainer.tsx
@@ -1,7 +1,8 @@
-import { Box, Card, CardContent } from "@mui/material";
+import { Box } from "@mui/material";
import { Background } from "components/Background/Background";
import { LandingPageAppBar } from "components/AppBar/LandingPageAppBar";
import { ReactNode } from "react";
+import { PaddedCardContainer } from "components/Card/PaddedCardContainer";
interface Props {
children?: ReactNode;
@@ -13,25 +14,9 @@ export function AuthContainer({ children, belowCard, aboveCard }: Props) {
return (
-
+
{aboveCard}
-
-
- {children}
-
-
+ {children}
{belowCard}
diff --git a/src/components/AppBar/LandingPageAppBar.tsx b/src/components/AppBar/LandingPageAppBar.tsx
index 99436d56..f5a8abfe 100644
--- a/src/components/AppBar/LandingPageAppBar.tsx
+++ b/src/components/AppBar/LandingPageAppBar.tsx
@@ -2,7 +2,6 @@ import {
AppBar,
Box,
Button,
- CssBaseline,
Divider,
Link,
LinkProps,
@@ -15,20 +14,23 @@ import { Link as RouterLink, useRouteMatch } from "react-router-dom";
import { useIsAuthenticated } from "auth/hooks/queries/useIsAuthenticated";
import { useSignOut } from "auth/hooks/mutations/useSignOut";
import { SupportMenu } from "components/Drawer/MiniSideBar";
+import { useIsMobile } from "hooks/useIsMobile";
export function LandingPageAppBar() {
const match = useRouteMatch();
const isAuthenticated = useIsAuthenticated();
+ const isMobile = useIsMobile();
const links = [
{
- component: isAuthenticated ? null : (
-
-
- Get started
-
-
- ),
+ component:
+ isMobile || isAuthenticated ? null : (
+
+
+ Get started
+
+
+ ),
},
{
component: (
@@ -48,26 +50,47 @@ export function LandingPageAppBar() {
return (
-
-
-
+
+
-
+
-
+
+
{links.map((l) => l.component)}
+ {isMobile && !match.url.includes("auth") && (
+
+ Log in
+
+ )}
- {!match.url.includes("auth") && (
+ {!isMobile && !match.url.includes("auth") && (
)}
diff --git a/src/components/Button/DashboardButton.tsx b/src/components/Button/DashboardButton.tsx
index 946480d4..7a7220de 100644
--- a/src/components/Button/DashboardButton.tsx
+++ b/src/components/Button/DashboardButton.tsx
@@ -1,15 +1,14 @@
import { Button } from "@mui/material";
import ArrowBack from "@mui/icons-material/ArrowBack";
-import { useHistory } from "react-router-dom";
+import { Link as RouterLink } from "react-router-dom";
export function DashboardButton() {
- const history = useHistory();
-
return (
}
- onClick={() => history.replace("/user/main")}
+ component={RouterLink}
+ to="/user/main"
>
Dashboard
diff --git a/src/components/Button/DashboardIconButton.tsx b/src/components/Button/DashboardIconButton.tsx
deleted file mode 100644
index 7a17145d..00000000
--- a/src/components/Button/DashboardIconButton.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { IconButton, Tooltip } from "@mui/material";
-import ArrowBack from "@mui/icons-material/ArrowBack";
-import { useHistory } from "react-router-dom";
-
-export function DashboardIconButton() {
- const history = useHistory();
-
- return (
-
- history.push("/user/main")}>
-
-
-
- );
-}
diff --git a/src/components/Campaigns/CampaignDateRange.tsx b/src/components/Campaigns/CampaignDateRange.tsx
index 18562fee..76deb961 100644
--- a/src/components/Campaigns/CampaignDateRange.tsx
+++ b/src/components/Campaigns/CampaignDateRange.tsx
@@ -6,8 +6,10 @@ import { useField } from "formik";
import { useState } from "react";
import { getDefaultTimezone, TimeZonePicker } from "../TimeZonePicker";
import { TimezoneAwareDatePicker } from "../TimeZonePicker/TimezoneAwareDatePicker";
+import { useIsEdit } from "form/FormikHelpers";
-export const CampaignDateRange = ({ isEdit }: { isEdit: boolean }) => {
+export const CampaignDateRange = () => {
+ const { isDraft } = useIsEdit();
const [tz, setTz] = useState(getDefaultTimezone());
const [, startMeta, startHelper] = useField("startAt");
const [, endMeta, endHelper] = useField("endAt");
@@ -25,7 +27,7 @@ export const CampaignDateRange = ({ isEdit }: { isEdit: boolean }) => {
startHelper.setValue(formatISO(dt));
startHelper.setTouched(true);
}}
- disabled={isEdit}
+ disabled={!isDraft}
/>
diff --git a/src/components/Card/PaddedCardContainer.tsx b/src/components/Card/PaddedCardContainer.tsx
index 811943ac..715b825d 100644
--- a/src/components/Card/PaddedCardContainer.tsx
+++ b/src/components/Card/PaddedCardContainer.tsx
@@ -6,7 +6,7 @@ export function PaddedCardContainer({ children }: PropsWithChildren) {
diff --git a/src/components/Drawer/MiniSideBar.tsx b/src/components/Drawer/MiniSideBar.tsx
index 01335fc1..ac761567 100644
--- a/src/components/Drawer/MiniSideBar.tsx
+++ b/src/components/Drawer/MiniSideBar.tsx
@@ -30,7 +30,7 @@ type RouteOption = {
onClick?: (event: MouseEvent) => void;
};
-const drawerWidth = 120;
+const drawerWidth = 100;
export default function MiniSideBar({ children }: PropsWithChildren) {
const dashboardRoutes: RouteOption[] = [
{
@@ -151,8 +151,8 @@ const ItemBox = (props: RouteOption) => {
borderRadius: "0px",
gap: "3px",
visibility: props.disabled ? "hidden" : "visible",
- paddingLeft: "5px",
- paddingRight: "5px",
+ paddingLeft: "3px",
+ paddingRight: "3px",
}}
selected={match.url.includes(props.href)}
onClick={props.onClick}
diff --git a/src/components/Navigation/DraftMenu.tsx b/src/components/Navigation/DraftMenu.tsx
index 929f64ac..0a4d7736 100644
--- a/src/components/Navigation/DraftMenu.tsx
+++ b/src/components/Navigation/DraftMenu.tsx
@@ -1,13 +1,12 @@
import { useContext, useState, MouseEvent } from "react";
-import { useHistory } from "react-router-dom";
+import { Link as RouterLink } from "react-router-dom";
import { Badge, Button, Menu, MenuItem } from "@mui/material";
import { DraftContext } from "state/context";
export function DraftMenu() {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
- const history = useHistory();
const { drafts } = useContext(DraftContext);
const handleClick = (event: MouseEvent) => {
@@ -46,10 +45,9 @@ export function DraftMenu() {
{drafts.map((d, idx) => (
))}
- {values.adSets.length <= 4 && !props.isEdit && (
+ {values.adSets.length <= 4 && !isEdit && (
-
+
-
+
>
);
}
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 6ff85fda..61812183 100644
--- a/src/user/views/adsManager/views/advanced/components/campaign/CampaignSettings.tsx
+++ b/src/user/views/adsManager/views/advanced/components/campaign/CampaignSettings.tsx
@@ -1,14 +1,11 @@
-import { FormikTextField } from "form/FormikHelpers";
+import { FormikTextField, useIsEdit } from "form/FormikHelpers";
import { CardContainer } from "components/Card/CardContainer";
import { CampaignDateRange } from "components/Campaigns/CampaignDateRange";
import { LocationField } from "user/views/adsManager/views/advanced/components/campaign/fields/LocationField";
import { Typography } from "@mui/material";
-import { useFormikContext } from "formik";
-import { CampaignForm } from "user/views/adsManager/types";
-export function CampaignSettings(props: { isEdit: boolean }) {
- const { values } = useFormikContext();
- const isEdit = props.isEdit && values.state !== "draft";
+export function CampaignSettings() {
+ const { isDraft } = useIsEdit();
return (
<>
@@ -19,10 +16,10 @@ export function CampaignSettings(props: { isEdit: boolean }) {
-
+
- {!isEdit && }
+ {isDraft && }
>
);
}
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 dc7c56ba..9232a52c 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,7 +1,11 @@
import { InputAdornment, Stack, Typography } from "@mui/material";
-import { FormikRadioControl, FormikTextField } from "form/FormikHelpers";
+import {
+ FormikRadioControl,
+ FormikTextField,
+ useIsEdit,
+} from "form/FormikHelpers";
import { useEffect, useState } from "react";
-import { useFormikContext } from "formik";
+import { useField, useFormikContext } from "formik";
import { CampaignForm } from "../../../../../types";
import { differenceInHours } from "date-fns";
import { MIN_PER_CAMPAIGN, MIN_PER_DAY } from "validation/CampaignSchema";
@@ -10,20 +14,18 @@ import _ from "lodash";
import { CardContainer } from "components/Card/CardContainer";
import { uiLabelsForBillingType } from "util/billingType";
-interface Props {
- isEdit: boolean;
-}
-
-export function BudgetField({ isEdit }: Props) {
+export function BudgetField() {
+ const [, , dailyBudget] = useField("dailyBudget");
+ const { isDraft } = useIsEdit();
const { advertiser } = useAdvertiser();
- const { values, setFieldValue, errors } = useFormikContext();
+ 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,
);
useEffect(() => {
- const dailyBudget =
+ const calculatedBudget =
campaignRuntime > 0
? Math.floor(Number(values.budget) / campaignRuntime)
: values.budget;
@@ -36,7 +38,7 @@ export function BudgetField({ isEdit }: Props) {
setMinBudget(minLifetime);
}
- setFieldValue("dailyBudget", dailyBudget);
+ dailyBudget.setValue(calculatedBudget);
}, [campaignRuntime, values.budget, minBudget]);
return (
@@ -62,11 +64,7 @@ export function BudgetField({ isEdit }: Props) {
: undefined
}
error={!!errors.budget || !!errors.dailyBudget}
- disabled={
- isEdit &&
- !advertiser.selfServiceSetPrice &&
- values.state !== "draft"
- }
+ disabled={!isDraft && !advertiser.selfServiceSetPrice}
/>
{!advertiser.selfServiceSetPrice ? (
@@ -89,7 +87,7 @@ export function BudgetField({ isEdit }: Props) {
$
),
}}
- disabled={isEdit && values.state !== "draft"}
+ disabled={!isDraft}
/>
)}
diff --git a/src/user/views/adsManager/views/advanced/components/campaign/fields/PaymentMethodField.tsx b/src/user/views/adsManager/views/advanced/components/campaign/fields/PaymentMethodField.tsx
index 18aeb227..37c734a1 100644
--- a/src/user/views/adsManager/views/advanced/components/campaign/fields/PaymentMethodField.tsx
+++ b/src/user/views/adsManager/views/advanced/components/campaign/fields/PaymentMethodField.tsx
@@ -1,17 +1,11 @@
import { Stack, Typography } from "@mui/material";
-import { FormikRadioControl } from "form/FormikHelpers";
+import { FormikRadioControl, useIsEdit } from "form/FormikHelpers";
import { PaymentType } from "graphql/types";
-import { useFormikContext } from "formik";
-import { CampaignForm } from "user/views/adsManager/types";
import { useAdvertiser } from "auth/hooks/queries/useAdvertiser";
import { CardContainer } from "components/Card/CardContainer";
-interface Props {
- isEdit: boolean;
-}
-
-export function PaymentMethodField({ isEdit }: Props) {
- const { values } = useFormikContext();
+export function PaymentMethodField() {
+ const { isDraft } = useIsEdit();
const { advertiser } = useAdvertiser();
if (advertiser.selfServiceSetPrice) {
@@ -26,7 +20,7 @@ export function PaymentMethodField({ isEdit }: Props) {
begin.
();
const [clickedSurvey, setClickedSurvey] = useState(false);
const searchParams = new URLSearchParams(window.location.search);
@@ -52,7 +51,8 @@ export function CompletionForm() {
sx={{ mt: 2, flexGrow: 0 }}
disabled={loading}
loading={loading}
- onClick={() => history.push("/user/main/campaigns")}
+ component={RouterLink}
+ to="/user/main/campaigns"
>
Continue
@@ -104,8 +104,6 @@ function ValidateCampaignButton(props: {
loading: boolean;
onClick: () => void;
}) {
- const history = useHistory();
-
return (
{
- history.push("/user/main");
- }}
+ component={RouterLink}
+ to="/user/main/campaign"
loading={props.loading}
disabled={props.loading}
>
diff --git a/src/user/views/adsManager/views/advanced/components/form/EditCampaign.tsx b/src/user/views/adsManager/views/advanced/components/form/EditCampaign.tsx
index 6bea4466..a5bdadd0 100644
--- a/src/user/views/adsManager/views/advanced/components/form/EditCampaign.tsx
+++ b/src/user/views/adsManager/views/advanced/components/form/EditCampaign.tsx
@@ -85,7 +85,7 @@ export function EditCampaign() {
}}
validationSchema={CampaignSchema}
>
-
+
);
diff --git a/src/user/views/adsManager/views/advanced/components/form/NewCampaign.tsx b/src/user/views/adsManager/views/advanced/components/form/NewCampaign.tsx
index e62136fc..dc4e3a5d 100644
--- a/src/user/views/adsManager/views/advanced/components/form/NewCampaign.tsx
+++ b/src/user/views/adsManager/views/advanced/components/form/NewCampaign.tsx
@@ -78,7 +78,7 @@ export function NewCampaign() {
validationSchema={CampaignSchema}
>
<>
-
+
>
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 1b126b45..a2240756 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
@@ -9,23 +9,19 @@ import { NewAdSet } from "user/views/adsManager/views/advanced/components/adSet/
import { Route, Switch, useRouteMatch } from "react-router-dom";
import { BudgetSettings } from "user/views/adsManager/views/advanced/components/campaign/BudgetSettings";
-interface Props {
- isEdit: boolean;
-}
-
-export function BaseForm({ isEdit }: Props) {
+export function BaseForm() {
const { url } = useRouteMatch();
const steps = [
{
label: "Campaign Settings",
path: `${url}/settings`,
- component: ,
+ component: ,
},
{
label: "Budget",
path: `${url}/budget`,
- component: ,
+ component: ,
},
{
label: "Ads",
@@ -36,8 +32,8 @@ export function BaseForm({ isEdit }: Props) {
label: "Ad Sets",
path: `${url}/adSets`,
queryParams: "?current=0",
- content: ,
- component: ,
+ content: ,
+ component: ,
},
{
label: "Review",
@@ -48,10 +44,7 @@ export function BaseForm({ isEdit }: Props) {
return (