@@ -3,14 +3,22 @@ import {
3
3
type LoaderFunctionArgs ,
4
4
redirect ,
5
5
} from "@remix-run/node" ;
6
- import { Form , Link , useLoaderData , useNavigation } from "@remix-run/react" ;
6
+ import {
7
+ Form ,
8
+ Link ,
9
+ useFetcher ,
10
+ useLoaderData ,
11
+ useNavigation ,
12
+ } from "@remix-run/react" ;
7
13
8
14
import { requireAuthCookie } from "~/auth/auth" ;
9
15
import { Button } from "~/components/button" ;
10
16
import { Label , LabeledInput } from "~/components/input" ;
11
17
import { badRequest } from "~/http/bad-response" ;
12
18
13
- import { getHomeData , createBoard } from "./queries" ;
19
+ import { getHomeData , createBoard , deleteBoard } from "./queries" ;
20
+ import { INTENTS } from "../board.$id/types" ;
21
+ import { Icon } from "~/icons/icons" ;
14
22
15
23
export const meta = ( ) => {
16
24
return [ { title : "Boards" } ] ;
@@ -25,11 +33,21 @@ export async function loader({ request }: LoaderFunctionArgs) {
25
33
export async function action ( { request } : ActionFunctionArgs ) {
26
34
let userId = await requireAuthCookie ( request ) ;
27
35
let formData = await request . formData ( ) ;
28
- let name = String ( formData . get ( "name" ) ) ;
29
- let color = String ( formData . get ( "color" ) ) ;
30
- if ( ! name ) throw badRequest ( "Bad request" ) ;
31
- let board = await createBoard ( userId , name , color ) ;
32
- throw redirect ( `/board/${ board . id } ` ) ;
36
+ let intent = String ( formData . get ( "intent" ) ) ;
37
+ switch ( intent ) {
38
+ case INTENTS . createBoard : {
39
+ let name = String ( formData . get ( "name" ) ) ;
40
+ let color = String ( formData . get ( "color" ) ) ;
41
+ if ( ! name ) throw badRequest ( "Bad request" ) ;
42
+ let board = await createBoard ( userId , name , color ) ;
43
+ return redirect ( `/board/${ board . id } ` ) ;
44
+ }
45
+ case INTENTS . deleteBoard : {
46
+ let boardId = Number ( formData . get ( "boardId" ) ) ;
47
+ await deleteBoard ( boardId ) ;
48
+ return { ok : true } ;
49
+ }
50
+ }
33
51
}
34
52
35
53
export default function Projects ( ) {
@@ -48,20 +66,54 @@ function Boards() {
48
66
< h2 className = "font-bold mb-2 text-xl" > Boards</ h2 >
49
67
< nav className = "flex flex-wrap gap-8" >
50
68
{ boards . map ( ( board ) => (
51
- < Link
69
+ < Board
52
70
key = { board . id }
53
- to = { `/board/${ board . id } ` }
54
- className = "w-60 h-40 p-4 block border-b-8 shadow rounded hover:shadow-lg hover:scale-105 transition-transform bg-white"
55
- style = { { borderColor : board . color } }
56
- >
57
- < div className = "font-bold" > { board . name } </ div >
58
- </ Link >
71
+ name = { board . name }
72
+ id = { board . id }
73
+ color = { board . color }
74
+ />
59
75
) ) }
60
76
</ nav >
61
77
</ div >
62
78
) ;
63
79
}
64
80
81
+ function Board ( {
82
+ name,
83
+ id,
84
+ color,
85
+ } : {
86
+ name : string ;
87
+ id : number ;
88
+ color : string ;
89
+ } ) {
90
+ let fetcher = useFetcher ( ) ;
91
+ let isDeleting = fetcher . state !== "idle" ;
92
+ return isDeleting ? null : (
93
+ < Link
94
+ to = { `/board/${ id } ` }
95
+ className = "w-60 h-40 p-4 block border-b-8 shadow rounded hover:shadow-lg bg-white relative"
96
+ style = { { borderColor : color } }
97
+ >
98
+ < div className = "font-bold" > { name } </ div >
99
+ < fetcher . Form method = "post" >
100
+ < input type = "hidden" name = "intent" value = { INTENTS . deleteBoard } />
101
+ < input type = "hidden" name = "boardId" value = { id } />
102
+ < button
103
+ aria-label = "Delete board"
104
+ className = "absolute top-4 right-4 hover:text-brand-red"
105
+ type = "submit"
106
+ onClick = { ( event ) => {
107
+ event . stopPropagation ( ) ;
108
+ } }
109
+ >
110
+ < Icon name = "trash" />
111
+ </ button >
112
+ </ fetcher . Form >
113
+ </ Link >
114
+ ) ;
115
+ }
116
+
65
117
function NewBoard ( ) {
66
118
let navigation = useNavigation ( ) ;
67
119
let isCreating = navigation . formData ?. get ( "intent" ) === "createBoard" ;
0 commit comments