001package com.studentgui.apppages; 002 003import java.awt.BorderLayout; 004import java.awt.Font; 005import java.awt.GridBagConstraints; 006import java.awt.GridBagLayout; 007import java.awt.Insets; 008import java.awt.event.ActionEvent; 009import java.awt.event.KeyEvent; 010import java.sql.SQLException; 011import java.time.LocalDate; 012 013import javax.swing.JButton; 014import javax.swing.JLabel; 015import javax.swing.JPanel; 016import javax.swing.JScrollPane; 017import javax.swing.JTextArea; 018import javax.swing.SwingUtilities; 019 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023/** 024 * Freeform session notes editor for general observations and reflections. 025 * 026 * <p>Provides a simple multi-line text area for educators to record unstructured notes 027 * about a student session. This complements the structured assessment pages (Braille, Abacus, etc.) 028 * by allowing qualitative observations, anecdotal records, and contextual details that don't 029 * fit into numeric scoring fields.</p> 030 * 031 * <p><b>Typical Use Cases:</b></p> 032 * <ul> 033 * <li>Recording behavioral observations (e.g., "Student showed increased frustration with Nemeth fractions today")</li> 034 * <li>Documenting environmental factors affecting performance (e.g., "Noisy classroom due to construction")</li> 035 * <li>Noting equipment issues or accommodations used (e.g., "Switched to Braille Sense due to BrailleNote malfunction")</li> 036 * <li>General reflections or instructional notes for future reference</li> 037 * </ul> 038 * 039 * <p><b>Data Storage:</b></p> 040 * <ul> 041 * <li>Notes persisted via {@link com.studentgui.apphelpers.Database#saveSessionNotes} to {@code ProgressSession.notes} column</li> 042 * <li>Associated with a SessionNotes progress type and session ID for consistent querying</li> 043 * <li>JSON export: {@code StudentDataFiles/<student>/Sessions/SessionNotes/SessionNotes-<sessionId>-<timestamp>.json}</li> 044 * <li>No plots or reports generated (text-only data)</li> 045 * </ul> 046 * 047 * <p>The shared {@link JLineGraph} component is present for UI layout consistency but remains 048 * empty (session notes are not quantitative data). This page does not implement listener interfaces 049 * as it operates on static student/date parameters provided at construction time.</p> 050 * 051 * @see com.studentgui.apphelpers.Database#saveSessionNotes 052 * @see com.studentgui.apphelpers.dto.NotesPayload 053 */ 054public class SessionNotes extends JPanel { 055 private static final Logger LOG = LoggerFactory.getLogger(SessionNotes.class); 056 /** Text area containing session notes entered by the user. */ 057 private final JTextArea notesArea; 058 059 /** Selected student's display name used when saving session notes (may be null). */ 060 private final String studentNameParam; 061 062 /** Date associated with these session notes. */ 063 private final LocalDate dateParam; 064 065 /** 066 * Create a SessionNotes page for the provided student and date. 067 * The supplied JLineGraph is displayed below the notes editor. 068 * 069 * @param studentName student display name (may be null when no student selected) 070 * @param date the date this session pertains to 071 * @param graph the chart component shown beneath the notes 072 */ 073 public SessionNotes(String studentName, LocalDate date, JLineGraph graph) { 074 this.studentNameParam = (studentName == null || studentName.trim().isEmpty()) ? com.studentgui.apphelpers.Helpers.defaultStudent() : studentName; 075 this.dateParam = date; 076 setLayout(new BorderLayout()); 077 078 JPanel p = new JPanel(new GridBagLayout()); 079 JPanel view = new JPanel(new BorderLayout()); 080 view.add(p, BorderLayout.NORTH); 081 view.setBorder(javax.swing.BorderFactory.createEmptyBorder(20,20,20,20)); 082 JScrollPane scroll = new JScrollPane(view); 083 scroll.getAccessibleContext().setAccessibleName("Session Notes data entry scroll pane"); 084 GridBagConstraints gbc = new GridBagConstraints(); gbc.insets=new Insets(2,2,2,2); gbc.fill = GridBagConstraints.BOTH; gbc.anchor = GridBagConstraints.NORTHWEST; 085 JLabel title = new JLabel("Session Notes", JLabel.LEFT); 086 title.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 28)); 087 title.getAccessibleContext().setAccessibleName("Session Notes Title"); 088 gbc.gridx=0; gbc.gridy=0; gbc.gridwidth=1; p.add(title, gbc); 089 090 int globalLabel = com.studentgui.uicomp.PhaseScoreField.getGlobalLabelWidth(); 091 gbc.gridy=1; gbc.gridx=0; JLabel notesLabel = new JLabel("Notes:"); notesLabel.setPreferredSize(new java.awt.Dimension(globalLabel, notesLabel.getPreferredSize().height)); p.add(notesLabel, gbc); 092 gbc.gridy=2; gbc.gridx=0; notesArea = new JTextArea(8,40); notesArea.setLineWrap(true); notesArea.setWrapStyleWord(true); notesArea.setToolTipText("Enter session notes for the student"); notesArea.getAccessibleContext().setAccessibleName("Session notes"); p.add(notesArea, gbc); 093 notesLabel.setLabelFor(notesArea); 094 095 gbc.gridy=3; JButton submit = new JButton("Save Session Notes"); 096 submit.addActionListener((ActionEvent e)-> saveNotes()); 097 submit.setMnemonic(KeyEvent.VK_S); 098 submit.setToolTipText("Save session notes (Alt+S)"); 099 submit.getAccessibleContext().setAccessibleName("Save Session Notes"); 100 p.add(submit, gbc); 101 102 add(scroll, BorderLayout.CENTER); 103 104 SwingUtilities.invokeLater(()->{ p.setPreferredSize(p.getPreferredSize()); revalidate(); }); 105 106 com.studentgui.apphelpers.Helpers.createFolderHierarchy(); 107 } 108 109 /** 110 * Persist the contents of the session notes into the database. Ensures 111 * required student and progress session records exist and writes the notes 112 * to the ProgressSession.notes column. 113 */ 114 private void saveNotes() { 115 if (this.studentNameParam == null || this.studentNameParam.trim().isEmpty()) { 116 javax.swing.JOptionPane.showMessageDialog(this, "Please select a student before saving session notes.", "Missing student", javax.swing.JOptionPane.WARNING_MESSAGE); 117 return; 118 } 119 120 try { 121 int studentId = com.studentgui.apphelpers.Database.getOrCreateStudent(this.studentNameParam); 122 int ptId = com.studentgui.apphelpers.Database.getOrCreateProgressType("SessionNotes"); 123 int sessionId = com.studentgui.apphelpers.Database.createProgressSession(studentId, ptId, this.dateParam); 124 String notes = notesArea.getText(); 125 com.studentgui.apphelpers.Database.saveSessionNotes(sessionId, notes); 126 LOG.info("Saved session notes for {}", studentNameParam); 127 com.studentgui.apphelpers.UiNotifier.show("Session notes saved."); 128 com.studentgui.apphelpers.dto.NotesPayload payload = new com.studentgui.apphelpers.dto.NotesPayload(sessionId, notes); 129 java.nio.file.Path jsonOut = com.studentgui.apphelpers.SessionJsonWriter.writeSessionJson(this.studentNameParam, "SessionNotes", payload, sessionId); 130 if (jsonOut == null) { 131 LOG.warn("Unable to save SessionNotes session JSON for sessionId={}", sessionId); 132 } 133 } catch (SQLException ex) { 134 LOG.error("Error saving session notes", ex); 135 javax.swing.JOptionPane.showMessageDialog(this, "Database error saving session notes: " + ex.getMessage(), "Database error", javax.swing.JOptionPane.ERROR_MESSAGE); 136 } 137 } 138}