HookList.tsx147 lines · main
| 1 | import { PermissionAction } from '@supabase/shared-types/out/constants' |
| 2 | import { useParams } from 'common' |
| 3 | import { includes } from 'lodash' |
| 4 | import { Edit3, MoreVertical, Trash } from 'lucide-react' |
| 5 | import Image from 'next/legacy/image' |
| 6 | import { parseAsString, useQueryState } from 'nuqs' |
| 7 | import { |
| 8 | Badge, |
| 9 | Button, |
| 10 | DropdownMenu, |
| 11 | DropdownMenuContent, |
| 12 | DropdownMenuItem, |
| 13 | DropdownMenuSeparator, |
| 14 | DropdownMenuTrigger, |
| 15 | } from 'ui' |
| 16 | |
| 17 | import Table from '@/components/to-be-cleaned/Table' |
| 18 | import { ButtonTooltip } from '@/components/ui/ButtonTooltip' |
| 19 | import { useDatabaseHooksQuery } from '@/data/database-triggers/database-triggers-query' |
| 20 | import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' |
| 21 | import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' |
| 22 | import { BASE_PATH } from '@/lib/constants' |
| 23 | |
| 24 | export interface HookListProps { |
| 25 | schema: string |
| 26 | filterString: string |
| 27 | } |
| 28 | |
| 29 | export const HookList = ({ schema, filterString }: HookListProps) => { |
| 30 | const { ref } = useParams() |
| 31 | const { data: project } = useSelectedProjectQuery() |
| 32 | const { data: hooks } = useDatabaseHooksQuery({ |
| 33 | projectRef: project?.ref, |
| 34 | connectionString: project?.connectionString, |
| 35 | }) |
| 36 | |
| 37 | const [, setSelectedHookIdToEdit] = useQueryState('edit', parseAsString.withDefault('')) |
| 38 | const [, setSelectedHookIdToDelete] = useQueryState('delete', parseAsString.withDefault('')) |
| 39 | |
| 40 | const restUrl = project?.restUrl |
| 41 | const restUrlTld = restUrl ? new URL(restUrl).hostname.split('.').pop() : 'co' |
| 42 | |
| 43 | const filteredHooks = (hooks ?? []).filter( |
| 44 | (x) => |
| 45 | includes(x.name.toLowerCase(), filterString.toLowerCase()) && |
| 46 | x.schema === schema && |
| 47 | x.function_args.length >= 2 |
| 48 | ) |
| 49 | const { can: canUpdateWebhook } = useAsyncCheckPermissions( |
| 50 | PermissionAction.TENANT_SQL_ADMIN_WRITE, |
| 51 | 'triggers' |
| 52 | ) |
| 53 | |
| 54 | return ( |
| 55 | <> |
| 56 | {filteredHooks.map((x) => { |
| 57 | const isEdgeFunction = (url: string) => |
| 58 | url.includes(`https://${ref}.functions.briven.${restUrlTld}/`) || |
| 59 | url.includes(`https://${ref}.briven.${restUrlTld}/functions/`) |
| 60 | const [url, method] = x.function_args |
| 61 | |
| 62 | return ( |
| 63 | <Table.tr key={x.id}> |
| 64 | <Table.td> |
| 65 | <div className="flex items-center space-x-4"> |
| 66 | <div> |
| 67 | <Image |
| 68 | src={ |
| 69 | isEdgeFunction(url) |
| 70 | ? `${BASE_PATH}/img/function-providers/briven-severless-function.png` |
| 71 | : `${BASE_PATH}/img/function-providers/http-request.png` |
| 72 | } |
| 73 | alt="hook-type" |
| 74 | layout="fixed" |
| 75 | width="20" |
| 76 | height="20" |
| 77 | title={isEdgeFunction(url) ? 'Briven Edge Function' : 'HTTP Request'} |
| 78 | /> |
| 79 | </div> |
| 80 | <p title={x.name} className="truncate"> |
| 81 | {x.name} |
| 82 | </p> |
| 83 | </div> |
| 84 | </Table.td> |
| 85 | <Table.td className="hidden space-x-2 lg:table-cell"> |
| 86 | <p title={x.table}>{x.table}</p> |
| 87 | </Table.td> |
| 88 | <Table.td className="hidden space-x-1 xl:table-cell"> |
| 89 | {x.events.map((event: string) => ( |
| 90 | <Badge key={event}>{event}</Badge> |
| 91 | ))} |
| 92 | </Table.td> |
| 93 | <Table.td className="hidden xl:table-cell"> |
| 94 | <p className="truncate" title={url}> |
| 95 | <code className="text-code-inline">{method}</code>: {url} |
| 96 | </p> |
| 97 | </Table.td> |
| 98 | <Table.td className="text-right"> |
| 99 | <div className="flex justify-end gap-4"> |
| 100 | {canUpdateWebhook ? ( |
| 101 | <DropdownMenu> |
| 102 | <DropdownMenuTrigger asChild> |
| 103 | <Button type="default" className="px-1" icon={<MoreVertical />} /> |
| 104 | </DropdownMenuTrigger> |
| 105 | |
| 106 | <DropdownMenuContent side="left"> |
| 107 | <> |
| 108 | <DropdownMenuItem |
| 109 | className="space-x-2" |
| 110 | onClick={() => setSelectedHookIdToEdit(x.id.toString())} |
| 111 | > |
| 112 | <Edit3 size="14" /> |
| 113 | <p>Edit hook</p> |
| 114 | </DropdownMenuItem> |
| 115 | <DropdownMenuSeparator /> |
| 116 | <DropdownMenuItem |
| 117 | className="space-x-2" |
| 118 | onClick={() => setSelectedHookIdToDelete(x.id.toString())} |
| 119 | > |
| 120 | <Trash stroke="red" size="14" /> |
| 121 | <p>Delete hook</p> |
| 122 | </DropdownMenuItem> |
| 123 | </> |
| 124 | </DropdownMenuContent> |
| 125 | </DropdownMenu> |
| 126 | ) : ( |
| 127 | <ButtonTooltip |
| 128 | disabled |
| 129 | type="default" |
| 130 | className="px-1" |
| 131 | icon={<MoreVertical />} |
| 132 | tooltip={{ |
| 133 | content: { |
| 134 | side: 'bottom', |
| 135 | text: 'You need additional permissions to update webhooks', |
| 136 | }, |
| 137 | }} |
| 138 | /> |
| 139 | )} |
| 140 | </div> |
| 141 | </Table.td> |
| 142 | </Table.tr> |
| 143 | ) |
| 144 | })} |
| 145 | </> |
| 146 | ) |
| 147 | } |