Skip to content

Commit dfbd510

Browse files
authored
Fe add dkim selectors to domain creation (canada-ca#2487)
* extrapolate domain update modal from AdminDomains.js * createDomain and updateDomain use same form inside AdminDomainModal.js * update tests * wrap Add Domain and search in form, give Add Domain modal fitting header
1 parent 2e9a577 commit dfbd510

5 files changed

Lines changed: 536 additions & 366 deletions

File tree

frontend/src/AdminDomainModal.js

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
import React, { useRef } from 'react'
2+
import { Trans, t } from '@lingui/macro'
3+
import {
4+
Stack,
5+
Text,
6+
Icon,
7+
Input,
8+
SlideIn,
9+
Modal,
10+
ModalOverlay,
11+
ModalContent,
12+
ModalHeader,
13+
ModalCloseButton,
14+
ModalBody,
15+
ModalFooter,
16+
FormLabel,
17+
FormControl,
18+
Box,
19+
Grid,
20+
useToast,
21+
} from '@chakra-ui/core'
22+
import { string, array, bool, object, func } from 'prop-types'
23+
import { TrackerButton } from './TrackerButton'
24+
import { Field, Formik, FieldArray } from 'formik'
25+
import FormErrorMessage from '@chakra-ui/core/dist/FormErrorMessage'
26+
import { UPDATE_DOMAIN, CREATE_DOMAIN } from './graphql/mutations'
27+
import { useMutation } from '@apollo/client'
28+
import { useUserState } from './UserState'
29+
30+
export function AdminDomainModal({
31+
isOpen,
32+
onClose,
33+
validationSchema,
34+
orgId,
35+
editingDomainId,
36+
editingDomainUrl,
37+
selectorInputList,
38+
orgSlug,
39+
mutation,
40+
}) {
41+
const { currentUser } = useUserState()
42+
const toast = useToast()
43+
const initialFocusRef = useRef()
44+
45+
const [createDomain] = useMutation(CREATE_DOMAIN, {
46+
refetchQueries: ['PaginatedOrgDomains'],
47+
context: {
48+
headers: {
49+
authorization: currentUser.jwt,
50+
},
51+
},
52+
onError(error) {
53+
toast({
54+
title: t`An error occurred.`,
55+
description: error.message,
56+
status: 'error',
57+
duration: 9000,
58+
isClosable: true,
59+
position: 'top-left',
60+
})
61+
},
62+
onCompleted({ createDomain }) {
63+
if (createDomain.result.__typename === 'Domain') {
64+
toast({
65+
title: t`Domain added`,
66+
description: t`${createDomain.result.domain} was added to ${orgSlug}`,
67+
status: 'success',
68+
duration: 9000,
69+
isClosable: true,
70+
position: 'top-left',
71+
})
72+
onClose()
73+
} else if (createDomain.result.__typename === 'DomainError') {
74+
toast({
75+
title: t`Unable to create new domain.`,
76+
description: createDomain.result.description,
77+
status: 'error',
78+
duration: 9000,
79+
isClosable: true,
80+
position: 'top-left',
81+
})
82+
} else {
83+
toast({
84+
title: t`Incorrect send method received.`,
85+
description: t`Incorrect createDomain.result typename.`,
86+
status: 'error',
87+
duration: 9000,
88+
isClosable: true,
89+
position: 'top-left',
90+
})
91+
console.log('Incorrect createDomain.result typename.')
92+
}
93+
},
94+
})
95+
96+
const [updateDomain] = useMutation(UPDATE_DOMAIN, {
97+
refetchQueries: ['PaginatedOrgDomains'],
98+
context: {
99+
headers: {
100+
authorization: currentUser.jwt,
101+
},
102+
},
103+
onError(error) {
104+
toast({
105+
title: t`An error occurred.`,
106+
description: error.message,
107+
status: 'error',
108+
duration: 9000,
109+
isClosable: true,
110+
position: 'top-left',
111+
})
112+
},
113+
onCompleted({ updateDomain }) {
114+
if (updateDomain.result.__typename === 'Domain') {
115+
toast({
116+
title: t`Domain updated`,
117+
description: t`${editingDomainUrl} from ${orgSlug} successfully updated to ${updateDomain.result.domain}`,
118+
status: 'success',
119+
duration: 9000,
120+
isClosable: true,
121+
position: 'top-left',
122+
})
123+
onClose()
124+
} else if (updateDomain.result.__typename === 'DomainError') {
125+
toast({
126+
title: t`Unable to update domain.`,
127+
description: updateDomain.result.description,
128+
status: 'error',
129+
duration: 9000,
130+
isClosable: true,
131+
position: 'top-left',
132+
})
133+
} else {
134+
toast({
135+
title: t`Incorrect send method received.`,
136+
description: t`Incorrect updateDomain.result typename.`,
137+
status: 'error',
138+
duration: 9000,
139+
isClosable: true,
140+
position: 'top-left',
141+
})
142+
console.log('Incorrect updateDomain.result typename.')
143+
}
144+
},
145+
})
146+
147+
return (
148+
<SlideIn in={isOpen}>
149+
{(styles) => (
150+
<Modal
151+
isOpen={true}
152+
onClose={onClose}
153+
initialFocusRef={initialFocusRef}
154+
>
155+
<ModalOverlay opacity={styles.opacity} />
156+
<ModalContent pb={4} {...styles}>
157+
<Formik
158+
validateOnBlur={false}
159+
initialValues={{
160+
domainUrl: editingDomainUrl,
161+
selectors: selectorInputList,
162+
}}
163+
initialTouched={{
164+
displayName: true,
165+
}}
166+
validationSchema={validationSchema}
167+
onSubmit={async (values) => {
168+
// Submit update detail mutation
169+
170+
if (mutation === 'update') {
171+
await updateDomain({
172+
variables: {
173+
domainId: editingDomainId,
174+
orgId: orgId,
175+
domain: values.domainUrl,
176+
selectors: values.selectors,
177+
},
178+
})
179+
} else if (mutation === 'create') {
180+
await createDomain({
181+
variables: {
182+
orgId: orgId,
183+
domain: values.domainUrl,
184+
selectors: values.selectors,
185+
},
186+
})
187+
}
188+
}}
189+
>
190+
{({ handleSubmit, isSubmitting, values, errors }) => (
191+
<form id="form" onSubmit={handleSubmit}>
192+
<ModalHeader>
193+
{mutation === 'update' ? (
194+
<Trans>Edit Domain Details</Trans>
195+
) : (
196+
<Trans>Add Domain Details</Trans>
197+
)}
198+
</ModalHeader>
199+
<ModalCloseButton />
200+
<ModalBody>
201+
<Stack spacing={4} p={25}>
202+
<Field id="domainUrl" name="domainUrl">
203+
{({ field, form }) => (
204+
<FormControl
205+
isInvalid={
206+
form.errors.domainUrl && form.touched.domainUrl
207+
}
208+
>
209+
<FormLabel htmlFor="domainUrl" fontWeight="bold">
210+
<Trans>Domain URL:</Trans>
211+
</FormLabel>
212+
213+
<Input
214+
mb="2"
215+
{...field}
216+
aria-label="new-domain-url"
217+
id="domainUrl"
218+
placeholder={t`Domain URL`}
219+
ref={initialFocusRef}
220+
/>
221+
<FormErrorMessage>
222+
{form.errors.domainUrl}
223+
</FormErrorMessage>
224+
</FormControl>
225+
)}
226+
</Field>
227+
228+
<FieldArray
229+
name="selectors"
230+
render={(arrayHelpers) => (
231+
<Box>
232+
<Text fontWeight="bold">
233+
<Trans>DKIM Selectors:</Trans>
234+
</Text>
235+
<Grid
236+
gridTemplateColumns="auto 1fr"
237+
gap="0.5em"
238+
alignItems="center"
239+
mb="0.5em"
240+
>
241+
{values.selectors.map((_selector, index) => (
242+
<React.Fragment key={index}>
243+
<TrackerButton
244+
data-testid="remove-dkim-selector"
245+
type="button"
246+
variant="danger"
247+
p="3"
248+
onClick={() => arrayHelpers.remove(index)}
249+
>
250+
<Icon name="minus" size="icons.xs" />
251+
</TrackerButton>
252+
<Field
253+
id={`selectors.${index}`}
254+
name={`selectors.${index}`}
255+
h="1.5rem"
256+
>
257+
{({ field, form }) => (
258+
<FormControl
259+
isInvalid={
260+
form.errors.selectors &&
261+
form.errors.selectors[index] &&
262+
form.touched.selectors &&
263+
form.touched.selectors[index]
264+
}
265+
>
266+
<Input
267+
{...field}
268+
id={`selectors.${index}`}
269+
name={`selectors.${index}`}
270+
placeholder={t`DKIM Selector`}
271+
ref={initialFocusRef}
272+
/>
273+
</FormControl>
274+
)}
275+
</Field>
276+
<Stack
277+
isInline
278+
align="center"
279+
gridColumn="2 / 3"
280+
color="red.500"
281+
>
282+
{errors.selectors &&
283+
errors.selectors[index] && (
284+
<>
285+
<Icon name="warning" mr="0.5em" />
286+
<Text>{errors.selectors[index]}</Text>
287+
</>
288+
)}
289+
</Stack>
290+
</React.Fragment>
291+
))}
292+
</Grid>
293+
<TrackerButton
294+
data-testid="add-dkim-selector"
295+
type="button"
296+
variant="primary"
297+
px="2"
298+
onClick={() => arrayHelpers.push('')}
299+
>
300+
<Icon name="small-add" size="icons.md" />
301+
</TrackerButton>
302+
</Box>
303+
)}
304+
/>
305+
</Stack>
306+
</ModalBody>
307+
308+
<ModalFooter>
309+
<TrackerButton
310+
variant="primary"
311+
isLoading={isSubmitting}
312+
type="submit"
313+
mr="4"
314+
>
315+
<Trans>Confirm</Trans>
316+
</TrackerButton>
317+
</ModalFooter>
318+
</form>
319+
)}
320+
</Formik>
321+
</ModalContent>
322+
</Modal>
323+
)}
324+
</SlideIn>
325+
)
326+
}
327+
328+
AdminDomainModal.propTypes = {
329+
isOpen: bool,
330+
onClose: func,
331+
validationSchema: object,
332+
orgId: string,
333+
editingDomainId: string,
334+
editingDomainUrl: string,
335+
selectorInputList: array,
336+
orgSlug: string,
337+
mutation: string,
338+
}

0 commit comments

Comments
 (0)