Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.6 KiB

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) => {
const nodeTarget: AlertOverrideTarget = {
description: node.description,
name: node.name,
node: node.uuid,
subnodes: [],
type: 'node',
uuid: node.uuid,
};
const subnodeTargets = Object.values(node.hosts)
.sort((a, b) => a.name.localeCompare(b.name))
.reduce<AlertOverrideTarget[]>((previous, subnode) => {
if (subnode.type === 'dr') return previous;
previous.push({
name: subnode.name,
node: node.uuid,
type: 'subnode',
uuid: subnode.uuid,
});
nodeTarget.subnodes?.push(subnode.uuid);
return previous;
}, []);
// Append the options in sequence: node followed by its subnode(s).
options.push(nodeTarget, ...subnodeTargets);
return options;
}, []),
[nodes],
);
const { fetch: getAlertOverrides, loading: loadingAlertOverrides } =
useActiveFetch<APIAlertOverrideOverviewList>({
onData: (data) => setAlertOverrides(data),
url: '/alert-override',
});
const formikAlertOverrides = useMemo<
AlertOverrideFormikValues | undefined
>(() => {
if (!nodes || !alertOverrides) return undefined;
/**
* Group alert override rules based on node UUID. The groups will be used
* for comparison to see whether the subnodes are assigned the same alert
* level.
*
* If subnodes have the same level, they will be consolidated into a single
* target for display. Otherwise, every subnode will get its own visual.
*/
const groups = Object.values(alertOverrides).reduce<
Record<string, APIAlertOverrideOverview[]>
>((previous, override) => {
const {
node: { uuid: nodeUuid },
} = override;
if (previous[nodeUuid]) {
previous[nodeUuid].push(override);
} else {
previous[nodeUuid] = [override];
}
return previous;
}, {});
return Object.entries(groups).reduce<AlertOverrideFormikValues>(
(previous, pair) => {
const [nodeUuid, overrides] = pair;
const [firstOverride, ...restOverrides] = overrides;
const sameLevel =
overrides.length > 1 &&
restOverrides.every(({ level }) => level === firstOverride.level);
if (sameLevel) {
const {
0: { level },
} = overrides;
const { [nodeUuid]: node } = nodes;
previous[nodeUuid] = {
level,
target: {
description: node.description,
name: node.name,
node: node.uuid,
subnodes: overrides.map<string>(({ subnode: { uuid } }) => uuid),
type: 'node',
uuid: node.uuid,
},
uuids: overrides.reduce<Record<string, string>>(
(uuids, { subnode, uuid: overrideUuid }) => {
uuids[overrideUuid] = subnode.uuid;
return uuids;
},
{},
),
};
} else {
overrides.forEach(({ level, node, subnode, uuid: overrideUuid }) => {
previous[subnode.uuid] = {
level,
target: {
name: subnode.name,
node: node.uuid,
type: 'subnode',
uuid: subnode.uuid,
},
uuids: { [overrideUuid]: subnode.uuid },
};
});
}
return previous;
},
{},
);
}, [alertOverrides, nodes]);
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;