IntrospectionConfirmModal.tsx119 lines · main
1import { type SafeSqlFragment } from '@supabase/pg-meta'
2import type { ReactNode } from 'react'
3import { CodeBlock } from 'ui-patterns/CodeBlock'
4import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
5
6type IntrospectionMode = 'enable' | 'disable'
7
8interface ModeCopy {
9 title: string
10 confirmLabel: string
11 confirmLabelLoading: string
12 persistenceBullet: (schema: string) => ReactNode
13 securityBullet: ReactNode
14}
15
16const COPY: Record<IntrospectionMode, ModeCopy> = {
17 enable: {
18 title: 'Enable GraphQL introspection?',
19 confirmLabel: 'Enable introspection',
20 confirmLabelLoading: 'Enabling...',
21 persistenceBullet: (_schema) => <>This setting persists until explicitly disabled.</>,
22 securityBullet: (
23 <>
24 External actors will be able to introspect your schema using the <code>anon</code> key.
25 </>
26 ),
27 },
28 disable: {
29 title: 'Disable GraphQL introspection?',
30 confirmLabel: 'Disable introspection',
31 confirmLabelLoading: 'Disabling...',
32 persistenceBullet: (_schema) => <>This setting persists until explicitly re-enabled.</>,
33 securityBullet: (
34 <>
35 External actors will no longer be able to introspect your schema via the <code>anon</code>{' '}
36 key. GraphiQL's docs explorer and autocomplete will stop working until introspection is
37 re-enabled.
38 </>
39 ),
40 },
41}
42
43interface IntrospectionConfirmModalProps {
44 mode: IntrospectionMode
45 visible: boolean
46 schema: string
47 sql: SafeSqlFragment
48 otherExistingKeys: string[]
49 existingDirectiveIsMalformed: boolean
50 isPending: boolean
51 onCancel: () => void
52 onConfirm: () => void
53}
54
55export const IntrospectionConfirmModal = ({
56 mode,
57 visible,
58 schema,
59 sql,
60 otherExistingKeys,
61 existingDirectiveIsMalformed,
62 isPending,
63 onCancel,
64 onConfirm,
65}: IntrospectionConfirmModalProps) => {
66 const copy = COPY[mode]
67 const hasPreservedOptions = otherExistingKeys.length > 0
68
69 return (
70 <ConfirmationModal
71 visible={visible}
72 size="large"
73 title={copy.title}
74 confirmLabel={copy.confirmLabel}
75 confirmLabelLoading={copy.confirmLabelLoading}
76 cancelLabel="Cancel"
77 loading={isPending}
78 onCancel={onCancel}
79 onConfirm={onConfirm}
80 >
81 <div className="space-y-4 text-sm">
82 <ul className="list-disc space-y-1 pl-5 text-foreground-light">
83 <li>{copy.persistenceBullet(schema)}</li>
84 <li>{copy.securityBullet}</li>
85 {hasPreservedOptions && (
86 <li>
87 Existing <code>@graphql(...)</code> options on this schema will be preserved:{' '}
88 <PreservedOptionKeys keys={otherExistingKeys} />.
89 </li>
90 )}
91 {existingDirectiveIsMalformed && (
92 <li className="text-warning">
93 The existing <code>@graphql(...)</code> directive on this schema could not be parsed
94 and will be replaced by the statement below.
95 </li>
96 )}
97 </ul>
98
99 <div>
100 <p className="text-foreground-light mb-2">The following statement will be executed:</p>
101 <CodeBlock language="sql" className="text-xs" hideLineNumbers>
102 {sql}
103 </CodeBlock>
104 </div>
105 </div>
106 </ConfirmationModal>
107 )
108}
109
110const PreservedOptionKeys = ({ keys }: { keys: string[] }) => (
111 <>
112 {keys.map((k, i) => (
113 <span key={k}>
114 {i > 0 && ', '}
115 <code>{k}</code>
116 </span>
117 ))}
118 </>
119)