Skip to content

Commit

Permalink
refactor(campaign-operations): use codegen, typescript, react fcs (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajohn25 authored Aug 17, 2024
1 parent 55f92ad commit 9c760df
Show file tree
Hide file tree
Showing 13 changed files with 740 additions and 617 deletions.
2 changes: 1 addition & 1 deletion libs/gql-schema/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const schema = `
textRequestMaxCount: Int
textsAvailable: Boolean
pendingAssignmentRequestCount: Int!
currentAssignmentTargets: [AssignmentTarget]!
currentAssignmentTargets: [AssignmentTarget!]!
myCurrentAssignmentTarget: AssignmentTarget
myCurrentAssignmentTargets: [AssignmentTarget]!
escalatedConversationCount: Int!
Expand Down
67 changes: 67 additions & 0 deletions libs/spoke-codegen/src/graphql/campaign-operations.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
fragment CurrentAssignmentTarget on AssignmentTarget {
type
campaign {
id
title
}
teamTitle
countLeft
enabled
}

fragment CampaignInfo on Campaign {
id
title
isStarted
isApproved
isArchived
hasUnassignedContacts
hasUnsentInitialMessages
hasUnhandledMessages
description
dueBy
}

query GetAdminAssignmentTargets($organizationId: String!) {
organization(id: $organizationId) {
id
currentAssignmentTargets {
...CurrentAssignmentTarget
}
}
}

mutation archiveCampaign($campaignId: String!) {
archiveCampaign(id: $campaignId) {
...CampaignInfo
}
}

mutation unarchiveCampaign($campaignId: String!) {
unarchiveCampaign(id: $campaignId) {
...CampaignInfo
}
}

mutation releaseMessages($campaignId: String!, $target: ReleaseActionTarget!, $ageInHours: Float) {
releaseMessages(campaignId: $campaignId, target: $target, ageInHours: $ageInHours)
}

mutation deleteNeedsMessage($campaignId: String!) {
deleteNeedsMessage(campaignId: $campaignId)
}

mutation markForSecondPass($campaignId: String!, $input: SecondPassInput!) {
markForSecondPass(campaignId: $campaignId, input: $input)
}

mutation unMarkForSecondPass($campaignId: String!) {
unMarkForSecondPass(campaignId: $campaignId)
}

mutation toggleAutoAssign($campaignId: String!, $enabled: Boolean!) {
editCampaign(id: $campaignId, campaign: { isAutoassignEnabled: $enabled }) {
id
isAutoassignEnabled
}
}
47 changes: 27 additions & 20 deletions src/containers/CampaignList/components/AssignmentHUD.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Chip from "@material-ui/core/Chip";
import { useTheme } from "@material-ui/core/styles";
import type { AssignmentTarget } from "@spoke/spoke-codegen";
import type { CurrentAssignmentTargetFragment } from "@spoke/spoke-codegen";
import { css, StyleSheet } from "aphrodite";
import { Card, CardHeader, CardText } from "material-ui/Card";
import React from "react";
Expand All @@ -16,7 +16,7 @@ const styles = StyleSheet.create({
});

interface Props {
assignmentTargets: AssignmentTarget[];
assignmentTargets: CurrentAssignmentTargetFragment[];
}

const AssignmentHUD: React.FC<Props> = (props) => {
Expand All @@ -34,24 +34,31 @@ const AssignmentHUD: React.FC<Props> = (props) => {
showExpandableButton
/>
<CardText expandable>
{assignmentTargets.map((target) => (
<div key={target.teamTitle} className={css(styles.row)}>
{!target.enabled && (
<Chip
label="Disabled"
className={css(styles.disabledChip)}
style={disabledStyle}
/>
)}
<Chip className={css(styles.chip)} label={target.teamTitle} />
<div className={css(styles.prefix)}>{target.type} &#8594;</div>
<div className={css(styles.title)}>
{target.campaign.id}: {target.campaign.title}
</div>
<div className={css(styles.spacer)} />
<div className={css(styles.count)}>({target.countLeft} left)</div>
</div>
))}
{assignmentTargets.map((target) => {
const { campaign } = target;
return (
target && (
<div key={target.teamTitle} className={css(styles.row)}>
{!target.enabled && (
<Chip
label="Disabled"
className={css(styles.disabledChip)}
style={disabledStyle}
/>
)}
<Chip className={css(styles.chip)} label={target.teamTitle} />
<div className={css(styles.prefix)}>{target.type} &#8594;</div>
<div className={css(styles.title)}>
{campaign?.id}: {campaign?.title}
</div>
<div className={css(styles.spacer)} />
<div className={css(styles.count)}>
({target.countLeft} left)
</div>
</div>
)
);
})}
</CardText>
</Card>
);
Expand Down
4 changes: 2 additions & 2 deletions src/containers/CampaignList/components/CampaignList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { CampaignListEntryFragment } from "@spoke/spoke-codegen";
import React from "react";

import Empty from "../../../components/Empty";
import type { CampaignOperations } from "./CampaignListMenu";
import type { CampaignOperationsProps } from "../utils";
import CampaignListRow from "./CampaignListRow";

interface Props extends CampaignOperations {
interface Props extends CampaignOperationsProps {
organizationId: string;
campaigns: CampaignListEntryFragment[];
isAdmin: boolean;
Expand Down
6 changes: 2 additions & 4 deletions src/containers/CampaignList/components/CampaignListLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@ import React from "react";

import LoadingIndicator from "../../../components/LoadingIndicator";
import { useAuthzContext } from "../../AuthzProvider";
import type { CampaignOperationsProps } from "../utils";
import { isCampaignGroupsPermissionError } from "../utils";
import CampaignList from "./CampaignList";

interface Props {
interface Props extends CampaignOperationsProps {
organizationId: string;
pageSize: number;
campaignsFilter: CampaignsFilter;
isAdmin: boolean;
startOperation: (...args: any[]) => any;
archiveCampaign: (...args: any[]) => any;
unarchiveCampaign: (...args: any[]) => any;
}

const CampaignListLoader: React.FC<Props> = (props) => {
Expand Down
61 changes: 34 additions & 27 deletions src/containers/CampaignList/components/CampaignListMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,9 @@ import UnarchiveIcon from "@material-ui/icons/Unarchive";
import type { CampaignListEntryFragment } from "@spoke/spoke-codegen";
import React, { useCallback, useState } from "react";

type ClickHandler = () => void | Promise<void>;
import type { CampaignOperationsProps } from "../utils";

export interface CampaignOperations {
startOperation: (
action: string,
campaign: CampaignListEntryFragment,
payload?: any
) => ClickHandler;
archiveCampaign: (campaignId: string) => ClickHandler;
unarchiveCampaign: (campaignId: string) => ClickHandler;
}

interface Props extends CampaignOperations {
interface Props extends CampaignOperationsProps {
campaign: CampaignListEntryFragment;
}

Expand Down Expand Up @@ -54,56 +44,73 @@ export const CampaignListMenu: React.FC<Props> = (props) => {
onClose={handleCloseMenu}
open={menuAnchor !== null}
>
<MenuItem onClick={startOperation("releaseUnsentMessages", campaign)}>
<MenuItem
onClick={startOperation({
name: "releaseUnsentMessages",
campaign
})}
>
Release Unsent Messages
</MenuItem>
<MenuItem
onClick={startOperation("markForSecondPass", campaign, {
excludeNewer: true,
excludeRecentlyTexted: true,
days: 3,
hours: 0
onClick={startOperation({
name: "markForSecondPass",
campaign,
payload: {
excludeNewer: true,
excludeRecentlyTexted: true,
days: 3,
hours: 0
}
})}
>
Mark for a Second Pass
</MenuItem>
<MenuItem
onClick={startOperation("releaseUnrepliedMessages", campaign, {
ageInHours: 1
onClick={startOperation({
name: "releaseUnrepliedMessages",
campaign,
payload: {
ageInHours: 1
}
})}
>
Release Unreplied Conversations
</MenuItem>
{!campaign.isArchived && (
<MenuItem onClick={() => archiveCampaign(campaign.id)}>
<MenuItem onClick={archiveCampaign(campaign.id)}>
<ListItemIcon>
<ArchiveIcon />
</ListItemIcon>
Archive Campaign
</MenuItem>
)}
{campaign.isArchived && (
<MenuItem onClick={() => unarchiveCampaign(campaign.id)}>
<MenuItem onClick={unarchiveCampaign(campaign.id)}>
<ListItemIcon>
<UnarchiveIcon />
</ListItemIcon>
Unarchive Campaign
</MenuItem>
)}
<MenuItem onClick={startOperation("deleteNeedsMessage", campaign)}>
<MenuItem
onClick={startOperation({ name: "deleteNeedsMessage", campaign })}
>
Delete Unmessaged Contacts
</MenuItem>

<MenuItem onClick={startOperation("unMarkForSecondPass", campaign)}>
<MenuItem
onClick={startOperation({ name: "unMarkForSecondPass", campaign })}
>
Un-Mark for Second Pass
</MenuItem>
<MenuItem
onClick={startOperation(
campaign.isAutoassignEnabled
onClick={startOperation({
name: campaign.isAutoassignEnabled
? "turnAutoAssignOff"
: "turnAutoAssignOn",
campaign
)}
})}
>
{campaign.isAutoassignEnabled
? "Turn auto-assign OFF"
Expand Down
22 changes: 12 additions & 10 deletions src/containers/CampaignList/components/CampaignListRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import ListItem from "@material-ui/core/ListItem";
import ListItemAvatar from "@material-ui/core/ListItemAvatar";
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
import ListItemText from "@material-ui/core/ListItemText";
import { useTheme } from "@material-ui/core/styles";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import WarningIcon from "@material-ui/icons/Warning";
import type { CampaignListEntryFragment } from "@spoke/spoke-codegen";
import React from "react";
import { useHistory } from "react-router-dom";

import { dataTest } from "../../../lib/attributes";
import { DateTime } from "../../../lib/datetime";
import type { CampaignOperations } from "./CampaignListMenu";
import type { CampaignOperationsProps } from "../utils";
import CampaignListMenu from "./CampaignListMenu";

const inlineStyles = {
const useStyles = makeStyles({
chipWrapper: {
display: "flex",
flexWrap: "wrap",
Expand All @@ -29,9 +29,9 @@ const inlineStyles = {
secondaryText: {
whiteSpace: "pre-wrap"
}
};
});

interface Props extends CampaignOperations {
interface Props extends CampaignOperationsProps {
organizationId: string;
isAdmin: boolean;
campaign: CampaignListEntryFragment;
Expand All @@ -40,6 +40,8 @@ interface Props extends CampaignOperations {
export const CampaignListRow: React.FC<Props> = (props) => {
const theme = useTheme();
const history = useHistory();
const styles = useStyles();

const { organizationId, isAdmin, campaign } = props;
const {
isStarted,
Expand All @@ -56,7 +58,7 @@ export const CampaignListRow: React.FC<Props> = (props) => {
let listItemStyle = {};
let leftIcon;
if (isArchived) {
listItemStyle = inlineStyles.past;
listItemStyle = styles.past;
} else if (!isStarted || hasUnassignedContacts) {
listItemStyle = {
color: theme.palette.warning.dark
Expand Down Expand Up @@ -115,14 +117,14 @@ export const CampaignListRow: React.FC<Props> = (props) => {
}

const primaryText = (
<div style={inlineStyles.chipWrapper}>
<div className={styles.chipWrapper}>
{campaign.title}
{tags.map((tag) => (
<Chip
key={tag.title}
label={tag.title}
className={styles.chip}
style={{
...inlineStyles.chip,
color: tag.color,
backgroundColor: tag.backgroundColor
}}
Expand All @@ -131,7 +133,7 @@ export const CampaignListRow: React.FC<Props> = (props) => {
</div>
);
const secondaryText = (
<span style={inlineStyles.secondaryText}>
<span className={styles.secondaryText}>
<span>
Campaign ID: {campaign.id}
<br />
Expand All @@ -148,7 +150,7 @@ export const CampaignListRow: React.FC<Props> = (props) => {
}`;
return (
<ListItem
{...dataTest("campaignRow")}
{...dataTest("campaignRow", false)}
style={listItemStyle}
onClick={() => history.push(campaignUrl)}
>
Expand Down
Loading

0 comments on commit 9c760df

Please sign in to comment.