EdgeFunctionBlock.tsx193 lines · main
| 1 | import { Code } from 'lucide-react' |
| 2 | import Link from 'next/link' |
| 3 | import type { DragEvent, ReactNode } from 'react' |
| 4 | import { Button, cn } from 'ui' |
| 5 | import { Admonition } from 'ui-patterns/admonition' |
| 6 | import { CodeBlock, type CodeBlockLang } from 'ui-patterns/CodeBlock' |
| 7 | |
| 8 | import { ReportBlockContainer } from '@/components/interfaces/Reports/ReportBlock/ReportBlockContainer' |
| 9 | |
| 10 | interface EdgeFunctionBlockProps { |
| 11 | /** Title of the EdgeFunctionBlock */ |
| 12 | label: string |
| 13 | /** Function code to display */ |
| 14 | code: string |
| 15 | /** Function name/slug */ |
| 16 | functionName: string |
| 17 | /** Any other actions specific to the parent to be rendered in the header */ |
| 18 | actions?: ReactNode |
| 19 | /** Toggle visiblity of code on render */ |
| 20 | showCode?: boolean |
| 21 | /** Whether function block is draggable */ |
| 22 | draggable?: boolean |
| 23 | /** Tooltip when hovering over the header of the block */ |
| 24 | tooltip?: ReactNode |
| 25 | /** Optional callback on drag start */ |
| 26 | onDragStart?: (e: DragEvent) => void |
| 27 | /** Hide the header deploy button (used when an external confirm footer is shown) */ |
| 28 | hideDeployButton?: boolean |
| 29 | /** Disable interactive actions */ |
| 30 | disabled?: boolean |
| 31 | /** Whether a deploy action is currently running */ |
| 32 | isDeploying?: boolean |
| 33 | /** Whether a deploy action has completed */ |
| 34 | isDeployed?: boolean |
| 35 | /** Optional message to show when deployment fails */ |
| 36 | errorText?: string |
| 37 | /** URL to the deployed function */ |
| 38 | functionUrl?: string |
| 39 | /** Link to the function details page */ |
| 40 | deploymentDetailsUrl?: string |
| 41 | /** CLI command to download the function */ |
| 42 | downloadCommand?: string |
| 43 | /** Show warning UI when replacing an existing function */ |
| 44 | showReplaceWarning?: boolean |
| 45 | /** Cancel handler when replacing an existing function */ |
| 46 | onCancelReplace?: () => void |
| 47 | /** Confirm handler when replacing an existing function */ |
| 48 | onConfirmReplace?: () => void |
| 49 | /** Handler for triggering a deploy */ |
| 50 | onDeploy?: () => void |
| 51 | } |
| 52 | |
| 53 | export const EdgeFunctionBlock = ({ |
| 54 | label, |
| 55 | code, |
| 56 | functionName, |
| 57 | actions, |
| 58 | tooltip, |
| 59 | hideDeployButton = false, |
| 60 | disabled = false, |
| 61 | isDeploying = false, |
| 62 | isDeployed = false, |
| 63 | errorText, |
| 64 | functionUrl, |
| 65 | deploymentDetailsUrl, |
| 66 | downloadCommand, |
| 67 | showReplaceWarning = false, |
| 68 | onCancelReplace, |
| 69 | onConfirmReplace, |
| 70 | onDeploy, |
| 71 | draggable = false, |
| 72 | onDragStart, |
| 73 | }: EdgeFunctionBlockProps) => { |
| 74 | const resolvedFunctionUrl = functionUrl ?? 'Function URL will be available after deployment' |
| 75 | const resolvedDownloadCommand = downloadCommand ?? `briven functions download ${functionName}` |
| 76 | |
| 77 | const hasStatusMessage = isDeploying || isDeployed || !!errorText |
| 78 | |
| 79 | return ( |
| 80 | <ReportBlockContainer |
| 81 | tooltip={tooltip} |
| 82 | icon={<Code size={16} strokeWidth={1.5} className="text-foreground-muted" />} |
| 83 | label={label} |
| 84 | loading={isDeploying} |
| 85 | draggable={draggable} |
| 86 | onDragStart={onDragStart} |
| 87 | actions={ |
| 88 | hideDeployButton || !onDeploy ? ( |
| 89 | (actions ?? null) |
| 90 | ) : ( |
| 91 | <> |
| 92 | <Button |
| 93 | type="outline" |
| 94 | size="tiny" |
| 95 | loading={isDeploying} |
| 96 | disabled={disabled || isDeploying} |
| 97 | onClick={onDeploy} |
| 98 | > |
| 99 | {isDeploying ? 'Deploying...' : 'Deploy'} |
| 100 | </Button> |
| 101 | |
| 102 | {actions} |
| 103 | </> |
| 104 | ) |
| 105 | } |
| 106 | > |
| 107 | {showReplaceWarning && ( |
| 108 | <Admonition |
| 109 | type="warning" |
| 110 | className="rounded-none border-0 border-b shrink-0 bg-background-100" |
| 111 | > |
| 112 | <p>An edge function with the name "{functionName}" already exists.</p> |
| 113 | <p className="text-foreground-light"> |
| 114 | Deploying will replace the existing function. Are you sure you want to proceed? |
| 115 | </p> |
| 116 | <div className="flex justify-stretch mt-2 gap-2"> |
| 117 | <Button |
| 118 | type="outline" |
| 119 | size="tiny" |
| 120 | className="w-full flex-1" |
| 121 | disabled={isDeploying} |
| 122 | onClick={onCancelReplace} |
| 123 | > |
| 124 | Cancel |
| 125 | </Button> |
| 126 | <Button |
| 127 | type="danger" |
| 128 | size="tiny" |
| 129 | className="w-full flex-1" |
| 130 | loading={isDeploying} |
| 131 | disabled={isDeploying} |
| 132 | onClick={onConfirmReplace} |
| 133 | > |
| 134 | Replace function |
| 135 | </Button> |
| 136 | </div> |
| 137 | </Admonition> |
| 138 | )} |
| 139 | |
| 140 | <div className="shrink-0 w-full max-h-96 overflow-y-auto"> |
| 141 | <CodeBlock |
| 142 | hideLineNumbers |
| 143 | wrapLines={false} |
| 144 | value={code} |
| 145 | language={'typescript' as CodeBlockLang} |
| 146 | className={cn( |
| 147 | 'max-w-none block bg-transparent! py-3! px-3.5! prose dark:prose-dark border-0 text-foreground rounded-none! w-full', |
| 148 | '[&>code]:m-0 [&>code>span]:text-foreground' |
| 149 | )} |
| 150 | /> |
| 151 | </div> |
| 152 | |
| 153 | {hasStatusMessage && ( |
| 154 | <div className="p-4 w-full border-t bg-surface-75 text-xs"> |
| 155 | {isDeploying ? ( |
| 156 | <p className="text-foreground-light">Deploying function...</p> |
| 157 | ) : errorText ? ( |
| 158 | <p className="text-danger">{errorText}</p> |
| 159 | ) : ( |
| 160 | <> |
| 161 | <p className="text-foreground-light mb-2"> |
| 162 | The{' '} |
| 163 | {deploymentDetailsUrl ? ( |
| 164 | <Link className="text-foreground" href={deploymentDetailsUrl}> |
| 165 | new function |
| 166 | </Link> |
| 167 | ) : ( |
| 168 | <span className="text-foreground">new function</span> |
| 169 | )}{' '} |
| 170 | is now live at: |
| 171 | </p> |
| 172 | <CodeBlock |
| 173 | language="bash" |
| 174 | hideLineNumbers |
| 175 | value={resolvedFunctionUrl} |
| 176 | className="text-xs p-2" |
| 177 | /> |
| 178 | <p className="text-foreground-light mt-4 mb-2"> |
| 179 | To download and work on this function locally, use the CLI command: |
| 180 | </p> |
| 181 | <CodeBlock |
| 182 | hideLineNumbers |
| 183 | language="bash" |
| 184 | value={resolvedDownloadCommand} |
| 185 | className="text-xs p-2" |
| 186 | /> |
| 187 | </> |
| 188 | )} |
| 189 | </div> |
| 190 | )} |
| 191 | </ReportBlockContainer> |
| 192 | ) |
| 193 | } |