Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emailer: Load HTML templates from S3 #334

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c2b5354
templatessss
CiciLing Feb 11, 2024
996adcf
prelim add es translations
huang0h Jan 28, 2024
6e07a0c
switch back to default en
huang0h Jan 28, 2024
4fd5716
open planting site spacing
huang0h Jan 28, 2024
9e89c5f
Bump @testing-library/react from 11.2.7 to 12.1.5
dependabot[bot] Feb 1, 2024
15ee0ea
Bump follow-redirects from 1.15.2 to 1.15.4
dependabot[bot] Jan 10, 2024
a23e48c
Bump @types/react-csv from 1.1.3 to 1.1.10
dependabot[bot] Feb 1, 2024
05c7ee8
dropdown inital implementation
CerberusLatrans Feb 11, 2024
e2c5186
add catch clause, debugging
CerberusLatrans Feb 15, 2024
32b2403
load template contents boilerplate
CerberusLatrans Feb 15, 2024
c595718
templatessss
CiciLing Feb 11, 2024
4e8ed0f
dropdown inital implementation
CerberusLatrans Feb 11, 2024
aada15a
add catch clause, debugging
CerberusLatrans Feb 15, 2024
8c5abfa
load template contents boilerplate
CerberusLatrans Feb 15, 2024
7a36e05
fixed compilation error and templateNamesResponse &LoadTemplateResponse
CiciLing Feb 16, 2024
cf7a7db
fixed compilation error and templateNamesResponse
CiciLing Feb 16, 2024
aa777e8
tried implementing SendEmailForm
CiciLing Feb 24, 2024
7f88839
template content change reacts but still blank
CerberusLatrans Mar 10, 2024
42c7186
someone said they are jealous of your high iq and hopes you get into …
CerberusLatrans Mar 17, 2024
708fc5c
Merge branch 'master' into CLOT.loadTemplate
CerberusLatrans Mar 17, 2024
8213776
remove pre merge old code, loading error messages
CerberusLatrans Mar 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/api/protectedApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
import {
FilterSitesParams,
FilterSitesResponse,
TemplateNamesResponse,
LoadTemplateResponse,
} from '../containers/email/types';

