WrappersTab.tsx108 lines · main
1import { PermissionAction } from '@supabase/shared-types/out/constants'
2import { useParams } from 'common'
3import { HTMLProps, ReactNode, useCallback, useState } from 'react'
4import { Sheet, SheetContent } from 'ui'
5
6import { CreateWrapperSheet } from './CreateWrapperSheet'
7import { WRAPPERS } from './Wrappers.constants'
8import { wrapperMetaComparator } from './Wrappers.utils'
9import { WrapperTable } from './WrapperTable'
10import { DiscardChangesConfirmationDialog } from '@/components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog'
11import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
12import { useFDWsQuery } from '@/data/fdw/fdws-query'
13import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
14import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
15import { useConfirmOnClose } from '@/hooks/ui/useConfirmOnClose'
16
17export const WrappersTab = () => {
18 const { id } = useParams()
19 const { data: project } = useSelectedProjectQuery()
20 const [createWrapperShown, setCreateWrapperShown] = useState(false)
21
22 const { can: canCreateWrapper } = useAsyncCheckPermissions(
23 PermissionAction.TENANT_SQL_ADMIN_WRITE,
24 'wrappers'
25 )
26
27 const { data } = useFDWsQuery({
28 projectRef: project?.ref,
29 connectionString: project?.connectionString,
30 })
31
32 const wrappers = data ?? []
33 const wrapperMeta = WRAPPERS.find((w) => w.name === id)
34
35 // this contains a collection of all wrapper instances for the wrapper type
36 const createdWrappers = wrapperMeta
37 ? wrappers.filter((w) => wrapperMetaComparator(wrapperMeta, w))
38 : []
39
40 const [isDirty, setIsDirty] = useState(false)
41 const { confirmOnClose, handleOpenChange, modalProps } = useConfirmOnClose({
42 checkIsDirty: useCallback(() => isDirty, [isDirty]),
43 onClose: useCallback(() => {
44 setCreateWrapperShown(false)
45 setIsDirty(false)
46 }, []),
47 })
48
49 const Container = useCallback(
50 ({ ...props }: { children: ReactNode } & HTMLProps<HTMLDivElement>) => (
51 <div className="w-full mx-10 py-10 ">
52 {props.children}
53 <Sheet open={!!createWrapperShown} onOpenChange={handleOpenChange}>
54 <SheetContent size="lg" tabIndex={undefined}>
55 {wrapperMeta && (
56 <CreateWrapperSheet
57 wrapperMeta={wrapperMeta}
58 onDirty={setIsDirty}
59 onClose={() => setCreateWrapperShown(false)}
60 onCloseWithConfirmation={confirmOnClose}
61 />
62 )}
63 </SheetContent>
64 </Sheet>
65 </div>
66 ),
67 [createWrapperShown, handleOpenChange, wrapperMeta, confirmOnClose]
68 )
69
70 if (!wrapperMeta) {
71 return <div>Missing integration.</div>
72 }
73
74 if (createdWrappers.length === 0) {
75 return (
76 <Container>
77 <div className=" w-full h-48 max-w-4xl">
78 <div className="border rounded-lg h-full flex flex-col gap-y-2 items-center justify-center">
79 <p className="text-sm text-foreground-light">
80 No {wrapperMeta.label} wrappers have been installed
81 </p>
82 <ButtonTooltip
83 type="default"
84 onClick={() => setCreateWrapperShown(true)}
85 disabled={!canCreateWrapper}
86 tooltip={{
87 content: {
88 text: !canCreateWrapper
89 ? 'You need additional permissions to create a foreign data wrapper'
90 : undefined,
91 },
92 }}
93 >
94 Add new wrapper
95 </ButtonTooltip>
96 </div>
97 </div>
98 </Container>
99 )
100 }
101
102 return (
103 <Container>
104 <WrapperTable />
105 <DiscardChangesConfirmationDialog {...modalProps} />
106 </Container>
107 )
108}