parent
1f5bf604e1
commit
3ea2fcc520
10 changed files with 751 additions and 0 deletions
@ -0,0 +1,287 @@ |
|||||||
|
import { Grid, menuClasses as muiMenuClasses } from '@mui/material'; |
||||||
|
import { FC, useMemo } from 'react'; |
||||||
|
import { v4 as uuidv4 } from 'uuid'; |
||||||
|
|
||||||
|
import ActionGroup from '../ActionGroup'; |
||||||
|
import api from '../../lib/api'; |
||||||
|
import FlexBox from '../FlexBox'; |
||||||
|
import FormSummary from '../FormSummary'; |
||||||
|
import handleAPIError from '../../lib/handleAPIError'; |
||||||
|
import mailRecipientListSchema from './schema'; |
||||||
|
import ManageAlertOverride from './ManageAlertOverride'; |
||||||
|
import MessageGroup from '../MessageGroup'; |
||||||
|
import OutlinedInputWithLabel from '../OutlinedInputWithLabel'; |
||||||
|
import SelectWithLabel from '../SelectWithLabel'; |
||||||
|
import { BodyText, SmallText } from '../Text'; |
||||||
|
import UncontrolledInput from '../UncontrolledInput'; |
||||||
|
import useFormikUtils from '../../hooks/useFormikUtils'; |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO: add descriptions to each item: |
||||||
|
* |
||||||
|
=head4 1 / critical |
||||||
|
|
||||||
|
Alerts at this level will go to all recipients, except for those ignoring the source system entirely. |
||||||
|
|
||||||
|
This is reserved for alerts that could lead to imminent service interruption or unexpected loss of redundancy. |
||||||
|
|
||||||
|
Alerts at this level should trigger alarm systems for all administrators as well as management who may be impacted by service interruptions. |
||||||
|
|
||||||
|
=head4 2 / warning |
||||||
|
|
||||||
|
This is used for alerts that require attention from administrators. Examples include intentional loss of redundancy caused by load shedding, hardware in pre-failure, loss of input power, temperature anomalies, etc. |
||||||
|
|
||||||
|
Alerts at this level should trigger alarm systems for administrative staff. |
||||||
|
|
||||||
|
=head4 3 / notice |
||||||
|
|
||||||
|
This is used for alerts that are generally safe to ignore, but might provide early warnings of developing issues or insight into system behaviour.
|
||||||
|
|
||||||
|
Alerts at this level should not trigger alarm systems. Periodic review is sufficient. |
||||||
|
|
||||||
|
=head4 4 / info |
||||||
|
|
||||||
|
This is used for alerts that are almost always safe to ignore, but may be useful in testing and debugging.
|
||||||
|
*
|
||||||
|
*/ |
||||||
|
const LEVEL_OPTIONS: SelectItem<number>[] = [ |
||||||
|
{ |
||||||
|
displayValue: ( |
||||||
|
<FlexBox spacing={0}> |
||||||
|
<BodyText inheritColour fontWeight="inherit"> |
||||||
|
Critical |
||||||
|
</BodyText> |
||||||
|
<SmallText inheritColour whiteSpace="normal"> |
||||||
|
Alerts that could lead to imminent service interruption or unexpected |
||||||
|
loss of redundancy. |
||||||
|
</SmallText> |
||||||
|
</FlexBox> |
||||||
|
), |
||||||
|
value: 1, |
||||||
|
}, |
||||||
|
{ |
||||||
|
displayValue: ( |
||||||
|
<FlexBox spacing={0}> |
||||||
|
<BodyText inheritColour fontWeight="inherit"> |
||||||
|
Warning |
||||||
|
</BodyText> |
||||||
|
<SmallText inheritColour whiteSpace="normal"> |
||||||
|
Alerts that require attention from administrators, such as redundancy |
||||||
|
loss due to load shedding, hardware in pre-failure, input power loss, |
||||||
|
temperature anomalies, etc. |
||||||
|
</SmallText> |
||||||
|
</FlexBox> |
||||||
|
), |
||||||
|
value: 2, |
||||||
|
}, |
||||||
|
{ |
||||||
|
displayValue: ( |
||||||
|
<FlexBox spacing={0}> |
||||||
|
<BodyText inheritColour fontWeight="inherit"> |
||||||
|
Notice |
||||||
|
</BodyText> |
||||||
|
<SmallText inheritColour whiteSpace="normal"> |
||||||
|
Alerts that are generally safe to ignore, but might provide early |
||||||
|
warnings of developing issues or insight into system behaviour. |
||||||
|
</SmallText> |
||||||
|
</FlexBox> |
||||||
|
), |
||||||
|
value: 3, |
||||||
|
}, |
||||||
|
{ |
||||||
|
displayValue: ( |
||||||
|
<FlexBox spacing={0}> |
||||||
|
<BodyText inheritColour fontWeight="inherit"> |
||||||
|
Info |
||||||
|
</BodyText> |
||||||
|
<SmallText inheritColour whiteSpace="normal"> |
||||||
|
Alerts that are almost always safe to ignore, but may be useful in |
||||||
|
testing and debugging. |
||||||
|
</SmallText> |
||||||
|
</FlexBox> |
||||||
|
), |
||||||
|
value: 4, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
const MAP_TO_LEVEL_LABEL: Record<number, string> = { |
||||||
|
1: 'Critical', |
||||||
|
2: 'Warning', |
||||||
|
3: 'Notice', |
||||||
|
4: 'Info', |
||||||
|
}; |
||||||
|
|
||||||
|
const AddMailRecipientForm: FC<AddMailRecipientFormProps> = (props) => { |
||||||
|
const { |
||||||
|
alertOverrideTargetOptions, |
||||||
|
mailRecipientUuid, |
||||||
|
previousFormikValues, |
||||||
|
tools, |
||||||
|
} = props; |
||||||
|
|
||||||
|
const mrUuid = useMemo<string>( |
||||||
|
() => mailRecipientUuid ?? uuidv4(), |
||||||
|
[mailRecipientUuid], |
||||||
|
); |
||||||
|
|
||||||
|
const formikUtils = useFormikUtils<MailRecipientFormikValues>({ |
||||||
|
initialValues: previousFormikValues ?? { |
||||||
|
[mrUuid]: { |
||||||
|
alertOverrides: {}, |
||||||
|
email: '', |
||||||
|
language: 'en_CA', |
||||||
|
level: 2, |
||||||
|
name: '', |
||||||
|
uuid: mrUuid, |
||||||
|
}, |
||||||
|
}, |
||||||
|
onSubmit: (values, { setSubmitting }) => { |
||||||
|
const { [mrUuid]: mailRecipient } = values; |
||||||
|
const { confirm } = tools; |
||||||
|
|
||||||
|
let actionProceedText: string = 'Add'; |
||||||
|
let errorMessage: React.ReactNode = <>Failed to add mail recipient.</>; |
||||||
|
let method: 'post' | 'put' = 'post'; |
||||||
|
let successMessage: React.ReactNode = <>Mail recipient added.</>; |
||||||
|
let titleText: string = `Add mail recipient with the following?`; |
||||||
|
let url: string = '/mail-recipient'; |
||||||
|
|
||||||
|
if (previousFormikValues) { |
||||||
|
actionProceedText = 'Update'; |
||||||
|
errorMessage = <>Failed to update mail server.</>; |
||||||
|
method = 'put'; |
||||||
|
successMessage = <>Mail recipient updated.</>; |
||||||
|
titleText = `Update ${mailRecipient.name} with the following?`; |
||||||
|
url += `/${mrUuid}`; |
||||||
|
} |
||||||
|
|
||||||
|
const { alertOverrides, uuid, ...mrBody } = mailRecipient; |
||||||
|
|
||||||
|
confirm.prepare({ |
||||||
|
actionProceedText, |
||||||
|
content: <FormSummary entries={mrBody} />, |
||||||
|
onCancelAppend: () => setSubmitting(false), |
||||||
|
onProceedAppend: () => { |
||||||
|
confirm.loading(true); |
||||||
|
|
||||||
|
api[method](url, mrBody) |
||||||
|
.then(() => confirm.finish('Success', { children: successMessage })) |
||||||
|
.catch((error) => { |
||||||
|
const emsg = handleAPIError(error); |
||||||
|
|
||||||
|
emsg.children = ( |
||||||
|
<> |
||||||
|
{errorMessage} {emsg.children} |
||||||
|
</> |
||||||
|
); |
||||||
|
|
||||||
|
confirm.finish('Error', emsg); |
||||||
|
}) |
||||||
|
.finally(() => setSubmitting(false)); |
||||||
|
}, |
||||||
|
titleText, |
||||||
|
}); |
||||||
|
|
||||||
|
confirm.open(true); |
||||||
|
}, |
||||||
|
validationSchema: mailRecipientListSchema, |
||||||
|
}); |
||||||
|
|
||||||
|
const { disabledSubmit, formik, formikErrors, handleChange } = formikUtils; |
||||||
|
|
||||||
|
const emailChain = useMemo<string>(() => `${mrUuid}.email`, [mrUuid]); |
||||||
|
const levelChain = useMemo<string>(() => `${mrUuid}.level`, [mrUuid]); |
||||||
|
const nameChain = useMemo<string>(() => `${mrUuid}.name`, [mrUuid]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<Grid |
||||||
|
columns={{ xs: 1, sm: 2 }} |
||||||
|
component="form" |
||||||
|
container |
||||||
|
onSubmit={(event) => { |
||||||
|
event.preventDefault(); |
||||||
|
|
||||||
|
formik.submitForm(); |
||||||
|
}} |
||||||
|
spacing="1em" |
||||||
|
> |
||||||
|
<Grid item xs={1}> |
||||||
|
<UncontrolledInput |
||||||
|
input={ |
||||||
|
<OutlinedInputWithLabel |
||||||
|
id={nameChain} |
||||||
|
label="Recipient name" |
||||||
|
name={nameChain} |
||||||
|
onChange={handleChange} |
||||||
|
required |
||||||
|
value={formik.values[mrUuid].name} |
||||||
|
/> |
||||||
|
} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
<Grid item xs={1}> |
||||||
|
<UncontrolledInput |
||||||
|
input={ |
||||||
|
<OutlinedInputWithLabel |
||||||
|
id={emailChain} |
||||||
|
label="Recipient email" |
||||||
|
name={emailChain} |
||||||
|
onChange={handleChange} |
||||||
|
required |
||||||
|
value={formik.values[mrUuid].email} |
||||||
|
/> |
||||||
|
} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
<Grid item xs={1}> |
||||||
|
<UncontrolledInput |
||||||
|
input={ |
||||||
|
<SelectWithLabel |
||||||
|
id={levelChain} |
||||||
|
label="Alert level" |
||||||
|
name={levelChain} |
||||||
|
onChange={formik.handleChange} |
||||||
|
required |
||||||
|
selectItems={LEVEL_OPTIONS} |
||||||
|
selectProps={{ |
||||||
|
MenuProps: { |
||||||
|
sx: { |
||||||
|
[`& .${muiMenuClasses.paper}`]: { |
||||||
|
maxWidth: { md: '60%', lg: '40%' }, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
renderValue: (value) => MAP_TO_LEVEL_LABEL[value], |
||||||
|
}} |
||||||
|
value={formik.values[mrUuid].level} |
||||||
|
/> |
||||||
|
} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
<Grid item width="100%"> |
||||||
|
<ManageAlertOverride |
||||||
|
alertOverrideTargetOptions={alertOverrideTargetOptions} |
||||||
|
formikUtils={formikUtils} |
||||||
|
mailRecipientUuid={mrUuid} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
<Grid item width="100%"> |
||||||
|
<MessageGroup count={1} messages={formikErrors} /> |
||||||
|
</Grid> |
||||||
|
<Grid item width="100%"> |
||||||
|
<ActionGroup |
||||||
|
actions={[ |
||||||
|
{ |
||||||
|
background: 'blue', |
||||||
|
children: previousFormikValues ? 'Update' : 'Add', |
||||||
|
disabled: disabledSubmit, |
||||||
|
type: 'submit', |
||||||
|
}, |
||||||
|
]} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
</Grid> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default AddMailRecipientForm; |
@ -0,0 +1,117 @@ |
|||||||
|
import { Grid } from '@mui/material'; |
||||||
|
import { FC, useMemo } from 'react'; |
||||||
|
import { v4 as uuidv4 } from 'uuid'; |
||||||
|
|
||||||
|
import Autocomplete from '../Autocomplete'; |
||||||
|
import FlexBox from '../FlexBox'; |
||||||
|
import IconButton from '../IconButton'; |
||||||
|
import SelectWithLabel from '../SelectWithLabel'; |
||||||
|
import { BodyText, SmallText } from '../Text'; |
||||||
|
import UncontrolledInput from '../UncontrolledInput'; |
||||||
|
|
||||||
|
const LEVEL_OPTIONS: SelectItem<number>[] = [ |
||||||
|
{ displayValue: 'Critical', value: 1 }, |
||||||
|
{ displayValue: 'Warning', value: 2 }, |
||||||
|
{ displayValue: 'Notice', value: 3 }, |
||||||
|
{ displayValue: 'Info', value: 4 }, |
||||||
|
]; |
||||||
|
|
||||||
|
const AlertOverrideInputGroup: FC<AlertOverrideInputGroupProps> = (props) => { |
||||||
|
const { |
||||||
|
alertOverrideTargetOptions, |
||||||
|
alertOverrideUuid, |
||||||
|
mailRecipientUuid: mrUuid, |
||||||
|
formikUtils, |
||||||
|
} = props; |
||||||
|
|
||||||
|
const aoUuid = useMemo<string>( |
||||||
|
() => alertOverrideUuid ?? uuidv4(), |
||||||
|
[alertOverrideUuid], |
||||||
|
); |
||||||
|
|
||||||
|
const { formik } = formikUtils; |
||||||
|
const { |
||||||
|
values: { [mrUuid]: mailRecipient }, |
||||||
|
} = formik; |
||||||
|
const { |
||||||
|
alertOverrides: { [aoUuid]: alertOverride }, |
||||||
|
} = mailRecipient; |
||||||
|
|
||||||
|
const overrideChain = useMemo<string>( |
||||||
|
() => `${mrUuid}.alertOverrides.${aoUuid}`, |
||||||
|
[aoUuid, mrUuid], |
||||||
|
); |
||||||
|
const targetChain = useMemo<string>( |
||||||
|
() => `${overrideChain}.target`, |
||||||
|
[overrideChain], |
||||||
|
); |
||||||
|
const levelChain = useMemo<string>( |
||||||
|
() => `${overrideChain}.level`, |
||||||
|
[overrideChain], |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<Grid |
||||||
|
alignItems="center" |
||||||
|
columns={{ xs: 1, sm: 10 }} |
||||||
|
container |
||||||
|
justifyContent="stretch" |
||||||
|
spacing="1em" |
||||||
|
> |
||||||
|
<Grid item xs={6}> |
||||||
|
<Autocomplete |
||||||
|
getOptionLabel={(option) => option.name} |
||||||
|
id={targetChain} |
||||||
|
isOptionEqualToValue={(option, value) => option.uuid === value.uuid} |
||||||
|
label="Target" |
||||||
|
noOptionsText="No node or subnode found." |
||||||
|
onChange={(event, value) => |
||||||
|
formik.setFieldValue(targetChain, value, true) |
||||||
|
} |
||||||
|
openOnFocus |
||||||
|
options={alertOverrideTargetOptions} |
||||||
|
renderOption={(optionProps, option) => ( |
||||||
|
<li {...optionProps} key={`${option.node}-${option.uuid}`}> |
||||||
|
{option.type === 'node' ? ( |
||||||
|
<FlexBox spacing={0}> |
||||||
|
<BodyText inheritColour>{option.name}</BodyText> |
||||||
|
<SmallText inheritColour>{option.description}</SmallText> |
||||||
|
</FlexBox> |
||||||
|
) : ( |
||||||
|
<BodyText inheritColour paddingLeft=".6em"> |
||||||
|
{option.name} |
||||||
|
</BodyText> |
||||||
|
)} |
||||||
|
</li> |
||||||
|
)} |
||||||
|
value={alertOverride.target} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
<Grid item flexGrow={1}> |
||||||
|
<UncontrolledInput |
||||||
|
input={ |
||||||
|
<SelectWithLabel |
||||||
|
id={levelChain} |
||||||
|
label="Alert level" |
||||||
|
name={levelChain} |
||||||
|
onChange={formik.handleChange} |
||||||
|
selectItems={LEVEL_OPTIONS} |
||||||
|
value={alertOverride.level} |
||||||
|
/> |
||||||
|
} |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
<Grid item width="min-content"> |
||||||
|
<IconButton |
||||||
|
mapPreset="delete" |
||||||
|
onClick={() => { |
||||||
|
formik.setFieldValue(overrideChain, undefined, true); |
||||||
|
}} |
||||||
|
size="small" |
||||||
|
/> |
||||||
|
</Grid> |
||||||
|
</Grid> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default AlertOverrideInputGroup; |
@ -0,0 +1,9 @@ |
|||||||
|
import { FC } from 'react'; |
||||||
|
|
||||||
|
import AddMailRecipientForm from './AddMailRecipientForm'; |
||||||
|
|
||||||
|
const EditMailRecipientForm: FC<EditMailRecipientFormProps> = (props) => ( |
||||||
|
<AddMailRecipientForm {...props} /> |
||||||
|
); |
||||||
|
|
||||||
|
export default EditMailRecipientForm; |
@ -0,0 +1,58 @@ |
|||||||
|
import { FC } from 'react'; |
||||||
|
import { v4 as uuidv4 } from 'uuid'; |
||||||
|
|
||||||
|
import AlertOverrideInputGroup from './AlertOverrideInputGroup'; |
||||||
|
import List from '../List'; |
||||||
|
import useChecklist from '../../hooks/useChecklist'; |
||||||
|
|
||||||
|
const ManageAlertOverride: FC<ManageAlertOverrideProps> = (props) => { |
||||||
|
const { |
||||||
|
alertOverrideTargetOptions, |
||||||
|
formikUtils, |
||||||
|
mailRecipientUuid: mrUuid, |
||||||
|
} = props; |
||||||
|
|
||||||
|
const { formik } = formikUtils; |
||||||
|
const { |
||||||
|
values: { [mrUuid]: mailRecipient }, |
||||||
|
} = formik; |
||||||
|
const { alertOverrides } = mailRecipient; |
||||||
|
|
||||||
|
const { hasChecks } = useChecklist(alertOverrides); |
||||||
|
|
||||||
|
return ( |
||||||
|
<List |
||||||
|
allowAddItem |
||||||
|
disableDelete={!hasChecks} |
||||||
|
edit |
||||||
|
header="Alert override rules" |
||||||
|
listEmpty="No alert overrides(s)" |
||||||
|
listItems={alertOverrides} |
||||||
|
onAdd={() => { |
||||||
|
const aoUuid = uuidv4(); |
||||||
|
|
||||||
|
formik.setValues((previous) => { |
||||||
|
const current = { ...previous }; |
||||||
|
|
||||||
|
current[mrUuid].alertOverrides[aoUuid] = { |
||||||
|
level: 2, |
||||||
|
target: null, |
||||||
|
uuid: aoUuid, |
||||||
|
}; |
||||||
|
|
||||||
|
return current; |
||||||
|
}); |
||||||
|
}} |
||||||
|
renderListItem={(uuid) => ( |
||||||
|
<AlertOverrideInputGroup |
||||||
|
alertOverrideTargetOptions={alertOverrideTargetOptions} |
||||||
|
alertOverrideUuid={uuid} |
||||||
|
formikUtils={formikUtils} |
||||||
|
mailRecipientUuid={mrUuid} |
||||||
|
/> |
||||||
|
)} |
||||||
|
/> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default ManageAlertOverride; |
@ -0,0 +1,160 @@ |
|||||||
|
import { FC, useMemo, useState } from 'react'; |
||||||
|
|
||||||
|
import AddMailRecipientForm from './AddMailRecipientForm'; |
||||||
|
import { toAnvilOverviewList } from '../../lib/api_converters'; |
||||||
|
import CrudList from '../CrudList'; |
||||||
|
import EditMailRecipientForm from './EditMailRecipientForm'; |
||||||
|
import { BodyText } from '../Text'; |
||||||
|
import useActiveFetch from '../../hooks/useActiveFetch'; |
||||||
|
import useFetch from '../../hooks/useFetch'; |
||||||
|
|
||||||
|
const ManageMailRecipient: FC = () => { |
||||||
|
const [alertOverrides, setAlertOverrides] = useState< |
||||||
|
APIAlertOverrideOverviewList | undefined |
||||||
|
>(); |
||||||
|
|
||||||
|
const { altData: nodes, loading: loadingNodes } = useFetch< |
||||||
|
APIAnvilOverviewArray, |
||||||
|
APIAnvilOverviewList |
||||||
|
>('/anvil', { mod: toAnvilOverviewList }); |
||||||
|
|
||||||
|
const alertOverrideTargetOptions = useMemo<AlertOverrideTarget[] | undefined>( |
||||||
|
() => |
||||||
|
nodes && |
||||||
|
Object.values(nodes) |
||||||
|
.sort((a, b) => a.name.localeCompare(b.name)) |
||||||
|
.reduce<AlertOverrideTarget[]>((options, node) => { |
||||||
|
options.push({ |
||||||
|
description: node.description, |
||||||
|
name: node.name, |
||||||
|
node: node.uuid, |
||||||
|
type: 'node', |
||||||
|
uuid: node.uuid, |
||||||
|
}); |
||||||
|
|
||||||
|
Object.values(node.hosts) |
||||||
|
.sort((a, b) => a.name.localeCompare(b.name)) |
||||||
|
.forEach((subnode) => { |
||||||
|
if (subnode.type === 'dr') return; |
||||||
|
|
||||||
|
options.push({ |
||||||
|
name: subnode.name, |
||||||
|
node: node.uuid, |
||||||
|
type: 'subnode', |
||||||
|
uuid: subnode.uuid, |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
return options; |
||||||
|
}, []), |
||||||
|
[nodes], |
||||||
|
); |
||||||
|
|
||||||
|
const { fetch: getAlertOverrides, loading: loadingAlertOverrides } = |
||||||
|
useActiveFetch<APIAlertOverrideOverviewList>({ |
||||||
|
onData: (data) => setAlertOverrides(data), |
||||||
|
url: '/alert-override', |
||||||
|
}); |
||||||
|
|
||||||
|
const formikAlertOverrides = useMemo< |
||||||
|
AlertOverrideFormikValues | undefined |
||||||
|
>(() => { |
||||||
|
if (!alertOverrides) return undefined; |
||||||
|
|
||||||
|
const groups: Record<string, number> = {}; |
||||||
|
|
||||||
|
return Object.values(alertOverrides).reduce<AlertOverrideFormikValues>( |
||||||
|
(previous, value) => { |
||||||
|
const { level, node, subnode, uuid } = value; |
||||||
|
|
||||||
|
groups[node.uuid] = groups[node.uuid] ? groups[node.uuid] + 1 : 1; |
||||||
|
|
||||||
|
previous[uuid] = { |
||||||
|
level, |
||||||
|
target: |
||||||
|
groups[node.uuid] > 1 |
||||||
|
? { |
||||||
|
name: node.name, |
||||||
|
node: node.uuid, |
||||||
|
type: 'node', |
||||||
|
uuid: node.uuid, |
||||||
|
} |
||||||
|
: { |
||||||
|
name: subnode.name, |
||||||
|
node: node.uuid, |
||||||
|
type: 'subnode', |
||||||
|
uuid: subnode.uuid, |
||||||
|
}, |
||||||
|
uuid, |
||||||
|
}; |
||||||
|
|
||||||
|
return previous; |
||||||
|
}, |
||||||
|
{}, |
||||||
|
); |
||||||
|
}, [alertOverrides]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<CrudList<APIMailRecipientOverview, APIMailRecipientDetail> |
||||||
|
addHeader="Add mail recipient" |
||||||
|
editHeader={(entry) => `Update ${entry?.name}`} |
||||||
|
entriesUrl="/mail-recipient" |
||||||
|
getAddLoading={(previous) => previous || loadingNodes} |
||||||
|
getDeleteErrorMessage={({ children, ...rest }) => ({ |
||||||
|
...rest, |
||||||
|
children: <>Failed to delete mail recipient(s). {children}</>, |
||||||
|
})} |
||||||
|
getDeleteHeader={(count) => |
||||||
|
`Delete the following ${count} mail recipient(s)?` |
||||||
|
} |
||||||
|
getDeleteSuccessMessage={() => ({ |
||||||
|
children: <>Successfully deleted mail recipient(s).</>, |
||||||
|
})} |
||||||
|
getEditLoading={(previous) => previous || loadingAlertOverrides} |
||||||
|
onItemClick={(base, ...args) => { |
||||||
|
const [, mailRecipientUuid] = args; |
||||||
|
|
||||||
|
base(...args); |
||||||
|
|
||||||
|
getAlertOverrides(undefined, { |
||||||
|
params: { 'mail-recipient': mailRecipientUuid }, |
||||||
|
}); |
||||||
|
}} |
||||||
|
renderAddForm={(tools) => |
||||||
|
alertOverrideTargetOptions && ( |
||||||
|
<AddMailRecipientForm |
||||||
|
alertOverrideTargetOptions={alertOverrideTargetOptions} |
||||||
|
tools={tools} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
renderDeleteItem={(mailRecipientList, { key }) => { |
||||||
|
const mr = mailRecipientList?.[key]; |
||||||
|
|
||||||
|
return <BodyText>{mr?.name}</BodyText>; |
||||||
|
}} |
||||||
|
renderEditForm={(tools, mailRecipient) => |
||||||
|
alertOverrideTargetOptions && |
||||||
|
mailRecipient && |
||||||
|
formikAlertOverrides && ( |
||||||
|
<EditMailRecipientForm |
||||||
|
alertOverrideTargetOptions={alertOverrideTargetOptions} |
||||||
|
mailRecipientUuid={mailRecipient.uuid} |
||||||
|
previousFormikValues={{ |
||||||
|
[mailRecipient.uuid]: { |
||||||
|
alertOverrides: formikAlertOverrides, |
||||||
|
...mailRecipient, |
||||||
|
}, |
||||||
|
}} |
||||||
|
tools={tools} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
renderListItem={(uuid, { name }) => <BodyText>{name}</BodyText>} |
||||||
|
/> |
||||||
|
</> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default ManageMailRecipient; |
@ -0,0 +1,4 @@ |
|||||||
|
import AddMailRecipientForm from './AddMailRecipientForm'; |
||||||
|
import ManageMailRecipient from './ManageMailRecipient'; |
||||||
|
|
||||||
|
export { AddMailRecipientForm, ManageMailRecipient }; |
@ -0,0 +1,33 @@ |
|||||||
|
import * as yup from 'yup'; |
||||||
|
|
||||||
|
import buildYupDynamicObject from '../../lib/buildYupDynamicObject'; |
||||||
|
|
||||||
|
const alertLevelSchema = yup.number().oneOf([1, 2, 3, 4]); |
||||||
|
|
||||||
|
const alertOverrideSchema = yup.object({ |
||||||
|
level: alertLevelSchema.required(), |
||||||
|
target: yup.object({ |
||||||
|
type: yup.string().oneOf(['node', 'subnode']).required(), |
||||||
|
uuid: yup.string().uuid().required(), |
||||||
|
}), |
||||||
|
uuid: yup.string().uuid().required(), |
||||||
|
}); |
||||||
|
|
||||||
|
const alertOverrideListSchema = yup.lazy((entries) => |
||||||
|
yup.object(buildYupDynamicObject(entries, alertOverrideSchema)), |
||||||
|
); |
||||||
|
|
||||||
|
const mailRecipientSchema = yup.object({ |
||||||
|
alertOverrides: alertOverrideListSchema, |
||||||
|
email: yup.string().email().required(), |
||||||
|
language: yup.string().oneOf(['en_CA']).optional(), |
||||||
|
level: alertLevelSchema.required(), |
||||||
|
name: yup.string().required(), |
||||||
|
uuid: yup.string().uuid().required(), |
||||||
|
}); |
||||||
|
|
||||||
|
const mailRecipientListSchema = yup.lazy((entries) => |
||||||
|
yup.object(buildYupDynamicObject(entries, mailRecipientSchema)), |
||||||
|
); |
||||||
|
|
||||||
|
export default mailRecipientListSchema; |
@ -0,0 +1,11 @@ |
|||||||
|
type APIAlertOverrideOverview = { |
||||||
|
level: number; |
||||||
|
mailRecipient: APIMailRecipientOverview; |
||||||
|
node: { name: string; uuid: string }; |
||||||
|
subnode: { name: string; short: string; uuid: string }; |
||||||
|
uuid: string; |
||||||
|
}; |
||||||
|
|
||||||
|
type APIAlertOverrideOverviewList = { |
||||||
|
[uuid: string]: APIAlertOverrideOverview; |
||||||
|
}; |
@ -0,0 +1,11 @@ |
|||||||
|
type APIMailRecipientOverview = { name: string; uuid: string }; |
||||||
|
|
||||||
|
type APIMailRecipientOverviewList = { |
||||||
|
[uuid: string]: APIMailRecipientOverview; |
||||||
|
}; |
||||||
|
|
||||||
|
type APIMailRecipientDetail = APIMailRecipientOverview & { |
||||||
|
email: string; |
||||||
|
language: string; |
||||||
|
level: number; |
||||||
|
}; |
@ -0,0 +1,61 @@ |
|||||||
|
type AlertOverrideTarget = { |
||||||
|
description?: string; |
||||||
|
name: string; |
||||||
|
node: string; |
||||||
|
type: 'node' | 'subnode'; |
||||||
|
uuid: string; |
||||||
|
}; |
||||||
|
|
||||||
|
type AlertOverrideFormikAlertOverride = { |
||||||
|
level: number; |
||||||
|
target: AlertOverrideTarget | null; |
||||||
|
uuid: string; |
||||||
|
}; |
||||||
|
|
||||||
|
type AlertOverrideFormikValues = { |
||||||
|
[uuid: string]: AlertOverrideFormikAlertOverride; |
||||||
|
}; |
||||||
|
|
||||||
|
type MailRecipientFormikMailRecipient = APIMailRecipientDetail & { |
||||||
|
alertOverrides: AlertOverrideFormikValues; |
||||||
|
}; |
||||||
|
|
||||||
|
type MailRecipientFormikValues = { |
||||||
|
[uuid: string]: MailRecipientFormikMailRecipient; |
||||||
|
}; |
||||||
|
|
||||||
|
/** AddMailRecipientForm */ |
||||||
|
|
||||||
|
type AddMailRecipientFormOptionalProps = { |
||||||
|
mailRecipientUuid?: string; |
||||||
|
previousFormikValues?: MailRecipientFormikValues; |
||||||
|
}; |
||||||
|
|
||||||
|
type AddMailRecipientFormProps = AddMailRecipientFormOptionalProps & { |
||||||
|
alertOverrideTargetOptions: AlertOverrideTarget[]; |
||||||
|
tools: CrudListFormTools; |
||||||
|
}; |
||||||
|
|
||||||
|
/** EditMailRecipientForm */ |
||||||
|
|
||||||
|
type EditMailRecipientFormProps = Required<AddMailRecipientFormProps>; |
||||||
|
|
||||||
|
/** ManageAlertOverride */ |
||||||
|
|
||||||
|
type ManageAlertOverrideProps = Required< |
||||||
|
Pick< |
||||||
|
AddMailRecipientFormProps, |
||||||
|
'alertOverrideTargetOptions' | 'mailRecipientUuid' |
||||||
|
> |
||||||
|
> & { |
||||||
|
formikUtils: FormikUtils<MailRecipientFormikValues>; |
||||||
|
}; |
||||||
|
|
||||||
|
/** AlertOverrideInputGroup */ |
||||||
|
|
||||||
|
type AlertOverrideInputGroupOptionalProps = { |
||||||
|
alertOverrideUuid?: string; |
||||||
|
}; |
||||||
|
|
||||||
|
type AlertOverrideInputGroupProps = AlertOverrideInputGroupOptionalProps & |
||||||
|
ManageAlertOverrideProps; |
Loading…
Reference in new issue