Move EditSecretModal to be child of SecretRow

In the accompanying PR for `DeleteSecretModal`, that one will also be moved over to `SecretRow`
This commit is contained in:
Drake Costa committed 2025-07-21 04:07:17 -07:00
1 parent f24fb8c7aa
commit 9104e47079
4 files changed
+81 -75

No files matched your search

@@ -27,7 +27,7 @@ import { EyeOff, Eye } from 'lucide-react'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
interface EditSecretModalProps {
selectedSecret: VaultSecret | undefined
secret: VaultSecret | undefined
onClose: () => void
}
@@ -37,39 +37,39 @@ const SecretSchema = z.object({
secret: z.string().min(1, 'Please enter your secret value'),
})
const EditSecretModal = ({ selectedSecret, onClose }: EditSecretModalProps) => {
const EditSecretModal = ({ secret, onClose }: EditSecretModalProps) => {
const [showSecretValue, setShowSecretValue] = useState(false)
const { project } = useProjectContext()
const formId = 'edit-vault-secret-form'
const { data, isLoading: isLoadingSecretValue } = useVaultSecretDecryptedValueQuery(
{
projectRef: project?.ref!,
id: selectedSecret?.id!,
id: secret?.id!,
connectionString: project?.connectionString,
},
{ enabled: selectedSecret !== undefined && !!(project?.ref && selectedSecret?.id) }
{ enabled: secret !== undefined && !!(project?.ref && secret?.id) }
)
const form = useForm<z.infer<typeof SecretSchema>>({
resolver: zodResolver(SecretSchema),
defaultValues: {
name: selectedSecret?.name ?? '',
description: selectedSecret?.description ?? '',
secret: selectedSecret?.decryptedSecret ?? data ?? '',
name: secret?.name ?? '',
description: secret?.description ?? '',
secret: secret?.decryptedSecret ?? data ?? '',
},
values: {
name: selectedSecret?.name ?? '',
description: selectedSecret?.description ?? '',
secret: selectedSecret?.decryptedSecret ?? data ?? '',
name: secret?.name ?? '',
description: secret?.description ?? '',
secret: secret?.decryptedSecret ?? data ?? '',
},
})
useEffect(() => {
if (selectedSecret !== undefined) {
if (secret !== undefined) {
setShowSecretValue(false)
}
}, [selectedSecret])
}, [secret])
const { mutateAsync: updateSecret, isLoading: isSubmitting } = useVaultSecretUpdateMutation()
const { mutate: updateSecret, isLoading: isSubmitting } = useVaultSecretUpdateMutation()
const onSubmit: SubmitHandler<z.infer<typeof SecretSchema>> = async (values) => {
if (!project) return console.error('Project is required')
@@ -77,15 +77,15 @@ const EditSecretModal = ({ selectedSecret, onClose }: EditSecretModalProps) => {
const payload: Partial<VaultSecret> = {
secret: values.secret,
}
if (values.name !== selectedSecret?.name) payload.name = values.name
if (values.description !== selectedSecret?.description) payload.description = values.description
if (values.name !== secret?.name) payload.name = values.name
if (values.description !== secret?.description) payload.description = values.description
if (!isEmpty(payload) && selectedSecret) {
if (!isEmpty(payload) && secret) {
updateSecret(
{
projectRef: project.ref,
connectionString: project?.connectionString,
id: selectedSecret.id,
id: secret.id,
...payload,
},
{
@@ -103,7 +103,7 @@ const EditSecretModal = ({ selectedSecret, onClose }: EditSecretModalProps) => {
return (
<Dialog
open={selectedSecret !== undefined}
open
onOpenChange={(open) => {
if (!open) {
form.reset()
@@ -20,17 +20,17 @@ import { useVaultSecretDecryptedValueQuery } from 'data/vault/vault-secret-decry
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Edit3, Eye, EyeOff, Key, Loader, MoreVertical, Trash } from 'lucide-react'
import type { VaultSecret } from 'types'
import EditSecretModal from './EditSecretModal'
interface SecretRowProps {
secret: VaultSecret
onSelectEdit: (secret: VaultSecret) => void
onSelectRemove: (secret: VaultSecret) => void
}
const SecretRow = ({ secret, onSelectEdit, onSelectRemove }: SecretRowProps) => {
const { ref } = useParams()
const { project } = useProjectContext()
const [modal, setModal] = useState<string | null>(null)
const [revealSecret, setRevealSecret] = useState(false)
const name = secret?.name ?? 'No name provided'
@@ -46,6 +46,15 @@ const SecretRow = ({ secret, onSelectEdit, onSelectRemove }: SecretRowProps) =>
enabled: !!(ref! && secret.id) && revealSecret,
}
)
const renderModal = () => {
const onClose = () => setModal(null)
switch (modal) {
case `edit`:
return <EditSecretModal secret={secret} onClose={onClose} />
default:
return null
}
}
return (
<div className="px-6 py-4 flex items-center space-x-4">
@@ -95,31 +104,25 @@ const SecretRow = ({ secret, onSelectEdit, onSelectRemove }: SecretRowProps) =>
{secret.updated_at === secret.created_at ? 'Added' : 'Updated'} on{' '}
{dayjs(secret.updated_at).format('MMM D, YYYY')}
</p>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button title="Manage Secret" type="text" className="px-1" icon={<MoreVertical />} />
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end" className="w-32">
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuItem
className="space-x-2"
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
title="Manage Secret"
type="text"
className="px-1"
disabled={!canManageSecrets}
onClick={() => onSelectEdit(secret)}
>
icon={<MoreVertical />}
/>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end" className="w-32">
<DropdownMenuItem className="space-x-2" onClick={() => setModal(`edit`)}>
<Edit3 size="14" />
<p>Edit</p>
</DropdownMenuItem>
</TooltipTrigger>
{!canManageSecrets && (
<TooltipContent side="bottom">
You need additional permissions to edit secrets
</TooltipContent>
)}
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuItem
className="space-x-2"
disabled={!canManageSecrets}
@@ -128,15 +131,16 @@ const SecretRow = ({ secret, onSelectEdit, onSelectRemove }: SecretRowProps) =>
<Trash stroke="red" size="14" />
<p className="text-foreground-light">Delete</p>
</DropdownMenuItem>
</TooltipTrigger>
{!canManageSecrets && (
<TooltipContent side="bottom">
You need additional permissions to delete secrets
</TooltipContent>
)}
</Tooltip>
</DropdownMenuContent>
</DropdownMenu>
</DropdownMenuContent>
</DropdownMenu>
</TooltipTrigger>
{!canManageSecrets && (
<TooltipContent side="bottom">
You need additional permissions to manage secrets
</TooltipContent>
)}
</Tooltip>
{renderModal()}
</div>
</div>
)
@@ -9,22 +9,29 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import type { VaultSecret } from 'types'
import { Button, Input, Listbox, Separator } from 'ui'
import {
Button,
Input,
Select_Shadcn_,
SelectContent_Shadcn_,
SelectItem_Shadcn_,
SelectTrigger_Shadcn_,
SelectValue_Shadcn_,
Separator,
} from 'ui'
import AddNewSecretModal from './AddNewSecretModal'
import DeleteSecretModal from './DeleteSecretModal'
import EditSecretModal from './EditSecretModal'
import SecretRow from './SecretRow'
import { VaultSecret } from 'types'
export const SecretsManagement = () => {
const { search } = useParams()
const { project } = useProjectContext()
const [searchValue, setSearchValue] = useState<string>('')
const [selectedSort, setSelectedSort] = useState<'updated_at' | 'name'>('updated_at')
const [showAddSecretModal, setShowAddSecretModal] = useState(false)
const [selectedSecretToEdit, setSelectedSecretToEdit] = useState<VaultSecret>()
const [selectedSecretToRemove, setSelectedSecretToRemove] = useState<VaultSecret>()
const [selectedSort, setSelectedSort] = useState('updated_at')
const canManageSecrets = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
@@ -49,7 +56,7 @@ export const SecretsManagement = () => {
if (selectedSort === 'updated_at') {
return Number(new Date(s.updated_at))
} else {
return s[selectedSort]
return s[selectedSort as keyof VaultSecret]
}
}
)
@@ -81,26 +88,26 @@ export const SecretsManagement = () => {
: []
}
/>
<div className="md:w-44">
<Listbox size="tiny" value={selectedSort} onChange={setSelectedSort}>
<Listbox.Option
id="updated_at"
className="max-w-[180px]"
value="updated_at"
label="Sort by updated at"
>
<Select_Shadcn_ value={selectedSort} onValueChange={setSelectedSort}>
<SelectTrigger_Shadcn_ size="tiny" className="md:w-44">
<SelectValue_Shadcn_ asChild>
<>Sort by {selectedSort}</>
</SelectValue_Shadcn_>
</SelectTrigger_Shadcn_>
<SelectContent_Shadcn_>
<SelectItem_Shadcn_ value="updated_at" className="text-xs">
Updated at
</Listbox.Option>
<Listbox.Option
</SelectItem_Shadcn_>
<SelectItem_Shadcn_ value="name" className="text-xs">
id="name"
className="max-w-[180px]"
value="name"
label="Sort by name"
>
Name
</Listbox.Option>
</Listbox>
</div>
</SelectItem_Shadcn_>
</SelectContent_Shadcn_>
</Select_Shadcn_>
</div>
<div className="flex items-center gap-x-2">
<DocsButton href="https://supabase.com/docs/guides/database/vault" />
@@ -136,7 +143,6 @@ export const SecretsManagement = () => {
<Fragment key={`secret-${idx}`}>
<SecretRow
secret={secret}
onSelectEdit={setSelectedSecretToEdit}
onSelectRemove={setSelectedSecretToRemove}
/>
{idx !== secrets.length - 1 && <Separator />}
@@ -167,12 +173,8 @@ export const SecretsManagement = () => {
</div>
</div>
<EditSecretModal
selectedSecret={selectedSecretToEdit}
onClose={() => setSelectedSecretToEdit(undefined)}
/>
<DeleteSecretModal
selectedSecret={selectedSecretToRemove}
secret={selectedSecretToRemove}
onClose={() => setSelectedSecretToRemove(undefined)}
/>
<AddNewSecretModal
@@ -47,7 +47,7 @@ const Page = ({ onClose }: { onClose: () => void }) => {
</DropdownMenu>
<EditSecretModal
selectedSecret={selectedSecretToEdit}
secret={selectedSecretToEdit}
onClose={() => {
setSelectedSecretToEdit(undefined)
onClose()