4343 dbc .Row ([
4444 dbc .Col (dbc .Button ("💾 Save" , id = "save-button" , color = "secondary" , size = "sm" )),
4545 dbc .Col (dbc .Button ("📂 Load" , id = "load-button" , color = "secondary" , size = "sm" )),
46+ dbc .Col (html .Div (id = "save-status" ), width = "auto" ),
4647 ], className = "mb-3" ),
4748 html .Div (id = "exam-table" )
4849 ], width = 6 )
@@ -329,12 +330,17 @@ def add_exam(n_clicks, name, exam_date, hours, topics):
329330 })
330331
331332 rows = []
332- for e in exams :
333+ for i , e in enumerate ( exams ) :
333334 rows .append (html .Tr ([
334335 html .Td (e ["name" ]),
335336 html .Td (e ["date" ]),
336337 html .Td (f"{ e ['hours' ]} hrs" ),
337- html .Td (", " .join (e ["topics" ]) if e ["topics" ] else "—" )
338+ html .Td (", " .join (e ["topics" ]) if e ["topics" ] else "—" ),
339+ html .Td (dbc .Button ("🗑️" ,
340+ id = {"type" : "delete-exam" , "index" : i },
341+ color = "danger" ,
342+ size = "sm"
343+ ))
338344 ]))
339345
340346 table = dbc .Table ([
@@ -347,21 +353,56 @@ def add_exam(n_clicks, name, exam_date, hours, topics):
347353
348354 return table
349355
356+ @app .callback (
357+ Output ("exam-table" , "children" , allow_duplicate = True ),
358+ Input ({"type" : "delete-exam" , "index" : ALL }, "n_clicks" ),
359+ prevent_initial_call = True
360+ )
361+ def delete_exam (n_clicks_list ):
362+ if not any (n_clicks_list ):
363+ return dash .no_update
364+
365+ triggered_index = ctx .triggered_id ["index" ]
366+ exams .pop (triggered_index )
367+
368+ rows = []
369+ for i , e in enumerate (exams ):
370+ rows .append (html .Tr ([
371+ html .Td (e ["name" ]),
372+ html .Td (e ["date" ]),
373+ html .Td (f"{ e ['hours' ]} hrs" ),
374+ html .Td (", " .join (e ["topics" ]) if e ["topics" ] else "—" ),
375+ html .Td (dbc .Button ("🗑️" ,
376+ id = {"type" : "delete-exam" , "index" : i },
377+ color = "danger" ,
378+ size = "sm"
379+ ))
380+ ]))
381+
382+ table = dbc .Table ([
383+ html .Thead (html .Tr ([
384+ html .Th ("Exam" ), html .Th ("Date" ),
385+ html .Th ("Hours" ), html .Th ("Topics" ), html .Th ("" )
386+ ])),
387+ html .Tbody (rows )
388+ ], bordered = True , hover = True , striped = True , size = "sm" )
389+
390+ return table if exams else html .P ("No exams added." )
350391
351392# Callback — save exams
352393@app .callback (
353394 Output ("save-button" , "children" ),
395+ Output ("save-status" , "children" ),
354396 Input ("save-button" , "n_clicks" ),
355397 prevent_initial_call = True
356398)
357399def save_exams (n_clicks ):
358400 if not exams :
359- return "💾 Save"
401+ return "💾 Save" , ""
360402 df = pd .DataFrame (exams )
361403 df ["topics" ] = df ["topics" ].apply (lambda t : ", " .join (t ) if isinstance (t , list ) else "" )
362404 df .to_excel ("my_studysmart.xlsx" , index = False )
363- return "✅ Saved!"
364-
405+ return "💾 Save" , "✅ Saved!"
365406
366407# Callback — load exams
367408@app .callback (
@@ -389,18 +430,23 @@ def load_exams(n_clicks):
389430 })
390431
391432 rows = []
392- for e in exams :
433+ for i , e in enumerate ( exams ) :
393434 rows .append (html .Tr ([
394435 html .Td (e ["name" ]),
395436 html .Td (e ["date" ]),
396437 html .Td (f"{ e ['hours' ]} hrs" ),
397- html .Td (", " .join (e ["topics" ]) if e ["topics" ] else "—" )
398- ]))
438+ html .Td (", " .join (e ["topics" ]) if e ["topics" ] else "—" ),
439+ html .Td (dbc .Button ("🗑️" ,
440+ id = {"type" : "delete-exam" , "index" : i },
441+ color = "danger" ,
442+ size = "sm"
443+ ))
444+ ]))
399445
400446 table = dbc .Table ([
401447 html .Thead (html .Tr ([
402448 html .Th ("Exam" ), html .Th ("Date" ),
403- html .Th ("Hours" ), html .Th ("Topics" )
449+ html .Th ("Hours" ), html .Th ("Topics" ), html . Th ( "" )
404450 ])),
405451 html .Tbody (rows )
406452 ], bordered = True , hover = True , striped = True , size = "sm" )
@@ -629,5 +675,3 @@ def generate_schedule(n_clicks, spaced_repetition, default_hours, start_date):
629675if __name__ == "__main__" :
630676 app .run (debug = True )
631677
632-
633-
0 commit comments