Skip to content

Commit

Permalink
uik: Single dialog for default actions (#4193)
Browse files Browse the repository at this point in the history
* feat: enhance form and destination components with edit functionality and improved UI

* fix: update default action parameters to fallback on request body values

* refactor: remove DefaultActionEditDialog component and associated logic

* refactor: comment out setRuleActions logic for future API change

* fix: add data-testid to no actions message for improved testing
  • Loading branch information
mastercactapus authored Dec 12, 2024
1 parent 960fbf5 commit a504b73
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 142 deletions.
1 change: 1 addition & 0 deletions web/src/app/dialogs/FormDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export default function FormDialog(props) {
return (
<DialogContent className={classes.dialogContent}>
<Form
style={{ paddingTop: '1em' }}
id='dialog-form'
className={classes.formContainer}
onSubmit={(e, valid) => {
Expand Down
96 changes: 0 additions & 96 deletions web/src/app/services/UniversalKey/DefaultActionEditDialog.tsx

This file was deleted.

165 changes: 165 additions & 0 deletions web/src/app/services/UniversalKey/UniversalKeyActionDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useState } from 'react'
import { gql, useMutation, useQuery } from 'urql'
import {
Action,
ActionInput,
IntegrationKey,
UpdateKeyConfigInput,
} from '../../../schema'
import FormDialog from '../../dialogs/FormDialog'
import DynamicActionForm, { Value } from '../../selection/DynamicActionForm'
import { useDefaultAction } from '../../util/RequireConfig'
import { useErrorConsumer } from '../../util/ErrorConsumer'

type UniversalKeyActionDialogProps = {
keyID: string

/* The rule ID to add or edit an action for. If not set, operate on the default actions. */
ruleID?: string

/* The action index to edit. If not set, add a new action. */
actionIndex?: number

onClose: () => void
disablePortal?: boolean
}

const query = gql`
query GetKey($keyID: ID!) {
integrationKey(id: $keyID) {
id
config {
rules {
id
actions {
dest {
type
args
}
params
}
}
defaultActions {
dest {
type
args
}
params
}
}
}
}
`

const updateKeyConfig = gql`
mutation UpdateKeyConfig($input: UpdateKeyConfigInput!) {
updateKeyConfig(input: $input)
}
`

function actionToInput(action: Action): ActionInput {
return {
dest: action.dest,
params: action.params,
}
}

export function UniversalKeyActionDialog(
props: UniversalKeyActionDialogProps,
): React.ReactNode {
const defaultAction = useDefaultAction()
const [q] = useQuery<{ integrationKey: IntegrationKey }>({
query,
variables: { keyID: props.keyID },
})
if (q.error) throw q.error

const config = q.data?.integrationKey.config
if (!config) throw new Error('missing config')

const rule = config.rules.find((r) => r.id === props.ruleID) || null
if (props.ruleID && !rule) throw new Error('missing rule')
const actions = rule ? rule.actions : config.defaultActions
const action =
props.actionIndex !== undefined ? actions[props.actionIndex] : null
const [value, setValue] = useState<Value>({
destType: action?.dest.type || defaultAction.dest.type,
staticParams: action?.dest.args || {},
dynamicParams: action?.params || defaultAction.params,
})
const [m, commit] = useMutation(updateKeyConfig)

const verb = action ? 'Edit' : 'Add'
const title = `${verb} ${rule ? '' : 'Default '}Action${rule?.name ? ` for Rule "${rule.name}"` : ''}`

const input = { keyID: props.keyID } as UpdateKeyConfigInput
const newAction = {
dest: {
type: value.destType + 'brok',
args: value.staticParams,
},
params: value.dynamicParams,
}
if (rule && props.actionIndex !== undefined) {
// Edit rule action
// TODO: Commented out until next PR when this API change is introduced
// input.setRuleActions = {
// id: rule.id,
// actions: actions
// .map(actionToInput)
// .map((a, idx) => (idx === props.actionIndex ? newAction : a)),
// }
} else if (rule) {
// Add rule action
// TODO: Commented out until next PR when this API change is introduced
// input.setRuleActions = {
// id: rule.id,
// actions: actions.map(actionToInput).concat(newAction),
// }
} else if (props.actionIndex !== undefined) {
// Edit default action
input.defaultActions = actions
.map(actionToInput)
.map((a, idx) => (idx === props.actionIndex ? newAction : a))
} else {
// Add default action
input.defaultActions = actions.map(actionToInput).concat(newAction)
}

const errs = useErrorConsumer(m.error)

return (
<FormDialog
title={title}
onClose={props.onClose}
loading={m.fetching}
maxWidth='md'
errors={errs.remainingLegacyCallback()}
onSubmit={() =>
commit({ input }, { additionalTypenames: ['KeyConfig'] }).then(
(res) => {
if (res.error) return

props.onClose()
},
)
}
form={
<DynamicActionForm
disablePortal={props.disablePortal}
value={value}
onChange={setValue}
staticParamErrors={errs.getErrorMap('updateKeyConfig')}
dynamicParamErrors={errs.getErrorMap(
/updateKeyConfig.input.defaultActions.\d+.params/,
)}
/>
}
PaperProps={{
sx: {
minHeight: '500px',
},
}}
/>
)
}
31 changes: 21 additions & 10 deletions web/src/app/services/UniversalKey/UniversalKeyActionsList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React from 'react'
import { ActionInput } from '../../../schema'
import DestinationInputChip from '../../util/DestinationInputChip'
import { Grid, Typography } from '@mui/material'
import { Chip, Grid } from '@mui/material'
import { Warning } from '../../icons'

export type UniversalKeyActionsListProps = {
actions: ReadonlyArray<ActionInput>

noEdit?: boolean // disables onDelete and onChipClick
onDelete?: (action: ActionInput) => void
onChipClick?: (action: ActionInput) => void
onEdit?: (index: number) => void
}

export default function UniversalKeyActionsList(
Expand All @@ -24,7 +26,7 @@ export default function UniversalKeyActionsList(
sx={{ p: 1 }}
data-testid='actions-list'
>
{props.actions.map((a) => (
{props.actions.map((a, idx) => (
<Grid item key={JSON.stringify(a.dest)}>
<DestinationInputChip
value={a.dest}
Expand All @@ -38,19 +40,28 @@ export default function UniversalKeyActionsList(
? () => props.onChipClick && props.onChipClick(a)
: undefined
}
onEdit={
props.onEdit && !props.noEdit
? () => props.onEdit && props.onEdit(idx)
: undefined
}
/>
</Grid>
))}
</Grid>
{props.actions.length === 0 && (
<Grid item xs={12} data-testid='actions-list'>
<Typography
variant='body2'
color='textSecondary'
data-testid='no-actions'
>
No actions
</Typography>
<Grid item xs={12}>
<Chip
label='No actions'
icon={
<div style={{ padding: '4px' }} data-testid='no-actions'>
<Warning
placement='bottom'
message='With no actions configured, nothing will happen when this rule matches'
/>
</div>
}
/>
</Grid>
)}
</React.Fragment>
Expand Down
Loading

0 comments on commit a504b73

Please sign in to comment.