11"use client"
2- import { Plus } from "lucide-react"
2+ import { Plus , Edit } from "lucide-react"
33import Link from "next/link"
44import { useRouter } from "next/navigation"
5+ import { useState } from "react"
6+ import { z } from "zod"
7+ import { zodResolver } from "@hookform/resolvers/zod"
8+ import { useForm } from "react-hook-form"
59
610import { Button } from "@/components/ui/button"
11+ import {
12+ Dialog ,
13+ DialogContent ,
14+ DialogDescription ,
15+ DialogHeader ,
16+ DialogTitle ,
17+ } from "@/components/ui/dialog"
18+ import {
19+ Form ,
20+ FormControl ,
21+ FormDescription ,
22+ FormField ,
23+ FormItem ,
24+ FormLabel ,
25+ FormMessage ,
26+ } from "@/components/ui/form"
27+ import { Input } from "@/components/ui/input"
728
829import { createClient } from "@/lib/supabase/client"
930import { Database } from "../../database.types"
1031
32+ type UpsertServerData = {
33+ id ?: string // Omitted if we're adding a new row
34+ name : string
35+ }
36+
1137export function ServerCard ( {
1238 server,
1339} : {
1440 server : Database [ "public" ] [ "Tables" ] [ "servers" ] [ "Row" ]
1541} ) {
1642 const supabase = createClient ( )
1743 const router = useRouter ( )
44+ const [ editingServer , setEditingServer ] = useState < UpsertServerData > ( )
1845
1946 const handleDelete = async ( serverId : string ) => {
2047 await supabase . from ( "servers" ) . delete ( ) . eq ( "id" , serverId )
2148 router . refresh ( )
2249 }
2350
51+ const handleUpsertServer = async ( data : UpsertServerData ) => {
52+ await supabase . from ( "servers" ) . upsert ( { ...data } )
53+ router . refresh ( )
54+ setEditingServer ( undefined )
55+ }
56+
57+ const handleRename = ( ) => {
58+ setEditingServer ( {
59+ id : server . id ,
60+ name : server . name ,
61+ } )
62+ }
63+
2464 return (
2565 < div className = "bg-card border rounded-md px-6 py-6 flex items-center" >
66+ { editingServer && (
67+ < ServerForm
68+ defaultValues = { editingServer }
69+ onServerChange = { ( values ) => {
70+ handleUpsertServer ( { id : editingServer . id , ...values } )
71+ } }
72+ onOpenChange = { ( open ) => {
73+ if ( ! open ) {
74+ setEditingServer ( undefined )
75+ }
76+ } }
77+ />
78+ ) }
79+
2680 < span className = "grow" > { server . name } </ span >
2781 < div className = "flex items-center gap-2" >
82+ < Button variant = "ghost" size = "sm" onClick = { handleRename } >
83+ < Edit className = "h-4 w-4" />
84+ </ Button >
2885 < Button variant = "outline" asChild >
29- < Link href = { `/${ server . id } ` } > Edit </ Link >
86+ < Link href = { `/${ server . id } ` } > Manage </ Link >
3087 </ Button >
3188 < Button variant = "destructive" onClick = { ( ) => handleDelete ( server . id ) } >
3289 Delete
@@ -39,18 +96,106 @@ export function ServerCard({
3996export function AddServerButton ( ) {
4097 const supabase = createClient ( )
4198 const router = useRouter ( )
99+ const [ editingServer , setEditingServer ] = useState < UpsertServerData > ( )
42100
43- const handleAddServer = async ( ) => {
44- await supabase . from ( "servers" ) . insert ( {
45- name : "New Server" ,
46- } )
101+ const handleUpsertServer = async ( data : UpsertServerData ) => {
102+ await supabase . from ( "servers" ) . insert ( { name : data . name } )
47103 router . refresh ( )
104+ setEditingServer ( undefined )
105+ }
106+
107+ const handleAddServer = ( ) => {
108+ setEditingServer ( {
109+ name : "" ,
110+ } )
48111 }
49112
50113 return (
51- < Button className = "flex items-center gap-2" onClick = { handleAddServer } >
52- < Plus />
53- Add Server
54- </ Button >
114+ < >
115+ { editingServer && (
116+ < ServerForm
117+ defaultValues = { editingServer }
118+ onServerChange = { ( values ) => {
119+ handleUpsertServer ( values )
120+ } }
121+ onOpenChange = { ( open ) => {
122+ if ( ! open ) {
123+ setEditingServer ( undefined )
124+ }
125+ } }
126+ />
127+ ) }
128+
129+ < Button className = "flex items-center gap-2" onClick = { handleAddServer } >
130+ < Plus />
131+ Add Server
132+ </ Button >
133+ </ >
134+ )
135+ }
136+
137+ const serverFormSchema = z . object ( {
138+ name : z . string ( ) . min ( 1 , {
139+ message : "Name must be at least 1 character." ,
140+ } ) ,
141+ } )
142+
143+ export function ServerForm ( {
144+ defaultValues,
145+ onServerChange,
146+ onOpenChange,
147+ } : {
148+ defaultValues : UpsertServerData
149+ onServerChange ?: ( values : z . infer < typeof serverFormSchema > ) => void
150+ onOpenChange ?: ( open : boolean ) => void
151+ } ) {
152+ const form = useForm < z . infer < typeof serverFormSchema > > ( {
153+ resolver : zodResolver ( serverFormSchema ) ,
154+ defaultValues,
155+ } )
156+
157+ const onSubmit = ( values : z . infer < typeof serverFormSchema > ) => {
158+ onServerChange ?.( values )
159+ }
160+
161+ return (
162+ < Dialog open onOpenChange = { onOpenChange } >
163+ < DialogContent >
164+ < DialogHeader >
165+ < DialogTitle >
166+ { defaultValues . id !== undefined ? "Rename server" : "Add a server" }
167+ </ DialogTitle >
168+ < DialogDescription >
169+ { defaultValues . id !== undefined
170+ ? "Update the server name."
171+ : "Create a new server with a custom name." }
172+ </ DialogDescription >
173+ </ DialogHeader >
174+
175+ < Form { ...form } >
176+ < form onSubmit = { form . handleSubmit ( onSubmit ) } className = "space-y-8" >
177+ < FormField
178+ control = { form . control }
179+ name = "name"
180+ render = { ( { field } ) => (
181+ < FormItem >
182+ < FormLabel > Name</ FormLabel >
183+ < FormControl >
184+ < Input placeholder = "My Server" { ...field } />
185+ </ FormControl >
186+ < FormDescription >
187+ A descriptive name for your server.
188+ </ FormDescription >
189+ < FormMessage />
190+ </ FormItem >
191+ ) }
192+ />
193+ < Button type = "submit" >
194+ { defaultValues . id !== undefined ? "Update" : "Create" }
195+ </ Button >
196+ </ form >
197+ </ Form >
198+ </ DialogContent >
199+ </ Dialog >
55200 )
56201}
0 commit comments