mirror of
https://github.com/supabase/supabase.git
synced 2026-09-08 19:08:44 +08:00
<img width="1512" height="862" alt="image" src="https://github.com/user-attachments/assets/79a6d4dc-dcd2-489f-97d7-3ee7a0196b7d" /> ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature / UI refactor. ## What is the current behavior? Assistant Edge Function approval nests `ConfirmFooter` under the function block. `addToolApprovalResponse` is wired whenever state is `approval-requested`, including automatic approvals. ## What is the new behavior? Introduces a `Confirm` card that owns the frame, with the footer attached below the body. Edge Function approval uses that card. Interactive Approve/Deny only runs for manual `approval-requested` parts (`!approval.isAutomatic`), matching the [AI SDK tool-approvals `useChat` guidelines](https://ai-sdk.dev/docs/agents/tool-approvals). SQL still uses `DisplayBlockRenderer` until #49170. `ConfirmFooter` is inlined into `Confirm` so SQL can keep importing the named footer until that PR. ## Additional context Part of stack #49171. Base: `chore/ai-sdk-7` (#49167). Notebook proposal Confirm wrapping is **not** in this stack — that file lives on [#49159](https://github.com/supabase/supabase/pull/49159). Follow up after that stack merges. ## Test plan - [ ] Deploy-edge-function tool part shows Confirm with Skip / Deploy - [ ] Existing-function replace warning still requires the second confirm - [ ] After approve, footer morphs to loading and buttons disable - [ ] `Confirm.utils.test.ts` and `EdgeFunctionRenderer.test.tsx` pass <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added confirmation cards for AI-assisted actions, including approve and cancel controls. * Improved handling of manual approval requests for SQL execution, notebook changes, and Edge Function deployment. * Added support for customizing report and Edge Function block styling. * **Bug Fixes** * Automatic approvals no longer appear as pending manual confirmations. * Skipped SQL actions now provide clearer messaging. * **Tests** * Expanded coverage for approval states, confirmation controls, and automatic decisions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
197 lines
6.1 KiB
TypeScript
197 lines
6.1 KiB
TypeScript
import { Code } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import type { DragEvent, ReactNode } from 'react'
|
|
import { Button, cn } from 'ui'
|
|
import { Admonition } from 'ui-patterns/Admonition'
|
|
import { CodeBlock, type CodeBlockLang } from 'ui-patterns/CodeBlock'
|
|
|
|
import { ReportBlockContainer } from '@/components/interfaces/Reports/ReportBlock/ReportBlockContainer'
|
|
|
|
interface EdgeFunctionBlockProps {
|
|
/** Title of the EdgeFunctionBlock */
|
|
label: string
|
|
/** Function code to display */
|
|
code: string
|
|
/** Function name/slug */
|
|
functionName: string
|
|
/** Any other actions specific to the parent to be rendered in the header */
|
|
actions?: ReactNode
|
|
/** Toggle visiblity of code on render */
|
|
showCode?: boolean
|
|
/** Whether function block is draggable */
|
|
draggable?: boolean
|
|
/** Tooltip when hovering over the header of the block */
|
|
tooltip?: ReactNode
|
|
/** Optional callback on drag start */
|
|
onDragStart?: (e: DragEvent) => void
|
|
/** Hide the header deploy button (used when an external confirm footer is shown) */
|
|
hideDeployButton?: boolean
|
|
/** Disable interactive actions */
|
|
disabled?: boolean
|
|
/** Whether a deploy action is currently running */
|
|
isDeploying?: boolean
|
|
/** Whether a deploy action has completed */
|
|
isDeployed?: boolean
|
|
/** Optional message to show when deployment fails */
|
|
errorText?: string
|
|
/** URL to the deployed function */
|
|
functionUrl?: string
|
|
/** Link to the function details page */
|
|
deploymentDetailsUrl?: string
|
|
/** CLI command to download the function */
|
|
downloadCommand?: string
|
|
/** Show warning UI when replacing an existing function */
|
|
showReplaceWarning?: boolean
|
|
/** Cancel handler when replacing an existing function */
|
|
onCancelReplace?: () => void
|
|
/** Confirm handler when replacing an existing function */
|
|
onConfirmReplace?: () => void
|
|
/** Handler for triggering a deploy */
|
|
onDeploy?: () => void
|
|
className?: string
|
|
}
|
|
|
|
export const EdgeFunctionBlock = ({
|
|
label,
|
|
code,
|
|
functionName,
|
|
actions,
|
|
tooltip,
|
|
hideDeployButton = false,
|
|
disabled = false,
|
|
isDeploying = false,
|
|
isDeployed = false,
|
|
errorText,
|
|
functionUrl,
|
|
deploymentDetailsUrl,
|
|
downloadCommand,
|
|
showReplaceWarning = false,
|
|
onCancelReplace,
|
|
onConfirmReplace,
|
|
onDeploy,
|
|
draggable = false,
|
|
onDragStart,
|
|
className,
|
|
}: EdgeFunctionBlockProps) => {
|
|
const resolvedFunctionUrl = functionUrl ?? 'Function URL will be available after deployment'
|
|
const resolvedDownloadCommand = downloadCommand ?? `supabase functions download ${functionName}`
|
|
|
|
const hasStatusMessage = isDeploying || isDeployed || !!errorText
|
|
|
|
return (
|
|
<ReportBlockContainer
|
|
tooltip={tooltip}
|
|
icon={<Code size={16} strokeWidth={1.5} className="text-foreground-muted" />}
|
|
label={label}
|
|
loading={isDeploying}
|
|
draggable={draggable}
|
|
onDragStart={onDragStart}
|
|
className={className}
|
|
actions={
|
|
hideDeployButton || !onDeploy ? (
|
|
(actions ?? null)
|
|
) : (
|
|
<>
|
|
<Button
|
|
variant="outline"
|
|
size="tiny"
|
|
loading={isDeploying}
|
|
disabled={disabled || isDeploying}
|
|
onClick={onDeploy}
|
|
>
|
|
{isDeploying ? 'Deploying...' : 'Deploy'}
|
|
</Button>
|
|
|
|
{actions}
|
|
</>
|
|
)
|
|
}
|
|
>
|
|
{showReplaceWarning && (
|
|
<Admonition
|
|
type="warning"
|
|
className="rounded-none border-0 border-b shrink-0 bg-background-100"
|
|
>
|
|
<p>An edge function with the name "{functionName}" already exists.</p>
|
|
<p className="text-foreground-light">
|
|
Deploying will replace the existing function. Are you sure you want to proceed?
|
|
</p>
|
|
<div className="flex justify-stretch mt-2 gap-2">
|
|
<Button
|
|
variant="outline"
|
|
size="tiny"
|
|
className="w-full flex-1"
|
|
disabled={isDeploying}
|
|
onClick={onCancelReplace}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
variant="danger"
|
|
size="tiny"
|
|
className="w-full flex-1"
|
|
loading={isDeploying}
|
|
disabled={isDeploying}
|
|
onClick={onConfirmReplace}
|
|
>
|
|
Replace function
|
|
</Button>
|
|
</div>
|
|
</Admonition>
|
|
)}
|
|
|
|
<div className="shrink-0 w-full max-h-96 overflow-y-auto">
|
|
<CodeBlock
|
|
hideLineNumbers
|
|
wrapLines={false}
|
|
value={code}
|
|
language={'typescript' as CodeBlockLang}
|
|
className={cn(
|
|
'max-w-none block bg-transparent! py-3! px-3.5! prose dark:prose-dark border-0 text-foreground rounded-none! w-full',
|
|
'[&>code]:m-0 [&>code>span]:text-foreground'
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
{hasStatusMessage && (
|
|
<div className="p-4 w-full border-t bg-surface-75 text-xs">
|
|
{isDeploying ? (
|
|
<p className="text-foreground-light">Deploying function...</p>
|
|
) : errorText ? (
|
|
<p className="text-danger">{errorText}</p>
|
|
) : (
|
|
<>
|
|
<p className="text-foreground-light mb-2">
|
|
The{' '}
|
|
{deploymentDetailsUrl ? (
|
|
<Link className="text-foreground" href={deploymentDetailsUrl}>
|
|
new function
|
|
</Link>
|
|
) : (
|
|
<span className="text-foreground">new function</span>
|
|
)}{' '}
|
|
is now live at:
|
|
</p>
|
|
<CodeBlock
|
|
language="bash"
|
|
hideLineNumbers
|
|
value={resolvedFunctionUrl}
|
|
className="text-xs p-2"
|
|
/>
|
|
<p className="text-foreground-light mt-4 mb-2">
|
|
To download and work on this function locally, use the CLI command:
|
|
</p>
|
|
<CodeBlock
|
|
hideLineNumbers
|
|
language="bash"
|
|
value={resolvedDownloadCommand}
|
|
className="text-xs p-2"
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
</ReportBlockContainer>
|
|
)
|
|
}
|