Skip to content

Commit

Permalink
530 look into amazon simple email service (#547)
Browse files Browse the repository at this point in the history
* add basic amazon ses configuration

* cherry-pick some changes from permissions branch and slightly adjust them to work with current permissions system

* adjust contact page design by using mui components

* add email field to archives

* use archive email attribute instead of address config; change email text

* add email field to archive form and standardize colons

* fix styling

* move aws ses region into env file

* improve typings and error handling a bit

* update context type and use it for growthbook middleware

* implement review feedback
  • Loading branch information
olschulz committed Jun 15, 2023
1 parent b73b968 commit 975b2e7
Show file tree
Hide file tree
Showing 26 changed files with 803 additions and 145 deletions.
9 changes: 7 additions & 2 deletions projects/bp-gallery/src/components/common/PrimaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ const PrimaryButton = ({
children,
onClickFn,
isShowMore,
type,
className,
}: PropsWithChildren<{
onClickFn: MouseEventHandler;
onClickFn?: MouseEventHandler;
isShowMore?: boolean;
type?: 'button' | 'submit' | 'reset';
className?: string;
}>) => {
return (
<Button
variant='contained'
onClick={onClickFn}
className='primary-button'
className={`primary-button ${className ?? ''}`}
endIcon={isShowMore && <ArrowForwardIos />}
type={type}
>
{children}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,14 @@
display: flex;
flex-direction: row;
margin: 2rem 0;
flex-wrap: wrap;

@media (max-width: 1250px) {
flex-direction: column;
margin: 1rem 0;
}

.archive-form-label {
margin-top: 1rem;
flex-shrink: 0;
width: 20%;
display: flex;
align-items: flex-start;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Check, Close, Save } from '@mui/icons-material';
import { Button } from '@mui/material';
import { Jodit } from 'jodit-react';
import { pick } from 'lodash';
import { useCallback, useEffect, useState } from 'react';
import { FormEvent, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetArchiveQuery, useUpdateArchiveMutation } from '../../../graphql/APIConnector';
import { useSimplifiedQueryResponseData } from '../../../graphql/queryUtils';
Expand All @@ -26,6 +26,7 @@ interface ArchiveForm {
name: string;
shortDescription: string;
longDescription: string;
email: string | null;
paypalClient: string;
paypalDonationText: string;
paypalPurpose: string;
Expand Down Expand Up @@ -58,7 +59,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
const archive: FlatArchiveTag | undefined = useSimplifiedQueryResponseData(data)?.archiveTag;

const [updateArchive, updateMutationResponse] = useUpdateArchiveMutation({
refetchQueries: ['getArchive'],
refetchQueries: ['getArchive', 'getArchiveNames'],
awaitRefetchQueries: true,
});
const { createLink, updateLink, deleteLink } = useLinks(archiveId);
Expand All @@ -67,6 +68,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
name: '',
shortDescription: '',
longDescription: '',
email: null,
paypalClient: '',
paypalDonationText: '',
paypalPurpose: '',
Expand All @@ -80,6 +82,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
name: archive?.name ?? '',
shortDescription: archive?.shortDescription ?? '',
longDescription: archive?.longDescription ?? '',
email: archive?.email ?? null,
paypalClient: archive?.paypalClient ?? '',
paypalDonationText: archive?.paypalDonationText ?? '',
paypalPurpose: archive?.paypalPurpose ?? '',
Expand Down Expand Up @@ -139,7 +142,8 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
});
};

const handleSubmit = () => {
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (form.invalid) return;
handleLinks();
if (form.logo) {
Expand All @@ -152,6 +156,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
'name',
'shortDescription',
'longDescription',
'email',
'paypalClient',
'paypalDonationText',
'paypalPurpose',
Expand All @@ -170,6 +175,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
'name',
'shortDescription',
'longDescription',
'email',
'paypalClient',
'paypalDonationText',
'paypalPurpose',
Expand Down Expand Up @@ -204,8 +210,9 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
<Button
className='button-filled button-save'
startIcon={form.dirty ? <Save /> : <Check />}
onClick={handleSubmit}
disabled={!form.dirty || updateMutationResponse.loading || form.invalid}
type='submit'
form='archive-form'
>
{updateMutationResponse.loading
? t('archives.edit.saving')
Expand All @@ -217,7 +224,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {

<h1>{archive.name}</h1>

<form className='archive-form'>
<form id='archive-form' className='archive-form' onSubmit={handleSubmit}>
<ArchiveInputField
label={t('archives.edit.nameLabel')}
id='name'
Expand All @@ -233,7 +240,7 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
/>
<div className='archive-form-div'>
<label className='archive-form-label' htmlFor='archive-form-long-description'>
{t('archives.edit.longDescriptionLabel')}
{t('archives.edit.longDescriptionLabel')}:
</label>
<TextEditor
value={archive.longDescription ?? ''}
Expand All @@ -246,7 +253,17 @@ const ArchiveEditView = ({ archiveId }: ArchiveEditViewProps) => {
defaultUrl={asUploadPath(archive.logo, { highQuality: false })}
onChange={file => updateForm({ logo: file, dirty: true })}
/>
<ArchiveInputField
defaultValue={archive.email ?? ''}
label={t('archives.edit.email.label')}
id='email'
type='email'
onBlur={value => updateForm({ email: value || null, dirty: true })}
helperText={t('archives.edit.email.helperText')}
/>

<ArchiveLinkForm links={archive.links} onChange={handleLinkChange} />

<ArchiveInputField
label={t('archives.edit.paypal.client-label')}
defaultValue={archive.paypalClient ?? ''}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const ArchiveInputField = ({
<div className='archive-form-div'>
{label && (
<label className='archive-form-label' htmlFor={`archive-form-${id}`}>
{label}
{label}:
</label>
)}
<ArchiveInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ArchiveLinkForm = ({ links: defaultLinks, onChange }: LinkFormProps) => {

return (
<div className='archive-form-div'>
<label className='archive-form-label'>{t('archives.edit.links.label')}</label>
<label className='archive-form-label'>{t('archives.edit.links.label')}:</label>
<div className='archive-form-input archive-form-link-input'>
{links.map(
link =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ArchiveLogoInput = ({ defaultUrl, onChange }: ArchiveLogoInputProps) => {
return (
<div className='archive-form-div'>
<label className='archive-form-label' htmlFor='archive-form-logo'>
{t('archives.edit.logo.label')}
{t('archives.edit.logo.label')}:
</label>
<div>
<OutlinedInput
Expand Down
165 changes: 115 additions & 50 deletions projects/bp-gallery/src/components/views/contact/ContactFormView.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,126 @@
import { Button } from '@mui/material';
import { MenuItem, Select, TextField } from '@mui/material';
import { FormEvent, useCallback, useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetArchiveNamesQuery } from '../../../graphql/APIConnector';
import { useSimplifiedQueryResponseData } from '../../../graphql/queryUtils';
import { asApiPath } from '../../../helpers/app-helpers';
import { useOnChangeSetter } from '../../../hooks/onchange-setter.hook';
import { FlatArchiveTag } from '../../../types/additionalFlatTypes';
import PrimaryButton from '../../common/PrimaryButton';
import { AlertContext, AlertType } from '../../provider/AlertProvider';

const ContactFormView = () => {
const { t } = useTranslation();
const apiPath: string = asApiPath('/api/contact');

const [recipient, setRecipient] = useState('');
const [senderName, setSenderName] = useState('');
const [replyEmail, setReplyEmail] = useState('');
const [subject, setSubject] = useState('');
const [message, setMessage] = useState('');

const openAlert = useContext(AlertContext);
const { data } = useGetArchiveNamesQuery({
variables: { filters: { email: { notNull: true } } },
});

const archiveNames: FlatArchiveTag[] | undefined =
useSimplifiedQueryResponseData(data)?.archiveTags;

useEffect(() => {
setRecipient(archiveNames?.[0]?.id ?? '');
}, [archiveNames]);

//replace this with the onSubmit function of the new permission system when it's done, the rest of the component should (hopefully) stay the same
const onSubmit = useCallback(
async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData();
formData.append('recipient', recipient);
formData.append('sender_name', senderName);
formData.append('reply_email', replyEmail);
formData.append('subject', subject);
formData.append('message', message);

try {
const res = await fetch(asApiPath('/api/contact'), {
method: 'post',
body: formData,
});
if (!res.ok) {
const json = await res.json();
openAlert({
alertType: AlertType.ERROR,
message: `Error: ${json.error.message as string}`,
});
return;
}
openAlert({
alertType: AlertType.SUCCESS,
message: t('contact-form.success'),
});
setSubject('');
setMessage('');
} catch {
openAlert({
alertType: AlertType.ERROR,
message: t('contact-form.error'),
});
}
},
[recipient, senderName, replyEmail, subject, message, openAlert, t]
);

return (
<div className='contact-form-container flex flex-col flex-nowrap items-center m-auto p-4'>
<h1 className='pt-8'>{t('contact-form.title')}</h1>
<form
action={apiPath}
method='post'
encType='multipart/form-data'
target=''
className='contact-form w-fit h-fit'
>
<div className='form-contents'>
<p>
<label className='flex flex-col flex-nowrap text-xl p-0'>
{t('contact-form.choose-archive-label')}
<select className='max-w h-6' name='recipient'>
{/* <option value='Test'>Test</option> */}
<option value='Herbert-Ahrens-Archiv'>Herbert-Ahrens-Archiv</option>
</select>
</label>
</p>
<p>
<label className='flex flex-col flex-nowrap text-xl p-0'>
{t('contact-form.name-label')}
<input name='sender_name' className='form-input h-5 w-80 name-input' type='text' />
</label>
</p>
<p>
<label className='flex flex-col flex-nowrap text-xl p-0'>
{t('contact-form.email-label')}
<input name='email' className='form-input h-5 w-80 email-input' type='email' />
</label>
</p>
<p>
<label className='flex flex-col flex-nowrap text-xl p-0'>
{t('contact-form.subject-label')}
<input name='subject' className='form-input h-5 w-80 subject-input' />
</label>
</p>
<p>
<label className='flex flex-col flex-nowrap text-xl p-0'>
{t('contact-form.message-label')}
<textarea name='message' className='form-input max-w message-input h-20' />
</label>
</p>
<div className='submit-button-container '></div>
<Button className='submit-input' variant='contained' color='primary' type='submit'>
<div className='max-w-[1200px] h-full flex flex-col flex-nowrap items-center m-auto bg-white shadow-lg'>
<div className='mx-auto mt-20 p-4'>
<h1 className='pt-8'>{t('contact-form.title')}</h1>
<form onSubmit={onSubmit} className='flex flex-col w-96'>
<label className='flex flex-col flex-nowrap text-xl p-0'>
<p className='mb-1'>{t('contact-form.choose-archive-label')}</p>
<Select
onChange={useOnChangeSetter(setRecipient)}
value={recipient}
className='max-w'
required
>
{archiveNames?.map(archiveName => (
<MenuItem key={archiveName.id} value={archiveName.id}>
{archiveName.name}
</MenuItem>
))}
</Select>
</label>
<label className='flex flex-col flex-nowrap text-xl p-0'>
<p className='mb-1'>{t('contact-form.name-label')}</p>
<TextField onChange={useOnChangeSetter(setSenderName)} value={senderName} type='text' />
</label>
<label className='flex flex-col flex-nowrap text-xl p-0'>
<p className='mb-1'>{t('contact-form.email-label')}</p>
<TextField
onChange={useOnChangeSetter(setReplyEmail)}
value={replyEmail}
type='email'
/>
</label>
<label className='flex flex-col flex-nowrap text-xl p-0 h-full'>
<p className='mb-1'>{t('contact-form.subject-label')}</p>
<TextField onChange={useOnChangeSetter(setSubject)} value={subject} required />
</label>
<label className='flex flex-col flex-nowrap text-xl p-0 mb-5'>
<p className='mb-1'>{t('contact-form.message-label')}</p>
<TextField
multiline
onChange={useOnChangeSetter(setMessage)}
minRows={3}
value={message}
required
/>
</label>
<PrimaryButton type='submit' className='!w-full'>
{t('contact-form.submit-button-label').toString()}
</Button>
</div>
</form>
</PrimaryButton>
</form>
</div>
</div>
);
};
Expand Down
Loading

0 comments on commit 975b2e7

Please sign in to comment.