export interface ProtectedApiExtraArgs {
Expand Down Expand Up @@ -131,6 +133,10 @@ export interface ProtectedApiClient {
readonly filterSites: (
params: FilterSitesParams,
) => Promise<FilterSitesResponse>;
readonly getEmailTemplateNames: () => Promise<TemplateNamesResponse>;
readonly loadEmailTemplateContent: (
templateName: string,
) => Promise<LoadTemplateResponse>;
}

export enum ProtectedApiClientRoutes {
Expand Down Expand Up @@ -161,6 +167,7 @@ export enum AdminApiClientRoutes {
GET_STEWARDSHIP_REPORT_CSV = '/api/v1/protected/report/csv/adoption',
ADD_SITES = '/api/v1/protected/sites/add_sites',
SEND_EMAIL = '/api/v1/protected/neighborhoods/send_email',
GET_TEMPLATE_NAMES = 'api/v1/protected/emailer/template_names',
}

const baseTeamRoute = '/api/v1/protected/teams/';
Expand Down Expand Up @@ -228,6 +235,8 @@ export const ParameterizedAdminApiRoutes = {
? `&activityCountMax=${params.activityCountMax}`
: ''
}`,
LOAD_TEMPLATE: (templateName: string): string =>
`api/v1/protected/emailer/load_template/${templateName}`,
};

const makeReservation = (blockId: number, teamId?: number): Promise<void> => {
Expand Down Expand Up @@ -573,6 +582,20 @@ const filterSites = (
).then((res) => res.data);
};

const getEmailTemplateNames = (): Promise<TemplateNamesResponse> => {
return AppAxiosInstance.get(AdminApiClientRoutes.GET_TEMPLATE_NAMES).then(
(res) => res.data,
);
};

const loadEmailTemplateContent = (
templateName: string,
): Promise<LoadTemplateResponse> => {
return AppAxiosInstance.get(
ParameterizedAdminApiRoutes.LOAD_TEMPLATE(templateName),
).then((res) => res.data);
};

const Client: ProtectedApiClient = Object.freeze({
makeReservation,
completeReservation,
Expand Down Expand Up @@ -622,6 +645,8 @@ const Client: ProtectedApiClient = Object.freeze({
sendEmail,
deleteImage,
filterSites,
getEmailTemplateNames,
loadEmailTemplateContent,
});

export default Client;
15 changes: 12 additions & 3 deletions src/components/forms/sendEmailForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SendEmailRequest,
} from '../../../components/forms/ducks/types';
import { requiredRule } from '../../../utils/formRules';
import { FormInstance } from 'antd/es/form';
import { site } from '../../../constants';
import { useTranslation } from 'react-i18next';
import { n } from '../../../utils/stringFormat';
Expand All @@ -33,9 +34,14 @@ const EmailPreview = styled.div`

interface SendEmailFormProps {
readonly emails: string[];
readonly sendEmailForm: FormInstance<SendEmailRequest>;
}

const SendEmailForm: React.FC<SendEmailFormProps> = ({ emails }) => {
const SendEmailForm: React.FC<SendEmailFormProps> = ({
emails,
sendEmailForm,
}) => {
const SendEmailForm: React.FC<SendEmailFormProps> = ({ emails }) => {
Copy link
Contributor

@huang0h huang0h Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Looks like you have some errors (from merging) - make sure to get rid of this extra declaration of the SendEmailForm component!

Copy link
Contributor

@huang0h huang0h Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also make sure to remove line 50:
const [sendEmailForm] = Form.useForm();
since we're passing the form instance as a prop now (I can't comment on it since it's an unchanged line 😞)

const { t } = useTranslation(n(site, ['forms']), {
keyPrefix: 'volunteer_emailer',
nsMode: 'fallback',
Expand All @@ -62,7 +68,6 @@ const SendEmailForm: React.FC<SendEmailFormProps> = ({ emails }) => {
...values,
emails,
};

ProtectedApiClient.sendEmail(sendEmailRequest)
.then(() => {
message.success(t('success'));
Expand All @@ -71,7 +76,6 @@ const SendEmailForm: React.FC<SendEmailFormProps> = ({ emails }) => {
message.error(t('response_error', { error: err.response.data })),
);
};

return (
<Form
name="sendEmail"
Expand All @@ -98,6 +102,11 @@ const SendEmailForm: React.FC<SendEmailFormProps> = ({ emails }) => {
rules={requiredRule(t('body_required'))}
hidden={showPreview}
>
<Input.TextArea
name="emailContent"
rows={6}
placeholder={'Email Body'}
/>
<Input.TextArea rows={8} placeholder={t('body_placeholder')} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Can we also remove this extra TextArea? There's two text areas here:

image

</Form.Item>
{showPreview && (
Expand Down
51 changes: 42 additions & 9 deletions src/containers/email/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Alert,
Divider,
SelectProps,
Form,
} from 'antd';
import { ArrowLeftOutlined } from '@ant-design/icons';

Expand All @@ -19,10 +20,10 @@ import PageLayout from '../../components/pageLayout';
import { ReturnButton } from '../../components/themedComponents';
import PageHeader from '../../components/pageHeader';
import {
EmailType,
EmailerFilters,
FilteredSite,
FilterSitesParams,
LoadTemplateResponse,
} from './types';
import EmailerFilterControls from '../../components/emailerFilterControls';
import AdoptedSitesTable from '../../components/adoptedSitesTable';
Expand Down Expand Up @@ -63,6 +64,8 @@ const defaultFilters: EmailerFilters = {
lastActivityEnd: null,
};

const defaultTemplate = 'Pick a Template';

enum LoadingState {
SUCCESS = 'success',
LOADING = 'loading',
Expand All @@ -76,13 +79,37 @@ function neighborhoodToId(neighborhood: Neighborhoods): number {
}

const Email: React.FC = () => {
const [emailType, setEmailType] = useState<EmailType>(EmailType.INACTIVE);
const [emailType, setEmailType] = useState<string>(defaultTemplate);
const [templateNames, setTemplateNames] = useState<string[]>([]);
const [filters, setFilters] = useState<EmailerFilters>(defaultFilters);
const [fetchData, setFetchData] = useState<FilteredSite[]>([]);
const [fetchSitesState, setFetchSitesState] = useState<LoadingState>(
LoadingState.SUCCESS,
);
const [selectedEmails, setSelectedEmails] = useState<string[]>([]);
const [sendEmailForm] = Form.useForm();

function fetchTemplateNames() {
protectedApiClient
.getEmailTemplateNames()
.then((res) => {
setTemplateNames(res.templates);
})
.catch((err) => {
setTemplateNames([defaultTemplate]);
Copy link
Contributor

@huang0h huang0h Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Can we add an error message here in the catch (same for fetchTemplateData)? Something like the message.error() call in this function would do - if something fails on our end, it's better to explicitly tell the user that an error happened than to fail silently and have them guess at why something's not working

});
}

function fetchTemplateData(templateName: string) {
protectedApiClient
.loadEmailTemplateContent(templateName)
.then((res) => {
sendEmailForm.setFieldValue('emailBody', res.template);
})
.catch((err) => {
sendEmailForm.setFieldValue('emailBody', '');
});
}

function onClickSearch() {
setFetchSitesState(LoadingState.LOADING);
Expand Down Expand Up @@ -188,16 +215,22 @@ const Email: React.FC = () => {
</Typography.Title>
<EmailTypeSelect
value={emailType}
defaultValue={EmailType.INACTIVE}
options={Object.entries(EmailType).map(([key, value]) => ({
value: key,
label: value,
defaultValue={defaultTemplate}
onClick={fetchTemplateNames}
options={templateNames.map((e) => ({
value: e,
label: e,
}))}
onChange={(value: EmailType) => setEmailType(value)}
disabled // TODO uncomment when ready
onChange={(value: string) => {
setEmailType(value);
fetchTemplateData(value);
}}
/>
<Typography.Title level={3}>Email</Typography.Title>
<SendEmailForm emails={selectedEmails} />
<SendEmailForm
emails={selectedEmails}
sendEmailForm={sendEmailForm}
/>
</EmailPageContainer>
</PageLayout>
</>
Expand Down
11 changes: 8 additions & 3 deletions src/containers/email/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { Neighborhoods } from '../../assets/content';

export enum EmailType {
INACTIVE = 'Inactive',
NEIGHBORHOOD = 'Neighborhood',
export interface TemplateNamesResponse {
templates: string[];
}

export interface LoadTemplateResponse {
name : string,
template : string,
author : number;
}

export interface EmailerFilters {
Expand Down
Loading