2
2
import { useState , ChangeEvent , MouseEvent , FormEvent } from 'react' ;
3
3
import { QuestionBody , Difficulty , QuestionFullBody } from '@/api/structs' ;
4
4
import { addQuestion } from '@/api/gateway' ;
5
+ import style from "@/style/form.module.css" ;
6
+ import FormTextInput from '@/components/shared/form/FormTextInput' ;
7
+ import RadioButtonGroup from '@/components/shared/form/RadioButtonGroup' ;
8
+ import FormTextAreaInput from '@/components/shared/form/FormTextAreaInput' ;
9
+ import { useRouter } from 'next/navigation' ;
5
10
6
11
type Props = { }
7
12
@@ -11,40 +16,73 @@ interface Mapping {
11
16
}
12
17
13
18
function NewQuestion ( { } : Props ) {
14
- const [ testCases , setTestCases ] = useState < Mapping [ ] > ( [ {
15
- key : "" , value : ""
16
- } ] ) ;
19
+ const router = useRouter ( ) ;
20
+ // Form Data is handled as a single submission
17
21
const [ formData , setFormData ] = useState < QuestionBody > ( {
18
22
title : "" ,
19
23
difficulty : Difficulty . Easy ,
20
24
description : "" ,
25
+ categories : [ ]
26
+ } ) ;
27
+ // Choice 1: Test cases handled separately to allow modification of multiple fields
28
+ const [ testCases , setTestCases ] = useState < Mapping [ ] > ( [ ] ) ;
29
+ // TODO: Resolve this mess of hooks to combine the form data
30
+ const [ mapping , setMapping ] = useState < Mapping > ( {
31
+ key : "" , value : ""
21
32
} ) ;
33
+ // Choice 2: Categories handled in a separate state, inject into formData on confirm
34
+ const [ category , setCategory ] = useState < string > ( "" ) ;
35
+ const [ loading , setLoading ] = useState < boolean > ( false ) ;
36
+
37
+ const handleCategoriesInput = ( e : ChangeEvent < HTMLInputElement > ) => setCategory ( e . target . value ) ;
38
+ const handleCategoryAdd = ( e : MouseEvent < HTMLElement > ) => {
39
+ if ( category . length == 0 )
40
+ return ;
41
+ setFormData ( {
42
+ ...formData ,
43
+ categories : [ ...formData . categories , category ]
44
+ } )
45
+ setCategory ( "" ) ;
46
+ }
47
+ const handleCategoryDel = ( e : MouseEvent < HTMLParagraphElement > , idx : number ) => {
48
+ if ( loading )
49
+ return ;
50
+ const values = [ ...formData . categories ] ;
51
+ values . splice ( idx , 1 ) ;
52
+ setFormData ( {
53
+ ...formData ,
54
+ categories : values
55
+ } )
56
+ }
22
57
23
- const handleTextInput = ( e : ChangeEvent < HTMLInputElement | HTMLTextAreaElement > ) => setFormData ( {
58
+ const handleFormTextInput = ( e : ChangeEvent < HTMLInputElement | HTMLTextAreaElement > ) => setFormData ( {
24
59
...formData ,
25
60
[ e . target . name ] : e . target . value
26
61
} ) ;
27
62
28
- const handleTestCaseInput = ( e : ChangeEvent < HTMLInputElement > , idx : number ) => {
29
- const values = [ ...testCases ] ;
30
- values [ idx ] = {
31
- ...values [ idx ] ,
32
- [ e . target . name ] : e . target . value
33
- } ;
34
- setTestCases ( values ) ;
35
- }
63
+ const handleMappingInput = ( e : ChangeEvent < HTMLInputElement > ) => setMapping ( {
64
+ ...mapping ,
65
+ [ e . target . name ] : e . target . value
66
+ } ) ;
36
67
37
- const handleAddField = ( e : MouseEvent < HTMLElement > ) =>
38
- setTestCases ( [ ...testCases , { key : "" , value : "" } ] ) ;
68
+ const handleMappingAdd = ( e : MouseEvent < HTMLElement > ) => {
69
+ if ( mapping . key . length == 0 || mapping . value . length == 0 )
70
+ return ;
71
+ setTestCases ( [ ...testCases , mapping ] ) ;
72
+ setMapping ( { key : "" , value : "" } )
73
+ }
39
74
40
- const handleDeleteField = ( e : MouseEvent < HTMLElement > , idx : number ) => {
75
+ const handleMappingDel = ( e : MouseEvent < HTMLElement > , idx : number ) => {
76
+ if ( loading )
77
+ return ;
41
78
const values = [ ...testCases ] ;
42
79
values . splice ( idx , 1 ) ;
43
80
setTestCases ( values ) ;
44
81
}
45
82
46
83
const handleSubmission = async ( e : FormEvent < HTMLFormElement > ) => {
47
84
e . preventDefault ( ) ;
85
+ setLoading ( true ) ;
48
86
const question : QuestionFullBody = {
49
87
...formData ,
50
88
test_cases : testCases . map ( ( elem : Mapping ) => ( {
@@ -55,55 +93,60 @@ function NewQuestion({}: Props) {
55
93
if ( status . error ) {
56
94
console . log ( "Failed to add question." ) ;
57
95
console . log ( `Code ${ status . status } : ${ status . error } ` ) ;
96
+ setLoading ( false ) ;
58
97
return ;
59
98
}
60
99
console . log ( `Successfully added the question.` ) ;
100
+ router . push ( '/questions' ) ;
61
101
}
62
102
63
103
return (
64
- < div >
65
- < form style = { { color : "black" , padding : "5px" } } onSubmit = { handleSubmission } >
66
- < input type = "text" name = "title" value = { formData . title } onChange = { handleTextInput } /> < br />
67
- < input type = "radio" id = "easy" name = "difficulty" value = { 1 } onChange = { handleTextInput } />
68
- < label htmlFor = "easy" > Easy</ label > < br />
69
- < input type = "radio" id = "med" name = "difficulty" value = { 2 } onChange = { handleTextInput } />
70
- < label htmlFor = "med" > Medium</ label > < br />
71
- < input type = "radio" id = "hard" name = "difficulty" value = { 3 } onChange = { handleTextInput } />
72
- < label htmlFor = "hard" > Hard</ label > < br />
73
- < textarea name = "description" value = { formData . description } onChange = { handleTextInput } /> < br />
74
- { testCases . map ( ( elem , idx ) => (
75
- < >
76
- < input
77
- name = "key"
78
- type = "text"
79
- id = { `key_${ idx . toLocaleString ( ) } ` }
80
- value = { elem . key }
81
- onChange = { e => handleTestCaseInput ( e , idx ) } />
82
- < input
83
- name = "value"
84
- type = "text"
85
- id = { `val_${ idx . toLocaleString ( ) } ` }
86
- value = { elem . value }
87
- onChange = { e => handleTestCaseInput ( e , idx ) } />
88
- < input
89
- type = "button"
90
- name = "del_entry"
91
- value = "Delete..."
92
- onClick = { e => handleDeleteField ( e , idx ) }
93
- style = { { backgroundColor : "white" } } />
94
- < br />
95
- </ >
104
+ < div className = { style . wrapper } >
105
+ < form className = { style . form_container } onSubmit = { handleSubmission } >
106
+ < h1 className = { style . title } > Create a new Question</ h1 >
107
+ < FormTextInput required disabled = { loading } label = "Question Title: " name = "title"
108
+ value = { formData . title } onChange = { handleFormTextInput } />
109
+ < RadioButtonGroup required disabled = { loading } label = "Difficulty: " group = "difficulty"
110
+ options = { { "Easy" : 1 , "Medium" : 2 , "Hard" : 3 } } onChange = { handleFormTextInput } />
111
+ < FormTextAreaInput required disabled = { loading } label = "Description: " name = "description"
112
+ value = { formData . description } onChange = { handleFormTextInput } />
113
+ < FormTextInput disabled = { loading } label = "Categories: " name = "categories"
114
+ value = { category } onChange = { handleCategoriesInput } >
115
+ < input type = "button" onClick = { handleCategoryAdd } value = "Add" disabled = { loading } />
116
+ </ FormTextInput >
117
+ < div className = { style . radio_container } >
118
+ { formData . categories . length == 0
119
+ ? ( < p className = { style . disabledText } > No Categories added.</ p > )
120
+ : formData . categories . map ( ( elem , idx ) => (
121
+ < p key = { idx } className = { style . deletableText }
122
+ onClick = { e => handleCategoryDel ( e , idx ) } >
123
+ { elem }
124
+ </ p >
125
+ ) ) }
126
+ </ div >
127
+ < div className = { style . input_container } >
128
+ < div >
129
+ < FormTextInput disabled = { loading } label = "Test Case: " name = "key"
130
+ value = { mapping . key } onChange = { handleMappingInput } />
131
+ < FormTextInput disabled = { loading } label = "Expected: " name = "value"
132
+ value = { mapping . value } onChange = { handleMappingInput } />
133
+ </ div >
134
+ < input type = "button" onClick = { handleMappingAdd } value = "Add" disabled = { loading } />
135
+ </ div >
136
+ { testCases . length == 0
137
+ ? ( < p className = { style . disabledText } > No Test Cases added.</ p > )
138
+ : testCases . map ( ( elem , idx ) => (
139
+ < p key = { idx } className = { style . deletableText }
140
+ onClick = { e => handleMappingDel ( e , idx ) } >
141
+ { elem . key } /{ elem . value }
142
+ </ p >
96
143
) ) }
97
- < input
98
- type = "button"
99
- name = "add_entry"
100
- value = "Add..."
101
- onClick = { handleAddField }
102
- style = { { backgroundColor : "white" } } />
103
144
< button
145
+ disabled = { loading }
104
146
type = "submit"
105
147
name = "submit"
106
- style = { { backgroundColor : "white" } } > Submit</ button >
148
+ className = { `${ style . title } `
149
+ } > Submit</ button >
107
150
</ form >
108
151
</ div >
109
152
)
0 commit comments