@@ -15,6 +15,7 @@ import {
15
15
Tabs ,
16
16
Tag ,
17
17
Modal ,
18
+ Form ,
18
19
} from "antd" ;
19
20
import { Content } from "antd/es/layout/layout" ;
20
21
import {
@@ -29,13 +30,18 @@ import {
29
30
DeleteQuestion as DeleteQuestionByDocref ,
30
31
GetQuestions ,
31
32
Question ,
33
+ CreateQuestion ,
34
+ NewQuestion ,
35
+ EditQuestion ,
32
36
} from "./services/question" ;
33
37
import {
34
38
CategoriesOption ,
35
39
DifficultyOption ,
36
40
OrderOption ,
37
41
} from "../utils/SelectOptions" ;
38
42
import Link from "next/link" ;
43
+ import TextArea from "antd/es/input/TextArea" ;
44
+ import { title } from "process" ;
39
45
40
46
/**
41
47
* defines the State of the page whe a user is deleing an object. Has 3 general states:
@@ -98,6 +104,77 @@ export default function Home() {
98
104
// Message States
99
105
const [ messageApi , contextHolder ] = message . useMessage ( ) ;
100
106
107
+ // States for Create New Problem Modal
108
+ const [ form ] = Form . useForm ( ) ;
109
+ const [ isNewProblemModalOpen , setIsNewProblemModelOpen ] = useState ( false ) ;
110
+
111
+ // States for Edit Existing Problem Modal
112
+ const [ editForm ] = Form . useForm ( ) ;
113
+ const [ isEditModalOpen , setIsEditModalOpen ] = useState < boolean [ ] | undefined > (
114
+ undefined
115
+ ) ;
116
+
117
+ // State for refreshing data
118
+ const [ refresh , setRefresh ] = useState ( false ) ;
119
+
120
+ const handleEditClick = ( index : number , question : Question ) => {
121
+ // Open the modal for the specific question
122
+ const updatedModals =
123
+ isEditModalOpen && isEditModalOpen . map ( ( _ , idx ) => idx === index ) ;
124
+ setIsEditModalOpen ( updatedModals ) ; // Only the selected question's modal is open
125
+
126
+ // Set the form value
127
+ editForm . setFieldsValue ( {
128
+ title : question . title ,
129
+ description : question . description ,
130
+ complexity : question . complexity ,
131
+ categories : question . categories ,
132
+ } ) ;
133
+ } ;
134
+
135
+ // Function to handle modal close
136
+ const handleModalClose = ( index : number ) => {
137
+ if ( isEditModalOpen ) {
138
+ const updatedModals = [ ...isEditModalOpen ] ;
139
+ updatedModals [ index ] = false ; // Close the specific modal
140
+ setIsEditModalOpen ( updatedModals ) ;
141
+ }
142
+ } ;
143
+
144
+ const handleEditQuestion = async (
145
+ values : NewQuestion ,
146
+ index : number ,
147
+ docRefId : string
148
+ ) => {
149
+ try {
150
+ const editedQuestion = await EditQuestion ( values , docRefId ) ;
151
+ // Reset form or update UI as needed
152
+ handleModalClose ( index ) ;
153
+ editForm . resetFields ( ) ;
154
+ success ( "Problem Updated!" ) ;
155
+ setRefresh ( ! refresh ) ;
156
+ } catch ( err : any ) {
157
+ error ( err . message ) ;
158
+ }
159
+ } ;
160
+
161
+ const handleCreateQuestion = async ( values : NewQuestion ) => {
162
+ try {
163
+ const createdQuestion = await CreateQuestion ( values ) ;
164
+ // Reset form or update UI as needed
165
+ setIsNewProblemModelOpen ( false ) ;
166
+ form . resetFields ( ) ;
167
+ success ( "New Problem Created!" ) ;
168
+ setRefresh ( ! refresh ) ;
169
+ } catch ( err : any ) {
170
+ error ( err . message ) ;
171
+ }
172
+ } ;
173
+
174
+ const showNewProblemModal = ( ) => {
175
+ setIsNewProblemModelOpen ( true ) ;
176
+ } ;
177
+
101
178
const success = ( message : string ) => {
102
179
messageApi . open ( {
103
180
type : "success" ,
@@ -138,18 +215,27 @@ export default function Home() {
138
215
setCurrentPage ( data . currentPage ) ;
139
216
setLimit ( data . limit ) ;
140
217
setIsLoading ( false ) ;
218
+ setIsEditModalOpen ( Array ( data . questions . length ) . fill ( false ) ) ;
141
219
}
142
220
143
221
useEffect ( ( ) => {
144
222
loadQuestions ( ) ;
145
- } , [ limit , currentPage , sortBy , difficulty , categories , delayedSearch ] ) ;
223
+ } , [
224
+ limit ,
225
+ currentPage ,
226
+ sortBy ,
227
+ difficulty ,
228
+ categories ,
229
+ delayedSearch ,
230
+ refresh ,
231
+ ] ) ;
146
232
147
233
// Delay the fetching of data only after user stops typing for awhile
148
234
useEffect ( ( ) => {
149
235
const timeout = setTimeout ( ( ) => {
150
236
setDelayedSearch ( search ) ;
151
237
setCurrentPage ( 1 ) ; // Reset the current page
152
- } , 800 ) ;
238
+ } , 500 ) ;
153
239
return ( ) => clearTimeout ( timeout ) ;
154
240
} , [ search ] ) ;
155
241
@@ -174,7 +260,7 @@ export default function Home() {
174
260
>
175
261
< Button type = "link" > { text } </ Button >
176
262
</ Link >
177
- ) , // TODO (Sean): Onclick links to the individual question page
263
+ ) ,
178
264
} ,
179
265
{
180
266
title : "Categories" ,
@@ -207,10 +293,108 @@ export default function Home() {
207
293
title : "Actions" ,
208
294
key : "actions" ,
209
295
dataIndex : "id" ,
210
- render : ( _ : number , question : Question ) => (
296
+ render : ( _ : number , question : Question , index : number ) => (
211
297
< div >
212
298
{ /* TODO (Sean): Include Logic to handle retrieving of editable data here and display in a modal component */ }
213
- < Button className = "edit-button" icon = { < EditOutlined /> } > </ Button >
299
+ < Modal
300
+ title = "Edit Problem"
301
+ open = { isEditModalOpen && isEditModalOpen [ index ] }
302
+ onCancel = { ( ) => handleModalClose ( index ) }
303
+ footer = { null }
304
+ width = { 600 }
305
+ >
306
+ < Form
307
+ name = "edit-form"
308
+ { ...layout }
309
+ form = { editForm }
310
+ onFinish = { ( values ) => {
311
+ handleEditQuestion ( values , index , question . docRefId ) ;
312
+ } }
313
+ >
314
+ < Form . Item
315
+ name = "title"
316
+ label = "Title"
317
+ rules = { [
318
+ {
319
+ required : true ,
320
+ message : "Please enter question title!" ,
321
+ } ,
322
+ ] }
323
+ >
324
+ < Input name = "title" />
325
+ </ Form . Item >
326
+ < Form . Item
327
+ name = "description"
328
+ label = "Description"
329
+ rules = { [
330
+ {
331
+ required : true ,
332
+ message : "Please enter question description!" ,
333
+ } ,
334
+ ] }
335
+ >
336
+ < TextArea name = "description" />
337
+ </ Form . Item >
338
+ < Form . Item
339
+ name = "complexity"
340
+ label = "Complexity"
341
+ rules = { [
342
+ {
343
+ required : true ,
344
+ message : "Please select a complexity!" ,
345
+ } ,
346
+ ] }
347
+ >
348
+ < Select
349
+ options = { [
350
+ {
351
+ label : "Easy" ,
352
+ value : "easy" ,
353
+ } ,
354
+ {
355
+ label : "Medium" ,
356
+ value : "medium" ,
357
+ } ,
358
+ {
359
+ label : "Hard" ,
360
+ value : "hard" ,
361
+ } ,
362
+ ] }
363
+ onChange = { ( value ) => form . setFieldValue ( "complexity" , value ) }
364
+ allowClear
365
+ />
366
+ </ Form . Item >
367
+ < Form . Item
368
+ name = "categories"
369
+ label = "Categories"
370
+ rules = { [
371
+ {
372
+ required : true ,
373
+ message : "Please select the relevant categories!" ,
374
+ } ,
375
+ ] }
376
+ >
377
+ < Select
378
+ mode = "multiple"
379
+ options = { CategoriesOption }
380
+ onChange = { ( value ) => form . setFieldValue ( "categories" , value ) }
381
+ allowClear
382
+ />
383
+ </ Form . Item >
384
+ < Form . Item
385
+ style = { { display : "flex" , justifyContent : "flex-end" } }
386
+ >
387
+ < Button type = "primary" htmlType = "submit" >
388
+ Save
389
+ </ Button >
390
+ </ Form . Item >
391
+ </ Form >
392
+ </ Modal >
393
+ < Button
394
+ className = "edit-button"
395
+ icon = { < EditOutlined /> }
396
+ onClick = { ( ) => handleEditClick ( index , question ) }
397
+ > </ Button >
214
398
{ /* TODO (Ryan): Include Pop-up confirmation for delete when clicked and link to delete API --> can also explore success notification or look into react-toast*/ }
215
399
< Button
216
400
className = "delete-button"
@@ -285,6 +469,12 @@ export default function Home() {
285
469
}
286
470
setDeletionStage ( { } ) ;
287
471
} ;
472
+
473
+ const layout = {
474
+ labelCol : { span : 4 } ,
475
+ wrapperCol : { span : 20 } ,
476
+ } ;
477
+
288
478
return (
289
479
< div >
290
480
{ contextHolder }
@@ -296,9 +486,112 @@ export default function Home() {
296
486
< div className = "content-title" > Problems</ div >
297
487
< div className = "create-button" >
298
488
{ /* TODO (Sean): Launch a popup modal that links to the backend api to create a new entry in db, --> look into success/error notification/react toast */ }
299
- < Button type = "primary" icon = { < PlusCircleOutlined /> } >
489
+ < Button
490
+ type = "primary"
491
+ icon = { < PlusCircleOutlined /> }
492
+ onClick = { showNewProblemModal }
493
+ >
300
494
Create New Problem
301
495
</ Button >
496
+ < Modal
497
+ title = "Create New Problem"
498
+ open = { isNewProblemModalOpen }
499
+ // onOk={() => setIsNewProblemModelOpen(false)} // Replace with handleSubmit
500
+ onCancel = { ( ) => setIsNewProblemModelOpen ( false ) }
501
+ footer = { null }
502
+ width = { 600 }
503
+ >
504
+ < Form
505
+ name = "create-form"
506
+ { ...layout }
507
+ form = { form }
508
+ onFinish = { ( values ) => {
509
+ handleCreateQuestion ( values ) ;
510
+ } }
511
+ >
512
+ < Form . Item
513
+ name = "title"
514
+ label = "Title"
515
+ rules = { [
516
+ {
517
+ required : true ,
518
+ message : "Please enter question title!" ,
519
+ } ,
520
+ ] }
521
+ >
522
+ < Input name = "title" />
523
+ </ Form . Item >
524
+ < Form . Item
525
+ name = "description"
526
+ label = "Description"
527
+ rules = { [
528
+ {
529
+ required : true ,
530
+ message : "Please enter question description!" ,
531
+ } ,
532
+ ] }
533
+ >
534
+ < TextArea name = "description" />
535
+ </ Form . Item >
536
+ < Form . Item
537
+ name = "complexity"
538
+ label = "Complexity"
539
+ rules = { [
540
+ {
541
+ required : true ,
542
+ message : "Please select a complexity!" ,
543
+ } ,
544
+ ] }
545
+ >
546
+ < Select
547
+ options = { [
548
+ {
549
+ label : "Easy" ,
550
+ value : "easy" ,
551
+ } ,
552
+ {
553
+ label : "Medium" ,
554
+ value : "medium" ,
555
+ } ,
556
+ {
557
+ label : "Hard" ,
558
+ value : "hard" ,
559
+ } ,
560
+ ] }
561
+ onChange = { ( value ) =>
562
+ form . setFieldValue ( "complexity" , value )
563
+ }
564
+ allowClear
565
+ />
566
+ </ Form . Item >
567
+ < Form . Item
568
+ name = "categories"
569
+ label = "Categories"
570
+ rules = { [
571
+ {
572
+ required : true ,
573
+ message : "Please select the relevant categories!" ,
574
+ } ,
575
+ ] }
576
+ >
577
+ < Select
578
+ mode = "multiple"
579
+ options = { CategoriesOption }
580
+ onChange = { ( value ) =>
581
+ form . setFieldValue ( "categories" , value )
582
+ }
583
+ allowClear
584
+ />
585
+ </ Form . Item >
586
+ < Form . Item
587
+ style = { { display : "flex" , justifyContent : "flex-end" } }
588
+ >
589
+ < Button type = "primary" htmlType = "submit" >
590
+ Create
591
+ </ Button >
592
+ </ Form . Item >
593
+ </ Form >
594
+ </ Modal >
302
595
</ div >
303
596
</ div >
304
597
{ /* TODO (Ben/Ryan): Include and link search & filter parameters */ }
0 commit comments