1
1
"use client" ;
2
2
3
- import { useState , useEffect } from "react" ;
3
+ import { useState } from "react" ;
4
4
import { useForm } from "react-hook-form" ;
5
5
import { zodResolver } from "@hookform/resolvers/zod" ;
6
6
import { z } from "zod" ;
7
- import { Plus , X , Eye , UserX } from "lucide-react" ;
7
+ import {
8
+ Plus ,
9
+ X ,
10
+ Eye ,
11
+ UserX ,
12
+ ChartLine ,
13
+ ListOrdered ,
14
+ CircleUser ,
15
+ } from "lucide-react" ;
8
16
import { Button } from "@/components/ui/button" ;
9
17
import { Input } from "@/components/ui/input" ;
10
18
import { Label } from "@/components/ui/label" ;
11
19
import { RadioGroup , RadioGroupItem } from "@/components/ui/radio-group" ;
12
20
import { useToast } from "@/hooks/use-toast" ;
13
- import { useAuth } from "@/hooks/useAuth" ;
14
- import { isUnauthorizedError } from "@/lib/authUtils" ;
15
21
import Link from "next/link" ;
16
22
17
23
const createPollSchema = z . object ( {
18
24
title : z . string ( ) . min ( 1 , "Poll title is required" ) ,
19
- mode : z . enum ( [ "public" , "private" ] ) ,
25
+ mode : z . enum ( [ "normal" , "point" , "rank" ] ) ,
26
+ visibility : z . enum ( [ "public" , "private" ] ) ,
20
27
options : z
21
28
. array ( z . string ( ) . min ( 1 , "Option cannot be empty" ) )
22
29
. min ( 2 , "At least 2 options required" ) ,
@@ -26,32 +33,16 @@ const createPollSchema = z.object({
26
33
. refine ( ( val ) => {
27
34
if ( ! val ) return true ; // Allow empty deadline
28
35
const date = new Date ( val ) ;
29
- return ! isNaN ( date . getTime ( ) ) && date > new Date ( ) ;
36
+ return ! Number . isNaN ( date . getTime ( ) ) && date > new Date ( ) ;
30
37
} , "Deadline must be a valid future date" ) ,
31
38
} ) ;
32
39
33
40
type CreatePollForm = z . infer < typeof createPollSchema > ;
34
41
35
42
export default function CreatePoll ( ) {
36
43
const { toast } = useToast ( ) ;
37
- const { isAuthenticated, isLoading : authLoading } = useAuth ( ) ;
38
44
const [ options , setOptions ] = useState < string [ ] > ( [ "" , "" ] ) ;
39
45
40
- // TODO: Redirect to login if not authenticated
41
- // useEffect(() => {
42
- // if (!authLoading && !isAuthenticated) {
43
- // toast({
44
- // title: "Unauthorized",
45
- // description: "You are logged out. Logging in again...",
46
- // variant: "destructive",
47
- // });
48
- // setTimeout(() => {
49
- // window.location.href = "/api/login";
50
- // }, 500);
51
- // return;
52
- // }
53
- // }, [isAuthenticated, authLoading, toast]);
54
-
55
46
const {
56
47
register,
57
48
handleSubmit,
@@ -62,13 +53,20 @@ export default function CreatePoll() {
62
53
resolver : zodResolver ( createPollSchema ) ,
63
54
defaultValues : {
64
55
title : "" ,
65
- mode : "public" ,
56
+ mode : "normal" ,
57
+ visibility : "public" ,
66
58
options : [ "" , "" ] ,
67
59
deadline : "" ,
68
60
} ,
69
61
} ) ;
70
62
63
+ handleSubmit ( ( data ) => {
64
+ console . log ( "Form submitted:" , data ) ;
65
+ console . log ( data ) ;
66
+ } ) ;
67
+
71
68
const watchedMode = watch ( "mode" ) ;
69
+ const watchedVisibility = watch ( "visibility" ) ;
72
70
73
71
const addOption = ( ) => {
74
72
const newOptions = [ ...options , "" ] ;
@@ -132,7 +130,107 @@ export default function CreatePoll() {
132
130
< RadioGroup
133
131
value = { watchedMode }
134
132
onValueChange = { ( value ) =>
135
- setValue ( "mode" , value as "public" | "private" )
133
+ setValue (
134
+ "mode" ,
135
+ value as "normal" | "point" | "rank"
136
+ )
137
+ }
138
+ className = "mt-2"
139
+ >
140
+ < div className = "flex items-center space-x-4" >
141
+ < Label className = "flex items-center cursor-pointer" >
142
+ < RadioGroupItem
143
+ value = "normal"
144
+ className = "sr-only"
145
+ />
146
+ < div
147
+ className = { `border-2 rounded-lg p-4 flex-1 transition-all ${
148
+ watchedMode === "normal"
149
+ ? "border-(--crimson) bg-(--crimson-50)"
150
+ : "border-gray-300 hover:border-(--crimson)"
151
+ } `}
152
+ >
153
+ < div className = "flex items-center" >
154
+ < CircleUser className = "text-(--crimson) w-6 h-6 mr-3" />
155
+ < div >
156
+ < div className = "font-semibold text-gray-900" >
157
+ 1P 1V
158
+ </ div >
159
+ < div className = "text-sm text-gray-600" >
160
+ One person, one vote
161
+ </ div >
162
+ </ div >
163
+ </ div >
164
+ </ div >
165
+ </ Label >
166
+
167
+ < Label className = "flex items-center cursor-pointer" >
168
+ < RadioGroupItem
169
+ value = "point"
170
+ className = "sr-only"
171
+ />
172
+ < div
173
+ className = { `border-2 rounded-lg p-4 flex-1 transition-all ${
174
+ watchedMode === "point"
175
+ ? "border-(--crimson) bg-(--crimson-50)"
176
+ : "border-gray-300 hover:border-(--crimson)"
177
+ } `}
178
+ >
179
+ < div className = "flex items-center" >
180
+ < ChartLine className = "text-(--crimson) w-6 h-6 mr-3" />
181
+ < div >
182
+ < div className = "font-semibold text-gray-900" >
183
+ PBV
184
+ </ div >
185
+ < div className = "text-sm text-gray-600" >
186
+ Each voter gets 100 points
187
+ </ div >
188
+ </ div >
189
+ </ div >
190
+ </ div >
191
+ </ Label >
192
+
193
+ < Label className = "flex items-center cursor-pointer" >
194
+ < RadioGroupItem
195
+ value = "rank"
196
+ className = "sr-only"
197
+ />
198
+ < div
199
+ className = { `border-2 rounded-lg p-4 flex-1 transition-all ${
200
+ watchedMode === "rank"
201
+ ? "border-(--crimson) bg-(--crimson-50)"
202
+ : "border-gray-300 hover:border-(--crimson)"
203
+ } `}
204
+ >
205
+ < div className = "flex items-center" >
206
+ < ListOrdered className = "text-(--crimson) w-6 h-6 mr-3" />
207
+ < div >
208
+ < div className = "font-semibold text-gray-900" >
209
+ RBV
210
+ </ div >
211
+ < div className = "text-sm text-gray-600" >
212
+ Voters can rank order the
213
+ choices
214
+ </ div >
215
+ </ div >
216
+ </ div >
217
+ </ div >
218
+ </ Label >
219
+ </ div >
220
+ </ RadioGroup >
221
+ </ div >
222
+
223
+ < div >
224
+ < Label className = "text-sm font-semibold text-gray-700" >
225
+ Vote Visibility
226
+ </ Label >
227
+ < RadioGroup
228
+ value = { watchedVisibility }
229
+ onValueChange = { ( value ) =>
230
+ setValue (
231
+ "visibility" ,
232
+ value as "public" | "private"
233
+ )
136
234
}
137
235
className = "mt-2"
138
236
>
@@ -144,7 +242,7 @@ export default function CreatePoll() {
144
242
/>
145
243
< div
146
244
className = { `border-2 rounded-lg p-4 flex-1 transition-all ${
147
- watchedMode === "public"
245
+ watchedVisibility === "public"
148
246
? "border-(--crimson) bg-(--crimson-50)"
149
247
: "border-gray-300 hover:border-(--crimson)"
150
248
} `}
@@ -170,7 +268,7 @@ export default function CreatePoll() {
170
268
/>
171
269
< div
172
270
className = { `border-2 rounded-lg p-4 flex-1 transition-all ${
173
- watchedMode === "private"
271
+ watchedVisibility === "private"
174
272
? "border-(--crimson) bg-(--crimson-50)"
175
273
: "border-gray-300 hover:border-(--crimson)"
176
274
} `}
@@ -224,6 +322,7 @@ export default function CreatePoll() {
224
322
< div className = "mt-2 space-y-3" >
225
323
{ options . map ( ( option , index ) => (
226
324
< div
325
+ // biome-ignore lint/suspicious/noArrayIndexKey: jatt dont care OOOOOOOOOO
227
326
key = { index }
228
327
className = "flex items-center space-x-2"
229
328
>
@@ -278,21 +377,8 @@ export default function CreatePoll() {
278
377
</ Link >
279
378
< Button
280
379
type = "submit"
281
- // disabled={createPollMutation.isPending}
282
- disabled = { false } // TODO: replace with actual loading state
283
380
className = "flex-1 bg-(--crimson) hover:bg-(--crimson-50) hover:text-(--crimson) hover:border-(--crimson) border text-white"
284
381
>
285
- { /* {createPollMutation.isPending ? (
286
- <>
287
- <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
288
- Creating...
289
- </>
290
- ) : (
291
- <>
292
- <Plus className="w-4 h-4 mr-2" />
293
- Create Vote
294
- </>
295
- )} */ }
296
382
Create Vote
297
383
</ Button >
298
384
</ div >
0 commit comments