EdgeFunctionBlock.tsx193 lines · main
1import { Code } from 'lucide-react'
2import Link from 'next/link'
3import type { DragEvent, ReactNode } from 'react'
4import { Button, cn } from 'ui'
5import { Admonition } from 'ui-patterns/admonition'
6import { CodeBlock, type CodeBlockLang } from 'ui-patterns/CodeBlock'
7
8import { ReportBlockContainer } from '@/components/interfaces/Reports/ReportBlock/ReportBlockContainer'
9
10interface 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
53export 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}