Skip to content

Commit

Permalink
add actions menu with delete option to scm webhooks (#1330)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsladerman authored Aug 23, 2024
1 parent 784c9a7 commit c4c6027
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 77 deletions.
2 changes: 1 addition & 1 deletion assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@nivo/pie": "0.83.0",
"@nivo/radial-bar": "0.83.0",
"@nivo/tooltip": "0.83.0",
"@pluralsh/design-system": "3.67",
"@pluralsh/design-system": "3.67.1",
"@react-hooks-library/core": "0.6.0",
"@saas-ui/use-hotkeys": "1.1.3",
"@tanstack/react-virtual": "3.0.1",
Expand Down
145 changes: 125 additions & 20 deletions assets/src/components/pr/scm/ScmWebhooksColumns.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import { ArrowTopRightIcon, Button } from '@pluralsh/design-system'
import {
ArrowTopRightIcon,
CopyIcon,
ListBoxItem,
TrashCanIcon,
} from '@pluralsh/design-system'
import { createColumnHelper } from '@tanstack/react-table'
import styled, { useTheme } from 'styled-components'

import { ScmType, ScmWebhookFragment } from 'generated/graphql'
import { Edge } from 'utils/graphql'
import {
ScmType,
ScmWebhookFragment,
ScmWebhooksDocument,
useDeleteScmWebhookMutation,
} from 'generated/graphql'
import { Edge, removeConnection, updateCache } from 'utils/graphql'

import { StopPropagation } from 'components/utils/StopPropagation'
import { TruncateStart } from 'components/utils/table/TruncateStart'
import { DateTimeCol } from 'components/utils/table/DateTimeCol'
import CopyButton from 'components/utils/CopyButton'
import { DateTimeCol } from 'components/utils/table/DateTimeCol'
import { TruncateStart } from 'components/utils/table/TruncateStart'

import { MoreMenu } from 'components/utils/MoreMenu'

import { useState } from 'react'

import { Confirm } from 'components/utils/Confirm'

import { ScmTypeCell, scmTypeToLabel } from './PrScmConnectionsColumns'
import { SCM_WEBHOOKS_Q_VARS } from './ScmWebhooks'

export const columnHelper = createColumnHelper<Edge<ScmWebhookFragment>>()

Expand Down Expand Up @@ -84,31 +100,120 @@ function getWebhookUrl(scmWebhook: Nullable<ScmWebhookFragment>) {
return undefined
}

function DeleteScmWebhookModal({
scmWebhook,
open,
onClose,
}: {
scmWebhook: ScmWebhookFragment
open: boolean
onClose: Nullable<() => void>
}) {
const theme = useTheme()
const [mutation, { loading, error }] = useDeleteScmWebhookMutation({
variables: { id: scmWebhook.id },
update: (cache, { data }) =>
updateCache(cache, {
variables: SCM_WEBHOOKS_Q_VARS,
query: ScmWebhooksDocument,
update: (prev) =>
removeConnection(prev, data?.deleteScmWebhook, 'scmWebhooks'),
}),
onCompleted: () => {
onClose?.()
},
})

return (
<Confirm
close={() => onClose?.()}
destructive
label="Delete"
loading={loading}
error={error}
open={open}
submit={() => mutation()}
title="Delete SCM connection"
text={
<>
Are you sure you want to delete the{' '}
<span css={{ color: theme.colors['text-danger'] }}>
{scmWebhook.name}
</span>{' '}
webhook?
</>
}
/>
)
}

enum MenuItemKey {
Manage = 'manage',
Copy = 'copy',
Delete = 'delete',
}

export const ColActions = columnHelper.display({
id: 'actions',
header: '',
cell: function Cell({ row }) {
const theme = useTheme()
const [menuKey, setMenuKey] = useState<MenuItemKey | ''>()
const scmWebhook = row.original.node
const url = getWebhookUrl(scmWebhook)
const url = scmWebhook?.url
const scmName = scmWebhook?.type ? scmTypeToLabel[scmWebhook?.type] : ''

if (!url) {
if (!scmWebhook) {
return null
}

if (menuKey === MenuItemKey.Manage) {
window.open(getWebhookUrl(scmWebhook), '_blank', 'noopener,noreferrer')
setMenuKey('')
}

if (menuKey === MenuItemKey.Copy) {
if (url) {
navigator.clipboard.writeText(url)
}
setMenuKey('')
}

return (
<StopPropagation>
<Button
secondary
as="a"
href={url}
target="_blank"
rel="noopener noreferrer"
endIcon={<ArrowTopRightIcon />}
>
Manage{scmName && <> on {scmName}</>}
</Button>
</StopPropagation>
<>
<MoreMenu onSelectionChange={(newKey) => setMenuKey(newKey)}>
<ListBoxItem
key={MenuItemKey.Manage}
leftContent={<ArrowTopRightIcon color="icon-default" />}
label={
<a css={{ textDecoration: 'none', color: 'inherit' }}>
{`Manage${scmName ? ` on ${scmName}` : ''}`}
</a>
}
textValue="Manage"
/>
<ListBoxItem
key={MenuItemKey.Copy}
leftContent={<CopyIcon color={theme.colors['icon-default']} />}
label="Copy webhook URL"
textValue="Copy webhook URL"
/>
<ListBoxItem
key={MenuItemKey.Delete}
leftContent={
<TrashCanIcon color={theme.colors['icon-danger-critical']} />
}
label="Delete webhook"
textValue="Delete webhook"
/>
</MoreMenu>
{/* Modals */}
<DeleteScmWebhookModal
scmWebhook={scmWebhook}
open={menuKey === MenuItemKey.Delete}
onClose={() => setMenuKey('')}
/>
</>
)
},
})
Expand Down
99 changes: 48 additions & 51 deletions assets/src/components/utils/Confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { GraphQLError } from 'graphql'
import { GraphQLErrors } from '@apollo/client/errors'

import { GqlError } from './Alert'
import { ModalMountTransition } from './ModalMountTransition'

type ConfirmProps = {
open: boolean
Expand Down Expand Up @@ -40,57 +39,55 @@ export function Confirm({
const theme = useTheme()

return (
<ModalMountTransition open={open}>
<Modal
header={title}
open={open}
onClose={close || undefined}
actions={
<>
<Button
type="button"
secondary
onClick={close}
>
Cancel
</Button>
<Button
type="submit"
destructive={destructive}
onClick={submit}
loading={loading}
marginLeft="medium"
>
{label || 'Confirm'}
</Button>
</>
}
>
{error && (
<div
css={{
marginBottom: theme.spacing.large,
}}
<Modal
header={title}
open={open}
onClose={close || undefined}
actions={
<>
<Button
type="button"
secondary
onClick={close}
>
<GqlError
error={
errorMessage
? ({
graphQLErrors: [
{ message: errorMessage } as GraphQLError,
] as GraphQLErrors,
} as ApolloError)
: error
}
header={errorHeader}
/>
</div>
)}
<div css={{ ...theme.partials.text.body1, color: theme.colors.text }}>
{text}
Cancel
</Button>
<Button
type="submit"
destructive={destructive}
onClick={submit}
loading={loading}
marginLeft="medium"
>
{label || 'Confirm'}
</Button>
</>
}
>
{error && (
<div
css={{
marginBottom: theme.spacing.large,
}}
>
<GqlError
error={
errorMessage
? ({
graphQLErrors: [
{ message: errorMessage } as GraphQLError,
] as GraphQLErrors,
} as ApolloError)
: error
}
header={errorHeader}
/>
</div>
{extraContent}
</Modal>
</ModalMountTransition>
)}
<div css={{ ...theme.partials.text.body1, color: theme.colors.text }}>
{text}
</div>
{extraContent}
</Modal>
)
}
41 changes: 41 additions & 0 deletions assets/src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8137,6 +8137,13 @@ export type CreateScmWebhookMutationVariables = Exact<{

export type CreateScmWebhookMutation = { __typename?: 'RootMutationType', createScmWebhook?: { __typename?: 'ScmWebhook', id: string, name: string, owner: string, type: ScmType, url: string, insertedAt?: string | null, updatedAt?: string | null } | null };

export type DeleteScmWebhookMutationVariables = Exact<{
id: Scalars['ID']['input'];
}>;


export type DeleteScmWebhookMutation = { __typename?: 'RootMutationType', deleteScmWebhook?: { __typename?: 'ScmWebhook', id: string, name: string, owner: string, type: ScmType, url: string, insertedAt?: string | null, updatedAt?: string | null } | null };

export type CreateScmWebhookPointerMutationVariables = Exact<{
attributes: ScmWebhookAttributes;
}>;
Expand Down Expand Up @@ -13046,6 +13053,39 @@ export function useCreateScmWebhookMutation(baseOptions?: Apollo.MutationHookOpt
export type CreateScmWebhookMutationHookResult = ReturnType<typeof useCreateScmWebhookMutation>;
export type CreateScmWebhookMutationResult = Apollo.MutationResult<CreateScmWebhookMutation>;
export type CreateScmWebhookMutationOptions = Apollo.BaseMutationOptions<CreateScmWebhookMutation, CreateScmWebhookMutationVariables>;
export const DeleteScmWebhookDocument = gql`
mutation DeleteScmWebhook($id: ID!) {
deleteScmWebhook(id: $id) {
...ScmWebhook
}
}
${ScmWebhookFragmentDoc}`;
export type DeleteScmWebhookMutationFn = Apollo.MutationFunction<DeleteScmWebhookMutation, DeleteScmWebhookMutationVariables>;

/**
* __useDeleteScmWebhookMutation__
*
* To run a mutation, you first call `useDeleteScmWebhookMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useDeleteScmWebhookMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [deleteScmWebhookMutation, { data, loading, error }] = useDeleteScmWebhookMutation({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useDeleteScmWebhookMutation(baseOptions?: Apollo.MutationHookOptions<DeleteScmWebhookMutation, DeleteScmWebhookMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<DeleteScmWebhookMutation, DeleteScmWebhookMutationVariables>(DeleteScmWebhookDocument, options);
}
export type DeleteScmWebhookMutationHookResult = ReturnType<typeof useDeleteScmWebhookMutation>;
export type DeleteScmWebhookMutationResult = Apollo.MutationResult<DeleteScmWebhookMutation>;
export type DeleteScmWebhookMutationOptions = Apollo.BaseMutationOptions<DeleteScmWebhookMutation, DeleteScmWebhookMutationVariables>;
export const CreateScmWebhookPointerDocument = gql`
mutation CreateScmWebhookPointer($attributes: ScmWebhookAttributes!) {
createScmWebhookPointer(attributes: $attributes) {
Expand Down Expand Up @@ -21081,6 +21121,7 @@ export const namedOperations = {
DeleteScmConnection: 'DeleteScmConnection',
SetupRenovate: 'SetupRenovate',
CreateScmWebhook: 'CreateScmWebhook',
DeleteScmWebhook: 'DeleteScmWebhook',
CreateScmWebhookPointer: 'CreateScmWebhookPointer',
CreateObjectStore: 'CreateObjectStore',
UpdateObjectStore: 'UpdateObjectStore',
Expand Down
6 changes: 6 additions & 0 deletions assets/src/graph/automation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ mutation CreateScmWebhook($connectionId: ID!, $owner: String!) {
}
}

mutation DeleteScmWebhook($id: ID!) {
deleteScmWebhook(id: $id) {
...ScmWebhook
}
}

mutation CreateScmWebhookPointer($attributes: ScmWebhookAttributes!) {
createScmWebhookPointer(attributes: $attributes) {
...ScmWebhook
Expand Down
Loading

0 comments on commit c4c6027

Please sign in to comment.