Skip to content

Commit

Permalink
feat: creative management screen (#928)
Browse files Browse the repository at this point in the history
* wip: add creative management screen

* wip: add creative management screen

* feat: create and edit view, change verbiage, introduce floating button

* fix: schema

* feat: fix dirty submit

* fix: validation for inline creative

* fix: submit from dialog

* fix: make state rules more clear

* fix: submit process

* fix: spelling

* fix: grammar
  • Loading branch information
IanKrieger authored Oct 12, 2023
1 parent 57f947b commit 1e542ed
Show file tree
Hide file tree
Showing 36 changed files with 1,474 additions and 150 deletions.
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@mui/icons-material": "5.14.9",
"@mui/lab": "5.0.0-alpha.145",
"@mui/material": "5.14.10",
"@mui/x-data-grid": "6.16.1",
"@mui/x-date-pickers": "5.0.20",
"axios": "1.5.0",
"base64url": "3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/auth/registration/Register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { RegistrationSchema } from "validation/RegistrationSchema";
import { initialValues, RegistrationForm } from "auth/registration/types";
import { NameField } from "auth/registration/NameField";
import { AddressField } from "auth/registration/AddressField";
import { FormikSubmitButton } from "form/FormikHelpers";
import { FormikSubmitButton } from "form/FormikButton";
import { useRegister } from "auth/hooks/mutations/useRegister";
import { AdvertiserRegistered } from "auth/registration/AdvertiserRegistered";
import { NextAndBack } from "components/Steps/NextAndBack";
Expand Down
7 changes: 3 additions & 4 deletions src/components/Assets/ImageAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ type ImageOption = { label: string; image?: string };

const filter = createFilterOptions<ImageOption>();

export function ImageAutocomplete() {
export function ImageAutocomplete(props: { name: string }) {
const [createImage, setCreateImage] = useState(false);
const [, meta, imageUrl] = useField<string | undefined>(
`newCreative.payloadInlineContent.imageUrl`,
);
const [, meta, imageUrl] = useField<string | undefined>(props.name);
const hasError = Boolean(meta.error);
const showError = hasError && meta.touched;
const { advertiser } = useAdvertiser();
Expand Down Expand Up @@ -60,6 +58,7 @@ export function ImageAutocomplete() {
imageUrl.setValue(nv ? nv.image : undefined);
setCreateImage(nv != null && nv.image === undefined);
}}
isOptionEqualToValue={(option, value) => option.image === value.image}
/>

<UploadImage
Expand Down
19 changes: 19 additions & 0 deletions src/components/Button/FormatHelp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IconButton } from "@mui/material";
import HelpIcon from "@mui/icons-material/Help";

export function FormatHelp() {
return (
<IconButton
size="small"
onClick={() =>
window.open(
"https://brave.com/brave-ads/ad-formats/",
"__blank",
"noopener",
)
}
>
<HelpIcon fontSize="small" />
</IconButton>
);
}
88 changes: 88 additions & 0 deletions src/components/Button/SubmitPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Box, Link, Paper, Slide } from "@mui/material";
import { PropsWithChildren, ReactNode, useState } from "react";
import { useFormikContext } from "formik";
import {
extractErrors,
FormikDialogButton,
FormikSubmitButton,
} from "form/FormikButton";

function StatusMessage({
errors,
isDirty,
}: {
errors: string[];
isDirty: boolean;
}): ReactNode {
const [showErrors, setShowErrors] = useState(false);

if (errors.length === 0) {
return isDirty ? "You have unsaved changes" : null;
}

if (errors.length === 1) {
return errors[0];
}

return (
<Box>
<Link underline="hover" onClick={() => setShowErrors((state) => !state)}>
You have {errors.length} errors that must be fixed before submitting.
</Link>
{showErrors && (
<ul>
{errors.map((v, idx) => (
<li key={idx}>{`${v}`}</li>
))}
</ul>
)}
</Box>
);
}

interface Props {
isCreate: boolean;
hasDialog?: boolean;
dialogTitle?: string;
dialogMessage?: string;
}

export function SubmitPanel(props: PropsWithChildren<Props>) {
const { dirty, errors, submitCount } = useFormikContext();
// when creating a new item, we don't want to bombard with a whole load
// of validation errors. So wait until it's been submitted at least once
// before dumping the set of things that need to be completed.
const errorStrings =
props.isCreate && submitCount < 1 ? [] : extractErrors(errors);

return (
<Slide in={true} direction="up">
<Paper
elevation={8}
sx={{
p: 2,
backgroundColor: "#eeeeee",
position: "sticky",
bottom: 0,
zIndex: 9999,
borderRadius: "16px",
}}
>
<Box display="flex" justifyContent="flex-end" alignItems="end" gap={2}>
<Box flex={1} alignSelf="center">
<StatusMessage errors={errorStrings} isDirty={dirty} />
</Box>

{props.hasDialog && props.dialogTitle && props.dialogMessage && (
<FormikDialogButton
{...props}
dialogMessage={props.dialogMessage}
dialogTitle={props.dialogTitle}
/>
)}
{!props.hasDialog && <FormikSubmitButton {...props} />}
</Box>
</Paper>
</Slide>
);
}
6 changes: 5 additions & 1 deletion src/components/Card/CardContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export function CardContainer(
additionalAction?: ReactNode;
sx?: SxProps;
childSx?: SxProps;
useTypography?: boolean;
} & PropsWithChildren,
) {
return (
Expand All @@ -19,7 +20,10 @@ export function CardContainer(
alignItems="center"
mb={1}
>
{props.header && <Typography variant="h2">{props.header}</Typography>}
{props.header && props.useTypography && (
<Typography variant="h2">{props.header}</Typography>
)}
{!props.useTypography && <>{props.header}</>}
{props.additionalAction && <Box>{props.additionalAction}</Box>}
</Stack>
)}
Expand Down
69 changes: 69 additions & 0 deletions src/components/Creatives/CreativeCampaigns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { CampaignsForCreativeQuery } from "graphql/creative.generated";
import { Link as RouterLink } from "react-router-dom";
import {
Link,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from "@mui/material";
import { ErrorDetail } from "components/Error/ErrorDetail";
import { CardContainer } from "components/Card/CardContainer";
import _ from "lodash";
import { Status } from "components/Campaigns/Status";
import { ApolloError } from "@apollo/client";

interface Props {
data?: CampaignsForCreativeQuery;
error?: ApolloError;
loading: boolean;
}

export default function CreativeCampaigns({ data, error, loading }: Props) {
if (loading || !data || !data.creativeCampaigns) {
return null;
}

if (error) {
return (
<ErrorDetail
error={error}
additionalDetails="Unable to get campaign information for creative"
/>
);
}

const campaigns = _.uniqBy(data.creativeCampaigns, "id");
return (
<CardContainer header="Campaigns">
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{campaigns.map((c) => (
<TableRow key={c.id}>
<TableCell>
<Link
component={RouterLink}
to={`/user/main/campaign/${c.id}`}
underline="none"
color="secondary"
>
{c.name}
</Link>
</TableCell>
<TableCell>
<Status state={c.state} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContainer>
);
}
Loading

0 comments on commit 1e542ed

Please sign in to comment.