@@ -11,19 +11,24 @@ import {
11
11
PackagePlus ,
12
12
Pencil ,
13
13
Trash2 ,
14
+ Upload ,
14
15
} from 'lucide-react'
15
16
import Link from 'next/link'
16
17
import { useParams , useRouter } from 'next/navigation'
17
18
import { useState } from 'react'
18
19
import { Button } from '~/components/ui/button'
20
+ import { Dialog , DialogContent , DialogHeader , DialogTitle } from '~/components/ui/dialog'
19
21
import { Popover , PopoverContent , PopoverTrigger } from '~/components/ui/popover'
20
22
import { Tooltip , TooltipContent , TooltipTrigger } from '~/components/ui/tooltip'
21
23
import { useDatabaseDeleteMutation } from '~/data/databases/database-delete-mutation'
22
24
import { useDatabaseUpdateMutation } from '~/data/databases/database-update-mutation'
23
25
import { useDatabasesQuery } from '~/data/databases/databases-query'
26
+ import { usePublishWaitlistCreateMutation } from '~/data/publish-waitlist/publish-waitlist-create-mutation'
27
+ import { useIsOnPublishWaitlistQuery } from '~/data/publish-waitlist/publish-waitlist-query'
24
28
import { Database } from '~/lib/db'
25
29
import { cn } from '~/lib/utils'
26
30
import { useApp } from './app-provider'
31
+ import { CodeBlock } from './code-block'
27
32
28
33
export default function Sidebar ( ) {
29
34
const { user, signOut } = useApp ( )
@@ -203,117 +208,192 @@ type DatabaseMenuItemProps = {
203
208
204
209
function DatabaseMenuItem ( { database, isActive } : DatabaseMenuItemProps ) {
205
210
const router = useRouter ( )
211
+ const { user } = useApp ( )
206
212
const [ isPopoverOpen , setIsPopoverOpen ] = useState ( false )
207
213
const { mutateAsync : deleteDatabase } = useDatabaseDeleteMutation ( )
208
214
const { mutateAsync : updateDatabase } = useDatabaseUpdateMutation ( )
209
215
210
216
const [ isRenaming , setIsRenaming ] = useState ( false )
217
+ const [ isPublishDialogOpen , setIsPublishDialogOpen ] = useState ( false )
218
+
219
+ const { data : isOnPublishWaitlist } = useIsOnPublishWaitlistQuery ( )
220
+ const { mutateAsync : joinPublishWaitlist } = usePublishWaitlistCreateMutation ( )
211
221
212
222
return (
213
- < Link
214
- data-active = { isActive || isPopoverOpen }
215
- className = { cn (
216
- 'group text-sm w-full relative bg-inherit justify-start bg-neutral-100 hover:bg-neutral-200 flex gap-3 p-3 rounded-md overflow-hidden data-[active=true]:bg-neutral-200'
217
- ) }
218
- href = { `/db/${ database . id } ` }
219
- >
220
- < span className = "text-nowrap" > { database . name ?? 'My database' } </ span >
221
- < div
222
- className = { cn (
223
- 'absolute right-0 top-0 bottom-0' ,
224
- 'w-8 bg-gradient-to-l from-neutral-100 from-0%' ,
225
- 'group-hover:w-16 group-hover:from-neutral-200 group-hover:from-50%' ,
226
- 'group-data-[active=true]:w-16 group-data-[active=true]:from-neutral-200 group-data-[active=true]:from-50%'
227
- ) }
228
- />
229
- < Popover
223
+ < >
224
+ < Dialog
225
+ open = { isPublishDialogOpen }
230
226
onOpenChange = { ( open ) => {
231
- setIsPopoverOpen ( open )
232
- if ( ! open ) {
233
- setIsRenaming ( false )
234
- }
227
+ setIsPublishDialogOpen ( open )
235
228
} }
236
- open = { isPopoverOpen }
237
229
>
238
- < PopoverTrigger
239
- asChild
240
- onClick = { ( e ) => {
241
- e . preventDefault ( )
242
- setIsPopoverOpen ( true )
230
+ < DialogContent className = "max-w-2xl" >
231
+ < DialogHeader >
232
+ < DialogTitle > Database publishing is in Private Alpha</ DialogTitle >
233
+ < div className = "py-2 border-b" />
234
+ </ DialogHeader >
235
+ < h2 className = "font-medium" > What is database publishing?</ h2 >
236
+ < p >
237
+ Publish your database so that it can be accessed outside the browser using any Postgres
238
+ client:
239
+ </ p >
240
+ < CodeBlock
241
+ className = "language-curl bg-neutral-800"
242
+ language = "curl"
243
+ hideLineNumbers
244
+ theme = "dark"
245
+ >
246
+ { `psql "postgres://postgres:<password>@<your-unique-server>/postgres"` }
247
+ </ CodeBlock >
248
+ < div className = "flex justify-center items-center mt-3" >
249
+ < AnimatePresence initial = { false } >
250
+ { ! isOnPublishWaitlist ? (
251
+ < button
252
+ className = "px-4 py-3 bg-black text-white rounded-md"
253
+ onClick = { async ( ) => {
254
+ await joinPublishWaitlist ( )
255
+ } }
256
+ >
257
+ Join Private Alpha
258
+ </ button >
259
+ ) : (
260
+ < m . div
261
+ className = "px-4 py-3 border-2 rounded-md text-center border-dashed"
262
+ variants = { {
263
+ hidden : { scale : 0 } ,
264
+ show : { scale : 1 } ,
265
+ } }
266
+ initial = "hidden"
267
+ animate = "show"
268
+ >
269
+ < h3 className = "font-medium mb-2" > 🎉 You're on the list!</ h3 >
270
+ < p > We'll let you know when you have access to publish.</ p >
271
+ </ m . div >
272
+ ) }
273
+ </ AnimatePresence >
274
+ </ div >
275
+ </ DialogContent >
276
+ </ Dialog >
277
+
278
+ < Link
279
+ data-active = { isActive || isPopoverOpen }
280
+ className = { cn (
281
+ 'group text-sm w-full relative bg-inherit justify-start bg-neutral-100 hover:bg-neutral-200 flex gap-3 p-3 rounded-md overflow-hidden data-[active=true]:bg-neutral-200'
282
+ ) }
283
+ href = { `/db/${ database . id } ` }
284
+ >
285
+ < span className = "text-nowrap" > { database . name ?? 'My database' } </ span >
286
+ < div
287
+ className = { cn (
288
+ 'absolute right-0 top-0 bottom-0' ,
289
+ 'w-8 bg-gradient-to-l from-neutral-100 from-0%' ,
290
+ 'group-hover:w-16 group-hover:from-neutral-200 group-hover:from-50%' ,
291
+ 'group-data-[active=true]:w-16 group-data-[active=true]:from-neutral-200 group-data-[active=true]:from-50%'
292
+ ) }
293
+ />
294
+ < Popover
295
+ onOpenChange = { ( open ) => {
296
+ setIsPopoverOpen ( open )
297
+ if ( ! open ) {
298
+ setIsRenaming ( false )
299
+ }
243
300
} }
301
+ open = { isPopoverOpen }
244
302
>
245
- < div
246
- className = { cn (
247
- 'hidden group-hover:flex absolute right-0 top-0 bottom-0 p-2 opacity-50 items-center' ,
248
- isActive || isPopoverOpen ? 'flex' : undefined
249
- ) }
303
+ < PopoverTrigger
304
+ asChild
305
+ onClick = { ( e ) => {
306
+ e . preventDefault ( )
307
+ setIsPopoverOpen ( true )
308
+ } }
250
309
>
251
- < CircleEllipsis size = { 24 } />
252
- </ div >
253
- </ PopoverTrigger >
310
+ < div
311
+ className = { cn (
312
+ 'hidden group-hover:flex absolute right-0 top-0 bottom-0 p-2 opacity-50 items-center' ,
313
+ isActive || isPopoverOpen ? 'flex' : undefined
314
+ ) }
315
+ >
316
+ < CircleEllipsis size = { 24 } />
317
+ </ div >
318
+ </ PopoverTrigger >
254
319
255
- < PopoverContent className = "p-2 flex flex-col overflow-hidden w-auto" portal >
256
- { isRenaming ? (
257
- < form
258
- className = "w-72"
259
- onSubmit = { async ( e ) => {
260
- e . preventDefault ( )
320
+ < PopoverContent className = "p-2 flex flex-col overflow-hidden w-auto" portal >
321
+ { isRenaming ? (
322
+ < form
323
+ className = "w-72"
324
+ onSubmit = { async ( e ) => {
325
+ e . preventDefault ( )
261
326
262
- if ( e . target instanceof HTMLFormElement ) {
263
- const formData = new FormData ( e . target )
264
- const name = formData . get ( 'name' )
327
+ if ( e . target instanceof HTMLFormElement ) {
328
+ const formData = new FormData ( e . target )
329
+ const name = formData . get ( 'name' )
265
330
266
- if ( typeof name === 'string' ) {
267
- await updateDatabase ( { ...database , name } )
331
+ if ( typeof name === 'string' ) {
332
+ await updateDatabase ( { ...database , name } )
333
+ }
268
334
}
269
- }
270
335
271
- setIsPopoverOpen ( false )
272
- setIsRenaming ( false )
273
- } }
274
- >
275
- < input
276
- name = "name"
277
- className = "flex-grow w-full border-none focus-visible:ring-0 text-base bg-inherit placeholder:text-neutral-400"
278
- placeholder = { `Rename ${ database . name } ` }
279
- defaultValue = { database . name ?? undefined }
280
- autoComplete = "off"
281
- autoFocus
282
- />
283
- </ form >
284
- ) : (
285
- < div className = "flex flex-col items-stretch w-32" >
286
- < Button
287
- className = "bg-inherit justify-start hover:bg-neutral-200 flex gap-3"
288
- onClick = { async ( e ) => {
289
- e . preventDefault ( )
290
- setIsRenaming ( true )
336
+ setIsPopoverOpen ( false )
337
+ setIsRenaming ( false )
291
338
} }
292
339
>
293
- < Pencil size = { 16 } strokeWidth = { 2 } className = "flex-shrink-0" />
340
+ < input
341
+ name = "name"
342
+ className = "flex-grow w-full border-none focus-visible:ring-0 text-base bg-inherit placeholder:text-neutral-400"
343
+ placeholder = { `Rename ${ database . name } ` }
344
+ defaultValue = { database . name ?? undefined }
345
+ autoComplete = "off"
346
+ autoFocus
347
+ />
348
+ </ form >
349
+ ) : (
350
+ < div className = "flex flex-col items-stretch w-32" >
351
+ < Button
352
+ className = "bg-inherit justify-start hover:bg-neutral-200 flex gap-3"
353
+ onClick = { async ( e ) => {
354
+ e . preventDefault ( )
355
+ setIsRenaming ( true )
356
+ } }
357
+ >
358
+ < Pencil size = { 16 } strokeWidth = { 2 } className = "flex-shrink-0" />
294
359
295
- < span > Rename</ span >
296
- </ Button >
297
- < Button
298
- className = "bg-inherit text-destructive-600 justify-start hover:bg-neutral-200 flex gap-3"
299
- onClick = { async ( e ) => {
300
- e . preventDefault ( )
301
- setIsPopoverOpen ( false )
302
- await deleteDatabase ( { id : database . id } )
360
+ < span > Rename</ span >
361
+ </ Button >
362
+ < Button
363
+ className = "bg-inherit justify-start hover:bg-neutral-200 flex gap-3"
364
+ onClick = { async ( e ) => {
365
+ e . preventDefault ( )
303
366
304
- if ( isActive ) {
305
- router . push ( '/' )
306
- }
307
- } }
308
- >
309
- < Trash2 size = { 16 } strokeWidth = { 2 } className = "flex-shrink-0" />
367
+ setIsPublishDialogOpen ( true )
368
+ setIsPopoverOpen ( false )
369
+ } }
370
+ disabled = { user === undefined }
371
+ >
372
+ < Upload size = { 16 } strokeWidth = { 2 } className = "flex-shrink-0" />
310
373
311
- < span > Delete</ span >
312
- </ Button >
313
- </ div >
314
- ) }
315
- </ PopoverContent >
316
- </ Popover >
317
- </ Link >
374
+ < span > Publish</ span >
375
+ </ Button >
376
+ < Button
377
+ className = "bg-inherit text-destructive-600 justify-start hover:bg-neutral-200 flex gap-3"
378
+ onClick = { async ( e ) => {
379
+ e . preventDefault ( )
380
+ setIsPopoverOpen ( false )
381
+ await deleteDatabase ( { id : database . id } )
382
+
383
+ if ( isActive ) {
384
+ router . push ( '/' )
385
+ }
386
+ } }
387
+ >
388
+ < Trash2 size = { 16 } strokeWidth = { 2 } className = "flex-shrink-0" />
389
+
390
+ < span > Delete</ span >
391
+ </ Button >
392
+ </ div >
393
+ ) }
394
+ </ PopoverContent >
395
+ </ Popover >
396
+ </ Link >
397
+ </ >
318
398
)
319
399
}
0 commit comments