Skip to content

Commit

Permalink
feat(account balance): add functionality to close campaigns and trans…
Browse files Browse the repository at this point in the history
…fer balance (#1356)

Allows for a user to close their campaign, and attribute the funds to a
different campaign.

This is the second to last step in:
brave/ads-serve#4050
  • Loading branch information
IanKrieger authored Oct 4, 2024
1 parent 59cb592 commit 56fc83f
Show file tree
Hide file tree
Showing 16 changed files with 475 additions and 16 deletions.
134 changes: 134 additions & 0 deletions src/components/Campaigns/CloseCampaignModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { graphql } from "@/graphql-client/index";
import { Box, Button, Modal, Stack, Typography } from "@mui/material";
import { useContext, useState } from "react";
import { modalStyles } from "@/theme";
import { useAdvertiser } from "@/auth/hooks/queries/useAdvertiser";
import { useMutation } from "@apollo/client";
import BigNumber from "bignumber.js";
import { Trans } from "@lingui/macro";
import { useHistory } from "react-router-dom";
import { FilterContext } from "@/state/context";
import {
AdvertiserCampaignsDocument,
CampaignSummaryFragment,
} from "@/graphql-client/graphql";
import CancelIcon from "@mui/icons-material/Cancel";

const ForceCampaignComplete = graphql(`
mutation ForceCampaignComplete($id: String!) {
forceCampaignCompletionAndTransferFunds(id: $id)
}
`);

interface Props {
campaign?: Pick<
CampaignSummaryFragment,
"id" | "hasInProcessOrCompleteTransfer" | "adsManagerCurrentBalance"
>;
type: "form" | "inline";
disabled?: boolean;
}

export function CloseCampaignModal({ campaign, type, disabled }: Props) {
const history = useHistory();
const [open, setOpen] = useState(false);
const { advertiser } = useAdvertiser();
const { fromDate } = useContext(FilterContext);

const [mutate, { loading: mutating }] = useMutation(ForceCampaignComplete, {
variables: { id: campaign?.id ?? "" },
});

const isInline = type === "inline";
const currentBalance = BigNumber(campaign?.adsManagerCurrentBalance ?? 0);
const hasNoBalance = currentBalance.lte(0);
if (!isInline && hasNoBalance) return null;
const hasInProcessOrCompleteTransfer =
campaign?.hasInProcessOrCompleteTransfer ?? false;

const doMutate = () => {
mutate({
refetchQueries: [
{
query: AdvertiserCampaignsDocument,
variables: {
id: advertiser.id,
filter: { from: fromDate?.toISOString() },
},
},
],
onCompleted: (data) => {
if (window.confirm(data.forceCampaignCompletionAndTransferFunds)) {
history.replace("/user/main/campaign");
} else {
window.location.reload();
}
},
});
};

return (
<>
<Button
variant={isInline ? "text" : "outlined"}
color="error"
size={isInline ? "small" : "medium"}
disabled={
mutating ||
hasInProcessOrCompleteTransfer ||
(isInline && hasNoBalance) ||
disabled
}
sx={{ borderRadius: "12px" }}
startIcon={isInline ? <CancelIcon /> : undefined}
onClick={() => {
setOpen(true);
}}
>
{isInline && <Trans>Close</Trans>}
{!isInline && <Trans>Close Campaign</Trans>}
</Button>
<Modal open={open} onClose={() => setOpen(false)}>
<Box
sx={{
...modalStyles,
maxWidth: 600,
}}
>
<Typography variant="h4" mb={2}>
<Trans>You are about to close this campaign.</Trans>
</Typography>

<Typography variant="subtitle1" mb={2}>
<Trans>
Closing a campaign will immediately stop it from running. Once it
has stopped running, any remaining funds will be transferred back
to your account in 24-48 hours.
</Trans>
</Typography>

<Stack direction="row" mt={2} spacing={2}>
<Button
variant="outlined"
size="medium"
onClick={() => {
setOpen(false);
}}
>
<Trans>Cancel</Trans>
</Button>
<Button
variant="contained"
size="medium"
onClick={() => {
doMutate();
}}
>
<Trans>Continue</Trans>
</Button>
</Stack>
</Box>
</Modal>
</>
);
}
2 changes: 1 addition & 1 deletion src/components/Datagrid/renderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const StandardRenderers: Record<string, CellValueRenderer> = {
};

export function renderMonetaryAmount(
value: BigNumber | number,
value: BigNumber | number | string,
currency: string,
) {
const val = BigNumber(value);
Expand Down
3 changes: 3 additions & 0 deletions src/components/Steps/ActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import RemoveIcon from "@mui/icons-material/Remove";
import { Link as RouterLink } from "react-router-dom";
import { Trans } from "@lingui/macro";
import { CloseCampaignSidebar } from "@/components/Steps/CloseCampaignSidebar";

export function ActionButtons() {
const { values } = useFormikContext<CampaignForm>();
Expand Down Expand Up @@ -37,6 +38,8 @@ export function ActionButtons() {
>
<Trans>Return to dashboard</Trans>
</Button>

<CloseCampaignSidebar />
</Stack>
);
}
35 changes: 35 additions & 0 deletions src/components/Steps/CloseCampaignSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { graphql } from "@/graphql-client/index";
import { useAdvertiser } from "@/auth/hooks/queries/useAdvertiser";
import { useQuery } from "@apollo/client";
import { useField } from "formik";
import { CloseCampaignModal } from "@/components/Campaigns/CloseCampaignModal";

const CampaignTransferStatus = graphql(`
query CampaignTransferStatus($id: String!) {
campaign(id: $id) {
id
adsManagerCurrentBalance
hasInProcessOrCompleteTransfer
}
}
`);

export function CloseCampaignSidebar() {
const [, meta] = useField<string | undefined>("id");
const { advertiser } = useAdvertiser();

const { data, loading } = useQuery(CampaignTransferStatus, {
variables: { id: meta.value ?? "" },
skip:
!meta.value ||
(!advertiser.selfServiceManageCampaign && advertiser.selfServiceSetPrice),
fetchPolicy: "cache-and-network",
});

if (!data || !data?.campaign || loading) return null;
const campaign = data.campaign;

return (
<CloseCampaignModal campaign={campaign} type="form" disabled={loading} />
);
}
19 changes: 17 additions & 2 deletions src/graphql-client/gql.ts

Large diffs are not rendered by default.

Loading

0 comments on commit 56fc83f

Please sign in to comment.