Skip to content

Commit

Permalink
feat: indicate new campaign id(s) from template (#1629)
Browse files Browse the repository at this point in the history
* fix: reset state when create template dialog is closed
  • Loading branch information
mkoontz-rewired authored Sep 11, 2023
1 parent 0d035af commit 0af2c75
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 29 deletions.
60 changes: 50 additions & 10 deletions src/components/CreateCampaignFromTemplateDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { MutationResult } from "@apollo/client/react/types/types";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
Expand All @@ -6,30 +7,44 @@ import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";
import type { TemplateCampaignFragment } from "@spoke/spoke-codegen";
import type {
CreateCampaignFromTemplateMutation,
TemplateCampaignFragment
} from "@spoke/spoke-codegen";
import {
GetAdminCampaignsDocument,
useCreateCampaignFromTemplateMutation,
useGetTemplateCampaignsQuery
} from "@spoke/spoke-codegen";
import React, { useCallback, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";

export interface CreateCampaignFromTemplateDialogProps {
organizationId: string;
open: boolean;
onCreateTemplateCompleted: (
copiedCampaigns: NonNullable<
MutationResult<CreateCampaignFromTemplateMutation>["data"]
>["copyCampaigns"],
selectedTemplateTitle: string
) => void;
onClose?: () => Promise<void> | void;
defaultTemplate?: TemplateCampaignFragment;
}

export const CreateCampaignFromTemplateDialog: React.FC<CreateCampaignFromTemplateDialogProps> = (
props
) => {
const defaultTemplate = props.defaultTemplate ?? null;
const defaultCopyCount = 1;
const [
selectedTemplate,
setSelectedTemplate
] = useState<TemplateCampaignFragment | null>(props.defaultTemplate ?? null);
const [quantity, setQuantity] = useState<number | null>(1);
const { data, error } = useGetTemplateCampaignsQuery({
] = useState<TemplateCampaignFragment | null>(defaultTemplate);
const [quantity, setQuantity] = useState<number | null>(defaultCopyCount);
const {
data: templateCampaignsData,
error: templateCampaignsError
} = useGetTemplateCampaignsQuery({
variables: { organizationId: props.organizationId }
});
const [
Expand All @@ -39,8 +54,18 @@ export const CreateCampaignFromTemplateDialog: React.FC<CreateCampaignFromTempla
refetchQueries: [GetAdminCampaignsDocument]
});

// Reset state when dialog is closed
useEffect(() => {
if (!props.open) {
setSelectedTemplate(defaultTemplate);
setQuantity(defaultCopyCount);
}
}, [props.open]);

const templates =
data?.organization?.templateCampaigns?.edges?.map(({ node }) => node) ?? [];
templateCampaignsData?.organization?.templateCampaigns?.edges?.map(
({ node }) => node
) ?? [];

const handleChangeTemplate = useCallback(
(
Expand Down Expand Up @@ -77,11 +102,26 @@ export const CreateCampaignFromTemplateDialog: React.FC<CreateCampaignFromTempla
const handleClickCreate = useCallback(async () => {
if (!(quantity !== null && selectedTemplate !== null && !working)) return;

await createFromTemplate({
const result = await createFromTemplate({
variables: { templateId: selectedTemplate.id, quantity }
});
const { data, errors } = result;
const copiedCampaigns = data?.copyCampaigns ?? [];
const hasCopiedCampaigns = copiedCampaigns.length > 0;
const noErrors = !errors || errors.length === 0;
if (hasCopiedCampaigns && noErrors) {
const selectedTemplateTitle = selectedTemplate?.title ?? "";
props.onCreateTemplateCompleted(copiedCampaigns, selectedTemplateTitle);
}

props.onClose?.();
}, [quantity, selectedTemplate, createFromTemplate, props.onClose]);
}, [
quantity,
selectedTemplate,
createFromTemplate,
props.onClose,
props.onCreateTemplateCompleted
]);

return (
<Dialog
Expand All @@ -96,9 +136,9 @@ export const CreateCampaignFromTemplateDialog: React.FC<CreateCampaignFromTempla
<DialogContentText>
Select a campaign template to create from
</DialogContentText>
{error && (
{templateCampaignsError && (
<DialogContentText>
Error fetching templates: {error.message}
Error fetching templates: {templateCampaignsError.message}
</DialogContentText>
)}
<Autocomplete
Expand Down
115 changes: 96 additions & 19 deletions src/containers/AdminCampaignList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import Snackbar from "@material-ui/core/Snackbar";
import CreateIcon from "@material-ui/icons/Create";
import FileCopyIcon from "@material-ui/icons/FileCopyOutlined";
import MuiAlert from "@material-ui/lab/Alert";
import AlertTitle from "@material-ui/lab/AlertTitle";
import SpeedDial from "@material-ui/lab/SpeedDial";
import SpeedDialAction from "@material-ui/lab/SpeedDialAction";
import SpeedDialIcon from "@material-ui/lab/SpeedDialIcon";
Expand Down Expand Up @@ -40,6 +43,11 @@ class AdminCampaignList extends React.Component {
state = {
speedDialOpen: false,
createFromTemplateOpen: false,
// created from template state
showCreatedFromTemplateSnackbar: false,
createdFromTemplateIds: [],
createdFromTemplateTitle: "",
// end created from template state
isCreating: false,
campaignsFilter: {
isArchived: false
Expand Down Expand Up @@ -82,8 +90,21 @@ class AdminCampaignList extends React.Component {
});
};

startReleasingAllReplies = () => {
this.setState({ releasingAllReplies: true });
handleClickSpeedDial = () => {
const { speedDialOpen } = this.state;
this.setState({ speedDialOpen: !speedDialOpen });
};

handleCreatedFromTemplateSnackbarClose = (_event, reason) => {
if (reason === "clickaway") {
return;
}

this.setState({
showCreatedFromTemplateSnackbar: false,
createdFromTemplateIds: [],
createdFromTemplateTitle: null
});
};

closeReleasingAllReplies = () => {
Expand All @@ -95,9 +116,23 @@ class AdminCampaignList extends React.Component {
});
};

handleClickSpeedDial = () => {
const { speedDialOpen } = this.state;
this.setState({ speedDialOpen: !speedDialOpen });
handleCreateTemplateCompleted = (copyCampaigns, selectedTemplateTitle) => {
if (copyCampaigns.length === 0) {
return;
}
this.setState({
showCreatedFromTemplateSnackbar: true,
createdFromTemplateIds: copyCampaigns.map((campaign) => campaign.id),
createdFromTemplateTitle: selectedTemplateTitle
});
};

handleCreateTemplateDialogClose = () => {
this.setState({ createFromTemplateOpen: false });
};

startReleasingAllReplies = () => {
this.setState({ releasingAllReplies: true });
};

releaseAllReplies = () => {
Expand Down Expand Up @@ -149,11 +184,19 @@ class AdminCampaignList extends React.Component {
const {
campaignsFilter,
releasingAllReplies,
releasingInProgress
releasingInProgress,
releaseAllRepliesResult,
releaseAllRepliesError,
createFromTemplateOpen,
createdFromTemplateIds,
createdFromTemplateTitle,
showCreatedFromTemplateSnackbar,
speedDialOpen,
isCreating
} = this.state;

const doneReleasingReplies =
this.state.releaseAllRepliesResult || this.state.releaseAllRepliesError;
releaseAllRepliesResult || releaseAllRepliesError;

const { organizationId } = this.props.match.params;
const { isAdmin } = this.props;
Expand All @@ -175,15 +218,12 @@ class AdminCampaignList extends React.Component {
<DialogContent>
{releasingInProgress ? (
<LoadingIndicator />
) : this.state.releaseAllRepliesError ? (
<span>
Error: {JSON.stringify(this.state.releaseAllRepliesError)}
</span>
) : this.state.releaseAllRepliesResult ? (
) : releaseAllRepliesError ? (
<span>Error: {JSON.stringify(releaseAllRepliesError)}</span>
) : releaseAllRepliesResult ? (
<span>
Released {this.state.releaseAllRepliesResult.contactCount}{" "}
replies on {this.state.releaseAllRepliesResult.campaignCount}{" "}
campaigns
Released {releaseAllRepliesResult.contactCount} replies on{" "}
{releaseAllRepliesResult.campaignCount} campaigns
</span>
) : !doneReleasingReplies ? (
<div>
Expand Down Expand Up @@ -258,7 +298,7 @@ class AdminCampaignList extends React.Component {
</DialogActions>
</Dialog>
)}
{this.state.isCreating ? (
{isCreating ? (
<LoadingIndicator />
) : (
<CampaignList
Expand All @@ -276,7 +316,7 @@ class AdminCampaignList extends React.Component {
icon={<SpeedDialIcon />}
onClick={this.handleClickSpeedDial}
onOpen={() => this.setState({ speedDialOpen: true })}
open={this.state.speedDialOpen}
open={speedDialOpen}
direction="up"
>
<SpeedDialAction
Expand All @@ -293,9 +333,46 @@ class AdminCampaignList extends React.Component {
) : null}
<CreateCampaignFromTemplateDialog
organizationId={organizationId}
open={this.state.createFromTemplateOpen}
onClose={() => this.setState({ createFromTemplateOpen: false })}
open={createFromTemplateOpen}
onClose={this.handleCreateTemplateDialogClose}
onCreateTemplateCompleted={this.handleCreateTemplateCompleted}
/>
<Snackbar
open={showCreatedFromTemplateSnackbar}
onClose={this.handleCreatedFromTemplateSnackbarClose}
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
>
<MuiAlert
elevation={6}
variant="filled"
severity="success"
onClose={this.handleCreatedFromTemplateSnackbarClose}
>
<AlertTitle>
Campaign{createdFromTemplateIds.length > 1 ? "s" : ""}{" "}
successfully created from template "{createdFromTemplateTitle}"
</AlertTitle>
<p>
Created campaign
{createdFromTemplateIds.length > 1 ? "s" : ""}:{" "}
{createdFromTemplateIds.map((id, i) => {
return (
<span key={id}>
{i > 0 && ", "}
<a
key={id}
target="_blank"
href={`${window.BASE_URL}/admin/${organizationId}/campaigns/${id}`}
rel="noreferrer"
>
Campaign {id}
</a>
</span>
);
})}
</p>
</MuiAlert>
</Snackbar>
</div>
);
}
Expand Down

0 comments on commit 0af2c75

Please sign in to comment.