DecryptedReadOnlyInput.tsx109 lines · main
1import { useParams } from 'common'
2import { ExternalLink, Eye, EyeOff, Loader } from 'lucide-react'
3import Link from 'next/link'
4import { useState } from 'react'
5import { Button, CardContent, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
6import { Input } from 'ui-patterns/DataInputs/Input'
7import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
8
9import { useVaultSecretDecryptedValueQuery } from '@/data/vault/vault-secret-decrypted-value-query'
10import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
11
12export const DecryptedReadOnlyInput = ({
13 value,
14 secureEntry,
15 descriptionText,
16 label,
17}: {
18 value?: string
19 secureEntry: boolean
20 descriptionText: string
21 label: string
22}) => {
23 const { ref } = useParams()
24 const { data: project } = useSelectedProjectQuery()
25 const [showHidden, setShowHidden] = useState(false)
26
27 const { data: decryptedValue, isPending: isDecryptedValueLoading } =
28 useVaultSecretDecryptedValueQuery(
29 {
30 projectRef: project?.ref,
31 connectionString: project?.connectionString,
32 id: value ?? '',
33 },
34 { enabled: secureEntry && showHidden }
35 )
36
37 const isLoading = isDecryptedValueLoading && showHidden
38 const renderedValue = secureEntry
39 ? isLoading
40 ? 'Fetching value from Vault...'
41 : showHidden
42 ? decryptedValue
43 : value
44 : value
45
46 return (
47 <CardContent className="py-6 border-b border-panel-border-interior-light">
48 <FormItemLayout
49 layout="horizontal"
50 label={
51 <div className="flex items-center gap-x-2">
52 <span>{label}</span>
53 {secureEntry && (
54 <Tooltip>
55 <TooltipTrigger asChild>
56 <Link
57 target="_blank"
58 rel="noreferrer noopener"
59 href={`/project/${ref}/integrations/vault/secrets?search=${value}`}
60 >
61 <ExternalLink
62 size={14}
63 className="text-foreground-lighter hover:text-foreground transition"
64 />
65 </Link>
66 </TooltipTrigger>
67 <TooltipContent side="bottom">Open in Vault</TooltipContent>
68 </Tooltip>
69 )}
70 </div>
71 }
72 description={descriptionText}
73 isReactForm={false}
74 >
75 <Input
76 readOnly
77 // If the value is secure, allow copying to clipboard if the value is revealed. Otherwise, always allow copying
78 copy={!secureEntry || (!isDecryptedValueLoading && showHidden)}
79 value={renderedValue}
80 type={secureEntry ? (isLoading ? 'text' : showHidden ? 'text' : 'password') : 'text'}
81 actions={
82 secureEntry ? (
83 isLoading ? (
84 <div className="flex items-center justify-center">
85 <Button
86 disabled
87 type="default"
88 className="w-7"
89 icon={<Loader className="animate-spin" />}
90 />
91 </div>
92 ) : (
93 <div className="flex items-center justify-center">
94 <Button
95 type="default"
96 className="w-7"
97 loading={showHidden && isDecryptedValueLoading}
98 icon={showHidden ? <Eye /> : <EyeOff />}
99 onClick={() => setShowHidden(!showHidden)}
100 />
101 </div>
102 )
103 ) : null
104 }
105 />
106 </FormItemLayout>
107 </CardContent>
108 )
109}