Compare commits

..

4 Commits

@ -0,0 +1,172 @@
package mathquiz.backend;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.Random;
/**
* Email Service Class - Handles email verification code sending
*/
public class EmailService {
// Email configuration - loaded from properties file
private String smtpHost;
private String smtpPort;
private String emailUsername;
private String emailPassword;
private String fromEmail;
public EmailService() {
loadEmailConfig();
}
/**
* Load email configuration from properties file
*/
private void loadEmailConfig() {
Properties config = new Properties();
try {
config.load(new FileInputStream("email_config.properties"));
smtpHost = config.getProperty("smtp.host", "smtp.gmail.com");
smtpPort = config.getProperty("smtp.port", "587");
emailUsername = config.getProperty("email.username", "your-email@gmail.com");
emailPassword = config.getProperty("email.password", "your-app-password");
fromEmail = config.getProperty("email.from", "your-email@gmail.com");
} catch (IOException e) {
System.err.println("Warning: Could not load email configuration, using default settings");
// Use default settings
smtpHost = "smtp.gmail.com";
smtpPort = "587";
emailUsername = "your-email@gmail.com";
emailPassword = "your-app-password";
fromEmail = "your-email@gmail.com";
}
}
/**
* Send verification code to email
*/
public String sendVerificationCode(String email) throws Exception {
// Generate 6-digit numeric verification code
String verificationCode = generateVerificationCode();
// Send real email
sendRealEmail(email, verificationCode);
// Also save to file for backup verification
saveVerificationCodeToFile(email, verificationCode);
return verificationCode;
}
/**
* Generate verification code
*/
private String generateVerificationCode() {
Random random = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < 6; i++) {
code.append(random.nextInt(10));
}
return code.toString();
}
/**
* Send real email using SMTP protocol
*/
private void sendRealEmail(String email, String verificationCode) throws Exception {
try {
// Create email content
String subject = "Math Quiz System - Registration Verification Code";
String content = createEmailContent(verificationCode);
// Use SimpleEmailSender
SimpleEmailSender sender = new SimpleEmailSender(
smtpHost,
Integer.parseInt(smtpPort),
emailUsername,
emailPassword,
fromEmail
);
sender.sendEmail(email, subject, content);
System.out.println("Verification code sent successfully to: " + email);
System.out.println("Verification code: " + verificationCode);
} catch (Exception e) {
System.err.println("Failed to send email: " + e.getMessage());
// Fall back to console output
System.out.println("=== EMAIL VERIFICATION CODE ===");
System.out.println("To: " + email);
System.out.println("Subject: Math Quiz System - Registration Verification Code");
System.out.println("Verification Code: " + verificationCode);
System.out.println("Please check your email or use the code above.");
System.out.println("================================");
}
}
/**
* Create email content with HTML formatting
*/
private String createEmailContent(String verificationCode) {
return "<html>" +
"<head><meta charset='UTF-8'></head>" +
"<body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>" +
"<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
"<h2 style='color: #0066cc; text-align: center;'>Math Quiz System</h2>" +
"<h3 style='color: #333;'>Registration Verification</h3>" +
"<p>Hello,</p>" +
"<p>Thank you for registering with Math Quiz System. Please use the following verification code to complete your registration:</p>" +
"<div style='background-color: #f0f8ff; border: 2px solid #0066cc; border-radius: 8px; padding: 20px; text-align: center; margin: 20px 0;'>" +
"<h1 style='color: #0066cc; font-size: 32px; margin: 0; letter-spacing: 5px;'>" + verificationCode + "</h1>" +
"</div>" +
"<p><strong>Important:</strong></p>" +
"<ul>" +
"<li>This verification code will expire in 5 minutes</li>" +
"<li>Please enter this code exactly as shown</li>" +
"<li>If you did not request this code, please ignore this email</li>" +
"</ul>" +
"<p>Best regards,<br>Math Quiz System Team</p>" +
"<hr style='border: none; border-top: 1px solid #eee; margin: 20px 0;'>" +
"<p style='font-size: 12px; color: #666; text-align: center;'>This is an automated message, please do not reply.</p>" +
"</div>" +
"</body>" +
"</html>";
}
/**
* Save verification code to file (for demonstration only)
*/
private void saveVerificationCodeToFile(String email, String verificationCode) {
try {
java.nio.file.Files.createDirectories(java.nio.file.Paths.get("data"));
java.nio.file.Files.write(
java.nio.file.Paths.get("data/verification_codes.txt"),
(email + ":" + verificationCode + "\n").getBytes(),
java.nio.file.StandardOpenOption.CREATE,
java.nio.file.StandardOpenOption.APPEND
);
} catch (Exception e) {
// Ignore file save errors
}
}
/**
* Validate email format
*/
public boolean isValidEmail(String email) {
if (email == null || email.trim().isEmpty()) {
return false;
}
String trimmedEmail = email.trim();
return trimmedEmail.contains("@") &&
trimmedEmail.contains(".") &&
trimmedEmail.length() > 5;
}
}

@ -0,0 +1,58 @@
package mathquiz.backend;
/**
* Question Class - Represents a multiple choice question
*/
public class Question {
private final String question;
private final String optionA;
private final String optionB;
private final String optionC;
private final String optionD;
private final String correctAnswer;
public Question(String question, String optionA, String optionB, String optionC, String optionD, String correctAnswer) {
this.question = question;
this.optionA = optionA;
this.optionB = optionB;
this.optionC = optionC;
this.optionD = optionD;
this.correctAnswer = correctAnswer;
}
public String getQuestion() {
return question;
}
public String getOptionA() {
return optionA;
}
public String getOptionB() {
return optionB;
}
public String getOptionC() {
return optionC;
}
public String getOptionD() {
return optionD;
}
public String getCorrectAnswer() {
return correctAnswer;
}
@Override
public String toString() {
return "Question{" +
"question='" + question + '\'' +
", optionA='" + optionA + '\'' +
", optionB='" + optionB + '\'' +
", optionC='" + optionC + '\'' +
", optionD='" + optionD + '\'' +
", correctAnswer='" + correctAnswer + '\'' +
'}';
}
}

@ -0,0 +1,298 @@
package mathquiz.backend;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import mathquiz.AccountType;
import mathquiz.Deduplicator;
import mathquiz.GeneratorFactory;
import mathquiz.ProblemGenerator;
/**
* Quiz Service Class - Handles question generation and quiz logic
*/
public class QuizService {
/**
* Generate multiple choice questions
*/
public List<Question> generateQuestions(AccountType accountType, int count) {
try {
// Get question generator
ProblemGenerator generator = GeneratorFactory.getGenerator(accountType);
// Generate original questions
List<String> rawQuestions = generator.generate(count);
// Deduplication processing - use default username if current user is null
String username = getCurrentUsername();
if (username == null || username.trim().isEmpty()) {
username = "default_user";
}
Deduplicator deduplicator = new Deduplicator(username);
List<String> uniqueQuestions = deduplicator.ensureUnique(rawQuestions, count, generator);
// Convert to multiple choice questions
List<Question> questions = new ArrayList<>();
for (String questionText : uniqueQuestions) {
Question question = convertToMultipleChoice(questionText, accountType);
questions.add(question);
}
return questions;
} catch (Exception e) {
System.err.println("Error in generateQuestions: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>(); // Return empty list instead of null
}
}
/**
* Convert expression questions to multiple choice questions
*/
private Question convertToMultipleChoice(String expression, AccountType accountType) {
try {
// Calculate correct answer
double correctAnswer = calculateExpression(expression, accountType);
// Handle invalid answers
if (Double.isNaN(correctAnswer) || Double.isInfinite(correctAnswer)) {
System.out.println("Invalid answer for expression: " + expression + ", using 0.0");
correctAnswer = 0.0;
}
System.out.println("Expression: " + expression + ", Answer: " + correctAnswer);
// Generate wrong options
List<Double> options = generateOptions(correctAnswer);
System.out.println("Generated " + options.size() + " wrong options: " + options);
// Randomly arrange options
Random random = new Random();
String[] optionTexts = new String[4];
String correctAnswerText = "";
// Create a list with correct answer and wrong options
List<Double> allOptions = new ArrayList<>();
allOptions.add(correctAnswer);
allOptions.addAll(options);
System.out.println("All options before shuffle: " + allOptions);
// Shuffle the options
Collections.shuffle(allOptions);
System.out.println("All options after shuffle: " + allOptions);
// Find the correct answer index after shuffling
int correctAnswerIndex = allOptions.indexOf(correctAnswer);
System.out.println("Correct answer index: " + correctAnswerIndex);
for (int i = 0; i < 4; i++) {
optionTexts[i] = formatAnswer(allOptions.get(i));
if (i == correctAnswerIndex) {
correctAnswerText = getOptionLetter(i);
}
}
System.out.println("Final options: " + java.util.Arrays.toString(optionTexts));
System.out.println("Correct answer: " + correctAnswerText);
return new Question(
"Calculate: " + expression + " = ?",
optionTexts[0], optionTexts[1], optionTexts[2], optionTexts[3],
correctAnswerText
);
} catch (Exception e) {
System.err.println("Error in convertToMultipleChoice: " + e.getMessage());
e.printStackTrace();
// Return a default question
return new Question(
"Calculate: 1 + 1 = ?",
"1", "2", "3", "4",
"B"
);
}
}
/**
* Calculate expression result
*/
private double calculateExpression(String expression, AccountType accountType) {
try {
// Handle special functions
String processedExpression = preprocessExpression(expression, accountType);
// Simple expression calculation (in actual application should use more comprehensive expression parser)
return evaluateExpression(processedExpression);
} catch (Exception e) {
// If calculation fails, return a default value
return 0.0;
}
}
/**
* Preprocess expression, handle special functions
*/
private String preprocessExpression(String expression, AccountType accountType) {
String result = expression;
// Handle square root
result = result.replaceAll("sqrt\\(([^)]+)\\)", "Math.sqrt($1)");
// Handle square
result = result.replaceAll("\\(([^)]+)\\)\\^2", "($1) * ($1)");
// Handle trigonometric functions
result = result.replaceAll("sin\\(([^)]+)\\)", "Math.sin($1)");
result = result.replaceAll("cos\\(([^)]+)\\)", "Math.cos($1)");
result = result.replaceAll("tan\\(([^)]+)\\)", "Math.tan($1)");
// Handle operators
result = result.replace("×", "*");
result = result.replace("÷", "/");
return result;
}
/**
* Simple expression calculation
*/
private double evaluateExpression(String expression) {
try {
// Simplified expression calculation, only handle basic arithmetic operations
return evaluateSimpleExpression(expression);
} catch (Exception e) {
// If calculation fails, return 0
return 0.0;
}
}
/**
* Simplified expression calculation
*/
private double evaluateSimpleExpression(String expression) {
// Implement a simple expression calculator here
// For simplification, only handle basic number operations
try {
// Remove all spaces
expression = expression.replaceAll("\\s+", "");
// Handle parentheses
while (expression.contains("(")) {
int start = expression.lastIndexOf("(");
int end = expression.indexOf(")", start);
if (end == -1) break;
String subExpr = expression.substring(start + 1, end);
double result = evaluateSimpleExpression(subExpr);
expression = expression.substring(0, start) + result + expression.substring(end + 1);
}
// Handle multiplication and division
while (expression.contains("*") || expression.contains("/")) {
expression = processOperators(expression, new String[]{"*", "/"});
}
// Handle addition and subtraction
while (expression.contains("+") || (expression.contains("-") && !expression.startsWith("-"))) {
expression = processOperators(expression, new String[]{"+", "-"});
}
return Double.parseDouble(expression);
} catch (Exception e) {
return 0.0;
}
}
/**
* Process operators
*/
private String processOperators(String expression, String[] operators) {
for (String op : operators) {
int index = expression.indexOf(op);
if (index > 0) {
// Find left operand
int leftStart = index - 1;
while (leftStart >= 0 && (Character.isDigit(expression.charAt(leftStart)) || expression.charAt(leftStart) == '.')) {
leftStart--;
}
leftStart++;
// Find right operand
int rightEnd = index + 1;
while (rightEnd < expression.length() && (Character.isDigit(expression.charAt(rightEnd)) || expression.charAt(rightEnd) == '.')) {
rightEnd++;
}
double left = Double.parseDouble(expression.substring(leftStart, index));
double right = Double.parseDouble(expression.substring(index + 1, rightEnd));
double result = 0;
switch (op) {
case "+": result = left + right; break;
case "-": result = left - right; break;
case "*": result = left * right; break;
case "/": result = right != 0 ? left / right : 0; break;
}
expression = expression.substring(0, leftStart) + result + expression.substring(rightEnd);
break;
}
}
return expression;
}
/**
* Generate wrong options
*/
private List<Double> generateOptions(double correctAnswer) {
List<Double> options = new ArrayList<>();
Random random = new Random();
// Generate 3 wrong options
for (int i = 0; i < 3; i++) {
double wrongAnswer;
do {
// Generate wrong answers near correct answer
double variation = (random.nextDouble() - 0.5) * Math.abs(correctAnswer) * 0.5;
wrongAnswer = correctAnswer + variation;
// Ensure wrong answer is not 0 and different from correct answer
if (wrongAnswer == 0) {
wrongAnswer = random.nextDouble() * 10 + 1;
}
} while (Math.abs(wrongAnswer - correctAnswer) < 0.01);
options.add(wrongAnswer);
}
return options;
}
/**
* Format answer
*/
private String formatAnswer(double answer) {
if (answer == (long) answer) {
return String.valueOf((long) answer);
} else {
return String.format("%.2f", answer);
}
}
/**
* Get option letter
*/
private String getOptionLetter(int index) {
return String.valueOf((char) ('A' + index));
}
/**
* Get current username
*/
private String getCurrentUsername() {
return mathquiz.gui.QuizSession.getInstance().getCurrentUser();
}
}

@ -0,0 +1,184 @@
package mathquiz.backend;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
/**
* Simple Email Sender using SMTP protocol
* This class provides a basic SMTP client implementation
*/
public class SimpleEmailSender {
private String smtpHost;
private int smtpPort;
private String username;
private String password;
private String fromEmail;
public SimpleEmailSender(String smtpHost, int smtpPort, String username, String password, String fromEmail) {
this.smtpHost = smtpHost;
this.smtpPort = smtpPort;
this.username = username;
this.password = password;
this.fromEmail = fromEmail;
}
/**
* Send email using SMTP protocol with SSL/TLS support
*/
public void sendEmail(String toEmail, String subject, String content) throws Exception {
Socket socket = null;
BufferedReader reader = null;
PrintWriter writer = null;
try {
System.out.println("Connecting to SMTP server: " + smtpHost + ":" + smtpPort);
// Connect to SMTP server
socket = new Socket(smtpHost, smtpPort);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream(), true);
// Read server greeting
String response = reader.readLine();
System.out.println("Server greeting: " + response);
if (!response.startsWith("220")) {
throw new Exception("SMTP server error: " + response);
}
// Send EHLO command
String hostname = InetAddress.getLocalHost().getHostName();
if (hostname == null || hostname.isEmpty()) {
hostname = "localhost";
}
writer.println("EHLO " + hostname);
response = reader.readLine();
System.out.println("EHLO response: " + response);
if (!response.startsWith("250")) {
throw new Exception("EHLO failed: " + response);
}
// Read additional EHLO responses
while (response.startsWith("250-")) {
response = reader.readLine();
System.out.println("EHLO continuation: " + response);
}
// Start TLS if supported
writer.println("STARTTLS");
response = reader.readLine();
System.out.println("STARTTLS response: " + response);
if (response.startsWith("220")) {
// Upgrade to SSL socket
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, smtpHost, smtpPort, true);
sslSocket.startHandshake();
// Update streams to use SSL socket
reader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
writer = new PrintWriter(sslSocket.getOutputStream(), true);
// Send EHLO again after TLS
writer.println("EHLO " + hostname);
response = reader.readLine();
System.out.println("EHLO after TLS: " + response);
if (!response.startsWith("250")) {
throw new Exception("EHLO after TLS failed: " + response);
}
// Read additional EHLO responses
while (response.startsWith("250-")) {
response = reader.readLine();
System.out.println("EHLO continuation after TLS: " + response);
}
}
// Authenticate using AUTH LOGIN
writer.println("AUTH LOGIN");
response = reader.readLine();
System.out.println("AUTH LOGIN response: " + response);
if (!response.startsWith("334")) {
throw new Exception("AUTH LOGIN failed: " + response);
}
// Send username (base64 encoded)
String encodedUsername = java.util.Base64.getEncoder().encodeToString(username.getBytes());
writer.println(encodedUsername);
response = reader.readLine();
System.out.println("Username auth response: " + response);
if (!response.startsWith("334")) {
throw new Exception("Username authentication failed: " + response);
}
// Send password (base64 encoded)
String encodedPassword = java.util.Base64.getEncoder().encodeToString(password.getBytes());
writer.println(encodedPassword);
response = reader.readLine();
System.out.println("Password auth response: " + response);
if (!response.startsWith("235")) {
throw new Exception("Password authentication failed: " + response);
}
// Send MAIL FROM
writer.println("MAIL FROM:<" + fromEmail + ">");
response = reader.readLine();
System.out.println("MAIL FROM response: " + response);
if (!response.startsWith("250")) {
throw new Exception("MAIL FROM failed: " + response);
}
// Send RCPT TO
writer.println("RCPT TO:<" + toEmail + ">");
response = reader.readLine();
System.out.println("RCPT TO response: " + response);
if (!response.startsWith("250")) {
throw new Exception("RCPT TO failed: " + response);
}
// Send DATA
writer.println("DATA");
response = reader.readLine();
System.out.println("DATA response: " + response);
if (!response.startsWith("354")) {
throw new Exception("DATA failed: " + response);
}
// Send email headers and content
writer.println("From: " + fromEmail);
writer.println("To: " + toEmail);
writer.println("Subject: " + subject);
writer.println("MIME-Version: 1.0");
writer.println("Content-Type: text/html; charset=UTF-8");
writer.println();
writer.println(content);
writer.println(".");
response = reader.readLine();
System.out.println("Email send response: " + response);
if (!response.startsWith("250")) {
throw new Exception("Email sending failed: " + response);
}
// Quit
writer.println("QUIT");
response = reader.readLine();
System.out.println("QUIT response: " + response);
System.out.println("Email sent successfully to: " + toEmail);
} catch (Exception e) {
System.err.println("Failed to send email: " + e.getMessage());
e.printStackTrace();
throw e;
} finally {
try {
if (writer != null) writer.close();
if (reader != null) reader.close();
if (socket != null) socket.close();
} catch (IOException e) {
// Ignore cleanup errors
}
}
}
}

@ -0,0 +1,209 @@
package mathquiz.backend;
import java.io.*;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* User Service Class - Handles user registration, login, password management
*/
public class UserService {
private static final String USERS_FILE = "data/users.txt";
private static final String PASSWORDS_FILE = "data/passwords.txt";
private Map<String, String> userPasswords;
private Map<String, String> userEmails;
public UserService() {
loadUserData();
}
/**
* User registration
*/
public void registerUser(String email, String username) throws IOException {
if (userEmails.containsValue(email)) {
throw new IllegalArgumentException("This email has already been registered");
}
userEmails.put(username, email);
// Save user data
saveUserData();
}
/**
* Set password
*/
public void setPassword(String email, String password) throws IOException {
if (!isValidPassword(password)) {
throw new IllegalArgumentException("Password does not meet requirements");
}
String username = getUsernameByEmail(email);
if (username == null) {
throw new IllegalArgumentException("User does not exist");
}
userPasswords.put(username, password);
savePasswordData();
}
/**
* User login
*/
public boolean login(String email, String password) {
String username = getUsernameByEmail(email);
if (username == null) {
return false;
}
String storedPassword = userPasswords.get(username);
if (storedPassword == null) {
return false;
}
if (Objects.equals(storedPassword, password)) {
// Set current user (use email for session)
mathquiz.gui.QuizSession.getInstance().setCurrentUser(email);
return true;
}
return false;
}
/**
* Change password
*/
public boolean changePassword(String username, String oldPassword, String newPassword) throws IOException {
if (!isValidPassword(newPassword)) {
throw new IllegalArgumentException("New password does not meet requirements");
}
String storedPassword = userPasswords.get(username);
if (!Objects.equals(storedPassword, oldPassword)) {
return false;
}
userPasswords.put(username, newPassword);
savePasswordData();
return true;
}
/**
* Validate password format
*/
private boolean isValidPassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpperCase = false;
boolean hasLowerCase = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) {
hasUpperCase = true;
} else if (Character.isLowerCase(c)) {
hasLowerCase = true;
} else if (Character.isDigit(c)) {
hasDigit = true;
}
}
return hasUpperCase && hasLowerCase && hasDigit;
}
/**
* Get username by email
*/
private String getUsernameByEmail(String email) {
for (Map.Entry<String, String> entry : userEmails.entrySet()) {
if (entry.getValue().equals(email)) {
return entry.getKey();
}
}
return null;
}
/**
* Generate username
*/
private String generateUsername(String email) {
String baseName = email.split("@")[0];
String username = baseName;
int counter = 1;
while (userEmails.containsKey(username)) {
username = baseName + counter;
counter++;
}
return username;
}
/**
* Load user data
*/
private void loadUserData() {
userPasswords = new HashMap<>();
userEmails = new HashMap<>();
try {
// Create data directory
Files.createDirectories(Paths.get("data"));
// Load user email mapping
if (Files.exists(Paths.get(USERS_FILE))) {
for (String line : Files.readAllLines(Paths.get(USERS_FILE))) {
String[] parts = line.split(",");
if (parts.length == 2) {
userEmails.put(parts[0], parts[1]);
}
}
}
// Load user passwords
if (Files.exists(Paths.get(PASSWORDS_FILE))) {
for (String line : Files.readAllLines(Paths.get(PASSWORDS_FILE))) {
String[] parts = line.split(",");
if (parts.length == 2) {
userPasswords.put(parts[0], parts[1]);
}
}
}
} catch (IOException e) {
// If loading fails, use empty data
userPasswords = new HashMap<>();
userEmails = new HashMap<>();
}
}
/**
* Save user data
*/
private void saveUserData() throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(USERS_FILE))) {
for (Map.Entry<String, String> entry : userEmails.entrySet()) {
writer.write(entry.getKey() + "," + entry.getValue());
writer.newLine();
}
}
}
/**
* Save password data
*/
private void savePasswordData() throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(PASSWORDS_FILE))) {
for (Map.Entry<String, String> entry : userPasswords.entrySet()) {
writer.write(entry.getKey() + "," + entry.getValue());
writer.newLine();
}
}
}
}

@ -0,0 +1,186 @@
package mathquiz.gui;
import mathquiz.backend.UserService;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Change Password Dialog
*/
public class ChangePasswordDialog extends JDialog {
private JPasswordField newPasswordField;
private JPasswordField confirmPasswordField;
private JButton okButton;
private JButton cancelButton;
private JLabel messageLabel;
public ChangePasswordDialog(JFrame parent) {
super(parent, "Change Password", true);
initializeComponents();
setupLayout();
setupEventHandlers();
setLocationRelativeTo(parent);
setSize(400, 300);
setResizable(false);
}
private void initializeComponents() {
newPasswordField = new JPasswordField(20);
confirmPasswordField = new JPasswordField(20);
okButton = new JButton("OK");
cancelButton = new JButton("Cancel");
messageLabel = new JLabel("", JLabel.CENTER);
messageLabel.setForeground(Color.RED);
okButton.setPreferredSize(new Dimension(80, 30));
cancelButton.setPreferredSize(new Dimension(80, 30));
}
private void setupLayout() {
setLayout(new BorderLayout());
// Input panel
JPanel inputPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
gbc.gridx = 0; gbc.gridy = 0;
inputPanel.add(new JLabel("New Password:"), gbc);
gbc.gridx = 1;
inputPanel.add(newPasswordField, gbc);
gbc.gridx = 0; gbc.gridy = 1;
inputPanel.add(new JLabel("Confirm Password:"), gbc);
gbc.gridx = 1;
inputPanel.add(confirmPasswordField, gbc);
// Password requirement description
JPanel requirementPanel = new JPanel();
JLabel requirementLabel = new JLabel("<html>Password requirements: 6-10 characters, must include uppercase, lowercase and digits</html>");
requirementLabel.setForeground(Color.BLUE);
requirementLabel.setFont(new Font("Arial", Font.PLAIN, 12));
requirementPanel.add(requirementLabel);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
// Message panel
JPanel messagePanel = new JPanel();
messagePanel.add(messageLabel);
// Create main content panel
JPanel mainContentPanel = new JPanel();
mainContentPanel.setLayout(new BoxLayout(mainContentPanel, BoxLayout.Y_AXIS));
mainContentPanel.add(inputPanel);
mainContentPanel.add(requirementPanel);
// Create bottom panel with buttons and message
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
bottomPanel.add(buttonPanel);
bottomPanel.add(messagePanel);
// Assemble
add(mainContentPanel, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.SOUTH);
setSize(400, 300);
}
private void setupEventHandlers() {
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
changePassword();
}
});
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
// Enter key handling
confirmPasswordField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
changePassword();
}
});
}
private void changePassword() {
String newPassword = new String(newPasswordField.getPassword());
String confirmPassword = new String(confirmPasswordField.getPassword());
if (newPassword.isEmpty() || confirmPassword.isEmpty()) {
showMessage("Please fill in all fields", Color.RED);
return;
}
if (!newPassword.equals(confirmPassword)) {
showMessage("New passwords do not match", Color.RED);
return;
}
if (!isValidPassword(newPassword)) {
showMessage("New password does not meet requirements: 6-10 characters, must include uppercase, lowercase and digits", Color.RED);
return;
}
try {
UserService userService = new UserService();
String currentUser = mathquiz.gui.QuizSession.getInstance().getCurrentUser();
userService.setPassword(currentUser, newPassword);
showMessage("Password changed successfully", Color.GREEN);
// Close dialog after delay
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
timer.setRepeats(false);
timer.start();
} catch (Exception ex) {
showMessage("Password change failed: " + ex.getMessage(), Color.RED);
}
}
private boolean isValidPassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpperCase = false;
boolean hasLowerCase = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) {
hasUpperCase = true;
} else if (Character.isLowerCase(c)) {
hasLowerCase = true;
} else if (Character.isDigit(c)) {
hasDigit = true;
}
}
return hasUpperCase && hasLowerCase && hasDigit;
}
private void showMessage(String message, Color color) {
messageLabel.setText(message);
messageLabel.setForeground(color);
}
}

@ -0,0 +1,152 @@
package mathquiz.gui;
import mathquiz.AccountType;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Grade Selection Panel
*/
public class GradeSelectionPanel extends JPanel {
private final MainFrame mainFrame;
private JButton primaryButton;
private JButton middleButton;
private JButton highButton;
private JButton logoutButton;
private JButton changePasswordButton;
private JLabel messageLabel;
public GradeSelectionPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
setLayout(new BorderLayout());
primaryButton = new JButton("Primary School");
middleButton = new JButton("Middle School");
highButton = new JButton("High School");
logoutButton = new JButton("Logout");
changePasswordButton = new JButton("Change Password");
messageLabel = new JLabel("Please select your grade level", JLabel.CENTER);
messageLabel.setFont(new Font("Arial", Font.BOLD, 16));
messageLabel.setForeground(Color.BLUE);
// Set button styles
primaryButton.setPreferredSize(new Dimension(150, 60));
middleButton.setPreferredSize(new Dimension(150, 60));
highButton.setPreferredSize(new Dimension(150, 60));
logoutButton.setPreferredSize(new Dimension(120, 40));
changePasswordButton.setPreferredSize(new Dimension(150, 40));
primaryButton.setFont(new Font("Arial", Font.BOLD, 16));
middleButton.setFont(new Font("Arial", Font.BOLD, 16));
highButton.setFont(new Font("Arial", Font.BOLD, 16));
}
private void setupLayout() {
JPanel mainPanel = new JPanel(new BorderLayout());
// Title
JPanel titlePanel = new JPanel();
JLabel titleLabel = new JLabel("Math Quiz System", JLabel.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 24));
titlePanel.add(titleLabel);
// Message panel
JPanel messagePanel = new JPanel();
messagePanel.add(messageLabel);
// Grade selection panel
JPanel gradePanel = new JPanel(new GridLayout(1, 3, 20, 20));
gradePanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
gradePanel.add(primaryButton);
gradePanel.add(middleButton);
gradePanel.add(highButton);
// Bottom button panel
JPanel bottomPanel = new JPanel(new FlowLayout());
bottomPanel.add(changePasswordButton);
bottomPanel.add(logoutButton);
// Assemble
mainPanel.add(titlePanel, BorderLayout.NORTH);
mainPanel.add(messagePanel, BorderLayout.NORTH);
mainPanel.add(gradePanel, BorderLayout.CENTER);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void setupEventHandlers() {
primaryButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectGrade(AccountType.PRIMARY);
}
});
middleButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectGrade(AccountType.MIDDLE);
}
});
highButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectGrade(AccountType.HIGH);
}
});
changePasswordButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
changePassword();
}
});
logoutButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
logout();
}
});
}
private void selectGrade(AccountType grade) {
// Save selected grade to global state
QuizSession.getInstance().setAccountType(grade);
showMessage("Selected " + grade.displayName() + " grade", Color.GREEN);
// Jump to question count input interface
mainFrame.showQuestionCountPanel();
}
private void changePassword() {
// Show change password dialog
ChangePasswordDialog dialog = new ChangePasswordDialog(mainFrame);
dialog.setVisible(true);
}
private void logout() {
// Clear session information
QuizSession.getInstance().clear();
// Return to login interface
mainFrame.showLoginPanel();
}
private void showMessage(String message, Color color) {
messageLabel.setText(message);
messageLabel.setForeground(color);
}
}

@ -0,0 +1,196 @@
package mathquiz.gui;
import mathquiz.backend.UserService;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Login Panel
*/
public class LoginPanel extends JPanel {
private final MainFrame mainFrame;
private JTextField usernameField;
private JTextField emailField;
private JPasswordField passwordField;
private JButton loginButton;
private JButton registerButton;
private JLabel messageLabel;
public LoginPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
// Create components
usernameField = new JTextField(20);
emailField = new JTextField(20);
passwordField = new JPasswordField(20);
loginButton = new JButton("Login");
registerButton = new JButton("Register");
messageLabel = new JLabel("", JLabel.CENTER);
messageLabel.setForeground(Color.RED);
// Set component styles
loginButton.setPreferredSize(new Dimension(100, 35));
registerButton.setPreferredSize(new Dimension(100, 35));
}
private void setupLayout() {
setLayout(new BorderLayout());
// Title panel
JPanel titlePanel = new JPanel();
JLabel titleLabel = new JLabel("Math Quiz System", JLabel.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 28));
titleLabel.setForeground(new Color(0, 102, 204));
titlePanel.add(titleLabel);
// Main content panel with centered layout
JPanel contentPanel = new JPanel(new GridBagLayout());
contentPanel.setBackground(new Color(248, 249, 250));
contentPanel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40));
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(15, 15, 15, 15);
// Username input with centered alignment
gbc.gridx = 0; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.CENTER;
JLabel usernameLabel = new JLabel("Username:");
usernameLabel.setFont(new Font("Arial", Font.BOLD, 14));
contentPanel.add(usernameLabel, gbc);
gbc.gridx = 1; gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
usernameField.setPreferredSize(new Dimension(200, 30));
usernameField.setFont(new Font("Arial", Font.PLAIN, 14));
contentPanel.add(usernameField, gbc);
// Email input with centered alignment
gbc.gridx = 0; gbc.gridy = 1;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
JLabel emailLabel = new JLabel("Email:");
emailLabel.setFont(new Font("Arial", Font.BOLD, 14));
contentPanel.add(emailLabel, gbc);
gbc.gridx = 1; gbc.gridy = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
emailField.setPreferredSize(new Dimension(200, 30));
emailField.setFont(new Font("Arial", Font.PLAIN, 14));
contentPanel.add(emailField, gbc);
// Password input with centered alignment
gbc.gridx = 0; gbc.gridy = 2;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
JLabel passwordLabel = new JLabel("Password:");
passwordLabel.setFont(new Font("Arial", Font.BOLD, 14));
contentPanel.add(passwordLabel, gbc);
gbc.gridx = 1; gbc.gridy = 2;
gbc.fill = GridBagConstraints.HORIZONTAL;
passwordField.setPreferredSize(new Dimension(200, 30));
passwordField.setFont(new Font("Arial", Font.PLAIN, 14));
contentPanel.add(passwordField, gbc);
// Buttons panel with centered alignment
gbc.gridx = 0; gbc.gridy = 3;
gbc.gridwidth = 2;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.setOpaque(false);
buttonPanel.add(loginButton);
buttonPanel.add(registerButton);
contentPanel.add(buttonPanel, gbc);
// Message panel
gbc.gridx = 0; gbc.gridy = 4;
gbc.gridwidth = 2;
JPanel messagePanel = new JPanel();
messagePanel.setOpaque(false);
messagePanel.add(messageLabel);
contentPanel.add(messagePanel, gbc);
// Assemble main panel
add(titlePanel, BorderLayout.NORTH);
add(contentPanel, BorderLayout.CENTER);
}
private void setupEventHandlers() {
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
performLogin();
}
});
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.showRegisterPanel();
}
});
// Enter key login
passwordField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
performLogin();
}
});
}
private void performLogin() {
String username = usernameField.getText().trim();
String email = emailField.getText().trim();
String password = new String(passwordField.getPassword());
if (username.isEmpty() || email.isEmpty() || password.isEmpty()) {
showMessage("Please enter username, email and password", Color.RED);
return;
}
if (!isValidEmail(email)) {
showMessage("Please enter a valid email address", Color.RED);
return;
}
try {
UserService userService = new UserService();
if (userService.login(email, password)) {
// Set current user in session
QuizSession.getInstance().setCurrentUser(email);
showMessage("Login successful", Color.GREEN);
mainFrame.showGradeSelectionPanel();
} else {
showMessage("Email or password is incorrect", Color.RED);
}
} catch (Exception ex) {
showMessage("Login failed: " + ex.getMessage(), Color.RED);
}
}
private boolean isValidEmail(String email) {
return email.contains("@") && email.contains(".");
}
private void showMessage(String message, Color color) {
messageLabel.setText(message);
messageLabel.setForeground(color);
}
public void clearFields() {
emailField.setText("");
passwordField.setText("");
messageLabel.setText("");
}
}

@ -0,0 +1,108 @@
package mathquiz.gui;
import java.awt.*;
import javax.swing.*;
/**
* Main Window Class - Math Quiz System GUI Main Interface
*/
public class MainFrame extends JFrame {
private CardLayout cardLayout;
private JPanel mainPanel;
// Various interface panels
private LoginPanel loginPanel;
private RegisterPanel registerPanel;
private PasswordSetupPanel passwordSetupPanel;
private GradeSelectionPanel gradeSelectionPanel;
private QuestionCountPanel questionCountPanel;
private QuizPanel quizPanel;
private ResultPanel resultPanel;
public MainFrame() {
initializeFrame();
createPanels();
setupLayout();
showLoginPanel();
}
private void initializeFrame() {
setTitle("Math Quiz System");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null);
setResizable(false);
// Set UTF-8 encoding
System.setProperty("file.encoding", "UTF-8");
}
private void createPanels() {
cardLayout = new CardLayout();
mainPanel = new JPanel(cardLayout);
// Create various panels
loginPanel = new LoginPanel(this);
registerPanel = new RegisterPanel(this);
passwordSetupPanel = new PasswordSetupPanel(this);
gradeSelectionPanel = new GradeSelectionPanel(this);
questionCountPanel = new QuestionCountPanel(this);
quizPanel = new QuizPanel(this);
resultPanel = new ResultPanel(this);
// Add panels to main panel
mainPanel.add(loginPanel, "LOGIN");
mainPanel.add(registerPanel, "REGISTER");
mainPanel.add(passwordSetupPanel, "PASSWORD_SETUP");
mainPanel.add(gradeSelectionPanel, "GRADE_SELECTION");
mainPanel.add(questionCountPanel, "QUESTION_COUNT");
mainPanel.add(quizPanel, "QUIZ");
mainPanel.add(resultPanel, "RESULT");
}
private void setupLayout() {
add(mainPanel);
}
// Methods to show various panels
public void showLoginPanel() {
cardLayout.show(mainPanel, "LOGIN");
}
public void showRegisterPanel() {
cardLayout.show(mainPanel, "REGISTER");
}
public void showPasswordSetupPanel(String email) {
passwordSetupPanel.setEmail(email);
cardLayout.show(mainPanel, "PASSWORD_SETUP");
}
public void showGradeSelectionPanel() {
cardLayout.show(mainPanel, "GRADE_SELECTION");
}
public void showQuestionCountPanel() {
questionCountPanel.updateGradeDisplay();
cardLayout.show(mainPanel, "QUESTION_COUNT");
}
public void showQuizPanel() {
quizPanel.startQuiz();
cardLayout.show(mainPanel, "QUIZ");
}
public void showResultPanel(int score, int total) {
resultPanel.setScore(score, total);
cardLayout.show(mainPanel, "RESULT");
}
// Get references to various panels for use by other panels
public LoginPanel getLoginPanel() { return loginPanel; }
public RegisterPanel getRegisterPanel() { return registerPanel; }
public PasswordSetupPanel getPasswordSetupPanel() { return passwordSetupPanel; }
public GradeSelectionPanel getGradeSelectionPanel() { return gradeSelectionPanel; }
public QuestionCountPanel getQuestionCountPanel() { return questionCountPanel; }
public QuizPanel getQuizPanel() { return quizPanel; }
public ResultPanel getResultPanel() { return resultPanel; }
}

@ -0,0 +1,224 @@
package mathquiz.gui;
import mathquiz.backend.UserService;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Password Setup Panel
*/
public class PasswordSetupPanel extends JPanel {
private final MainFrame mainFrame;
private JPasswordField passwordField;
private JPasswordField confirmPasswordField;
private JButton setPasswordButton;
private JButton backButton;
private JLabel messageLabel;
private JLabel emailLabel;
private String email;
public PasswordSetupPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
setLayout(new BorderLayout());
passwordField = new JPasswordField(20);
confirmPasswordField = new JPasswordField(20);
setPasswordButton = new JButton("Set Password");
backButton = new JButton("Back");
messageLabel = new JLabel("", JLabel.CENTER);
emailLabel = new JLabel("", JLabel.CENTER);
messageLabel.setForeground(Color.RED);
emailLabel.setFont(new Font("Arial", Font.BOLD, 14));
setPasswordButton.setPreferredSize(new Dimension(120, 35));
backButton.setPreferredSize(new Dimension(100, 35));
}
private void setupLayout() {
JPanel mainPanel = new JPanel(new BorderLayout());
// Title
JPanel titlePanel = new JPanel();
JLabel titleLabel = new JLabel("Set Password", JLabel.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 20));
titlePanel.add(titleLabel);
// Email display
JPanel emailPanel = new JPanel();
emailPanel.add(emailLabel);
// Input panel
JPanel inputPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
gbc.gridx = 0; gbc.gridy = 0;
inputPanel.add(new JLabel("New Password:"), gbc);
gbc.gridx = 1;
inputPanel.add(passwordField, gbc);
gbc.gridx = 0; gbc.gridy = 1;
inputPanel.add(new JLabel("Confirm Password:"), gbc);
gbc.gridx = 1;
inputPanel.add(confirmPasswordField, gbc);
// Password requirement description
JPanel requirementPanel = new JPanel();
JLabel requirementLabel = new JLabel("<html>Password requirements: 6-10 characters, must include uppercase, lowercase and digits</html>");
requirementLabel.setForeground(Color.BLUE);
requirementPanel.add(requirementLabel);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(setPasswordButton);
buttonPanel.add(backButton);
// Message panel
JPanel messagePanel = new JPanel();
messagePanel.add(messageLabel);
// Create a proper layout structure
JPanel container = new JPanel(new BorderLayout());
// Add title at top
container.add(titlePanel, BorderLayout.NORTH);
// Create center panel with email, input, and requirements
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
centerPanel.add(emailPanel);
centerPanel.add(inputPanel);
centerPanel.add(requirementPanel);
// Add center panel to container
container.add(centerPanel, BorderLayout.CENTER);
// Create bottom panel with buttons and message
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
bottomPanel.add(buttonPanel);
bottomPanel.add(messagePanel);
// Add bottom panel to container
container.add(bottomPanel, BorderLayout.SOUTH);
add(container);
}
private void setupEventHandlers() {
setPasswordButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setPassword();
}
});
backButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.showLoginPanel();
}
});
// Enter key handling
confirmPasswordField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setPassword();
}
});
}
private void setPassword() {
String password = new String(passwordField.getPassword());
String confirmPassword = new String(confirmPasswordField.getPassword());
if (password.isEmpty() || confirmPassword.isEmpty()) {
showMessage("Please enter password", Color.RED);
return;
}
if (!password.equals(confirmPassword)) {
showMessage("Passwords do not match", Color.RED);
return;
}
if (!isValidPassword(password)) {
showMessage("Password does not meet requirements: 6-10 characters, must include uppercase, lowercase and digits", Color.RED);
return;
}
try {
UserService userService = new UserService();
userService.setPassword(email, password);
// Set user session after successful password setup
QuizSession.getInstance().setCurrentUser(email);
showMessage("Password set successfully!", Color.GREEN);
// Delay jump to grade selection interface
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.showGradeSelectionPanel();
}
});
timer.setRepeats(false);
timer.start();
} catch (Exception ex) {
showMessage("Password setup failed: " + ex.getMessage(), Color.RED);
}
}
private boolean isValidPassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpperCase = false;
boolean hasLowerCase = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) {
hasUpperCase = true;
} else if (Character.isLowerCase(c)) {
hasLowerCase = true;
} else if (Character.isDigit(c)) {
hasDigit = true;
}
}
return hasUpperCase && hasLowerCase && hasDigit;
}
private void showMessage(String message, Color color) {
messageLabel.setText(message);
messageLabel.setForeground(color);
}
public void setEmail(String email) {
this.email = email;
emailLabel.setText("Set password for " + email);
}
public void reset() {
passwordField.setText("");
confirmPasswordField.setText("");
messageLabel.setText("");
email = null;
}
}

@ -0,0 +1,152 @@
package mathquiz.gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Question Count Input Panel
*/
public class QuestionCountPanel extends JPanel {
private final MainFrame mainFrame;
private JTextField countField;
private JButton startButton;
private JButton backButton;
private JLabel messageLabel;
private JLabel gradeLabel;
public QuestionCountPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
setLayout(new BorderLayout());
countField = new JTextField(10);
startButton = new JButton("Confirm & Start Quiz");
backButton = new JButton("Back");
messageLabel = new JLabel("", JLabel.CENTER);
gradeLabel = new JLabel("", JLabel.CENTER);
messageLabel.setForeground(Color.RED);
gradeLabel.setFont(new Font("Arial", Font.BOLD, 16));
gradeLabel.setForeground(Color.BLUE);
startButton.setPreferredSize(new Dimension(120, 35));
backButton.setPreferredSize(new Dimension(100, 35));
}
private void setupLayout() {
JPanel mainPanel = new JPanel(new BorderLayout());
// Title
JPanel titlePanel = new JPanel();
JLabel titleLabel = new JLabel("Set Question Count", JLabel.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 20));
titlePanel.add(titleLabel);
// Grade display
JPanel gradePanel = new JPanel();
gradePanel.add(gradeLabel);
// Input panel
JPanel inputPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(20, 10, 20, 10);
gbc.gridx = 0; gbc.gridy = 0;
inputPanel.add(new JLabel("Question Count (10-30):"), gbc);
gbc.gridx = 1;
inputPanel.add(countField, gbc);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(startButton);
buttonPanel.add(backButton);
// Message panel
JPanel messagePanel = new JPanel();
messagePanel.add(messageLabel);
// Assemble
mainPanel.add(titlePanel, BorderLayout.NORTH);
mainPanel.add(gradePanel, BorderLayout.NORTH);
mainPanel.add(inputPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
mainPanel.add(messagePanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void setupEventHandlers() {
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
startQuiz();
}
});
backButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.showGradeSelectionPanel();
}
});
// Enter key handling
countField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
startQuiz();
}
});
}
private void startQuiz() {
String countText = countField.getText().trim();
if (countText.isEmpty()) {
showMessage("Please enter question count", Color.RED);
return;
}
try {
int count = Integer.parseInt(countText);
if (count < 10 || count > 30) {
showMessage("Question count must be between 10-30", Color.RED);
return;
}
// Save question count to session
QuizSession.getInstance().setQuestionCount(count);
// Jump to quiz interface
mainFrame.showQuizPanel();
} catch (NumberFormatException e) {
showMessage("Please enter a valid number", Color.RED);
}
}
private void showMessage(String message, Color color) {
messageLabel.setText(message);
messageLabel.setForeground(color);
}
public void updateGradeDisplay() {
QuizSession session = QuizSession.getInstance();
if (session.getAccountType() != null) {
gradeLabel.setText("Current Grade: " + ((mathquiz.AccountType) session.getAccountType()).displayName());
}
}
public void reset() {
countField.setText("");
messageLabel.setText("");
}
}

@ -0,0 +1,315 @@
package mathquiz.gui;
import mathquiz.backend.QuizService;
import mathquiz.backend.Question;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
/**
* Quiz Panel
*/
public class QuizPanel extends JPanel {
private final MainFrame mainFrame;
private JLabel questionLabel;
private JLabel progressLabel;
private JRadioButton optionA;
private JRadioButton optionB;
private JRadioButton optionC;
private JRadioButton optionD;
private ButtonGroup optionGroup;
private JButton submitButton;
private JButton quitButton;
private QuizService quizService;
private List<Question> questions;
private int currentQuestionIndex;
private int correctAnswers;
public QuizPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
setLayout(new BorderLayout());
// Set font for better math symbol support
Font mathFont = new Font("Arial Unicode MS", Font.PLAIN, 16);
Font largeFont = new Font("Arial Unicode MS", Font.BOLD, 18);
questionLabel = new JLabel("", JLabel.CENTER);
questionLabel.setFont(largeFont);
progressLabel = new JLabel("", JLabel.CENTER);
progressLabel.setFont(mathFont);
optionA = new JRadioButton();
optionA.setFont(mathFont);
optionB = new JRadioButton();
optionB.setFont(mathFont);
optionC = new JRadioButton();
optionC.setFont(mathFont);
optionD = new JRadioButton();
optionD.setFont(mathFont);
submitButton = new JButton("Submit Answer");
submitButton.setFont(mathFont);
quitButton = new JButton("Quit Quiz");
quitButton.setFont(mathFont);
// Set styles
questionLabel.setFont(new Font("Arial", Font.BOLD, 16));
progressLabel.setFont(new Font("Arial", Font.BOLD, 14));
progressLabel.setForeground(Color.BLUE);
optionA.setFont(new Font("Arial", Font.PLAIN, 14));
optionB.setFont(new Font("Arial", Font.PLAIN, 14));
optionC.setFont(new Font("Arial", Font.PLAIN, 14));
optionD.setFont(new Font("Arial", Font.PLAIN, 14));
submitButton.setPreferredSize(new Dimension(120, 35));
quitButton.setPreferredSize(new Dimension(120, 35));
// Create button group
optionGroup = new ButtonGroup();
optionGroup.add(optionA);
optionGroup.add(optionB);
optionGroup.add(optionC);
optionGroup.add(optionD);
}
private void setupLayout() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBackground(new Color(248, 249, 250));
// Progress panel with styling
JPanel progressPanel = new JPanel();
progressPanel.setBackground(new Color(0, 102, 204));
progressPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
progressLabel.setForeground(Color.WHITE);
progressPanel.add(progressLabel);
// Question panel with better styling
JPanel questionPanel = new JPanel(new BorderLayout());
questionPanel.setBackground(Color.WHITE);
questionPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(200, 200, 200), 1),
BorderFactory.createEmptyBorder(30, 30, 30, 30)
));
questionLabel.setForeground(new Color(51, 51, 51));
questionPanel.add(questionLabel, BorderLayout.CENTER);
// Options panel with better styling
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 15, 15));
optionsPanel.setBackground(Color.WHITE);
optionsPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(200, 200, 200), 1),
BorderFactory.createEmptyBorder(20, 30, 20, 30)
));
// Style radio buttons
optionA.setBackground(Color.WHITE);
optionA.setForeground(new Color(51, 51, 51));
optionB.setBackground(Color.WHITE);
optionB.setForeground(new Color(51, 51, 51));
optionC.setBackground(Color.WHITE);
optionC.setForeground(new Color(51, 51, 51));
optionD.setBackground(Color.WHITE);
optionD.setForeground(new Color(51, 51, 51));
optionsPanel.add(optionA);
optionsPanel.add(optionB);
optionsPanel.add(optionC);
optionsPanel.add(optionD);
// Button panel with styling
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.setBackground(new Color(248, 249, 250));
buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Style buttons
submitButton.setBackground(new Color(0, 102, 204));
submitButton.setForeground(Color.WHITE);
submitButton.setBorderPainted(false);
submitButton.setFocusPainted(false);
quitButton.setBackground(new Color(108, 117, 125));
quitButton.setForeground(Color.WHITE);
quitButton.setBorderPainted(false);
quitButton.setFocusPainted(false);
buttonPanel.add(submitButton);
buttonPanel.add(quitButton);
// Create center panel with question and options
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.setBackground(new Color(248, 249, 250));
centerPanel.add(questionPanel, BorderLayout.NORTH);
centerPanel.add(optionsPanel, BorderLayout.CENTER);
// Assemble
mainPanel.add(progressPanel, BorderLayout.NORTH);
mainPanel.add(centerPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void setupEventHandlers() {
submitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
submitAnswer();
}
});
quitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
quitQuiz();
}
});
}
public void startQuiz() {
try {
QuizSession session = QuizSession.getInstance();
// Debug information
System.out.println("Debug - Session info:");
System.out.println("Current User: " + session.getCurrentUser());
System.out.println("Account Type: " + session.getAccountType());
System.out.println("Question Count: " + session.getQuestionCount());
if (session.getCurrentUser() == null) {
JOptionPane.showMessageDialog(this, "User session not found. Please login again.", "Error", JOptionPane.ERROR_MESSAGE);
mainFrame.showLoginPanel();
return;
}
if (session.getAccountType() == null) {
JOptionPane.showMessageDialog(this, "Account type not set. Please select grade again.", "Error", JOptionPane.ERROR_MESSAGE);
mainFrame.showGradeSelectionPanel();
return;
}
quizService = new QuizService();
questions = quizService.generateQuestions((mathquiz.AccountType) session.getAccountType(), session.getQuestionCount());
if (questions == null || questions.isEmpty()) {
JOptionPane.showMessageDialog(this, "No questions generated. Please try again.", "Error", JOptionPane.ERROR_MESSAGE);
mainFrame.showQuestionCountPanel();
return;
}
currentQuestionIndex = 0;
correctAnswers = 0;
displayCurrentQuestion();
} catch (Exception e) {
System.err.println("Quiz generation error: " + e.getMessage());
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Failed to generate questions: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
mainFrame.showQuestionCountPanel();
}
}
private void displayCurrentQuestion() {
if (currentQuestionIndex >= questions.size()) {
// Quiz ended, show result
showResult();
return;
}
Question question = questions.get(currentQuestionIndex);
// Update progress
progressLabel.setText("Question " + (currentQuestionIndex + 1) + " of " + questions.size());
// Display question with proper font and HTML rendering
String questionText = question.getQuestion();
// Convert ASCII symbols to proper math symbols for display
questionText = questionText.replace("*", "×").replace("/", "÷");
questionLabel.setText("<html><div style='text-align: center; font-family: Arial Unicode MS, Arial, sans-serif; font-size: 18px;'>" + questionText + "</div></html>");
// Display options
optionA.setText("A. " + question.getOptionA());
optionB.setText("B. " + question.getOptionB());
optionC.setText("C. " + question.getOptionC());
optionD.setText("D. " + question.getOptionD());
// Clear selection
optionGroup.clearSelection();
// Update button state
submitButton.setEnabled(true);
}
private void submitAnswer() {
if (questions == null || questions.isEmpty()) {
JOptionPane.showMessageDialog(this, "No questions available. Please try again.", "Error", JOptionPane.ERROR_MESSAGE);
mainFrame.showQuestionCountPanel();
return;
}
if (!optionA.isSelected() && !optionB.isSelected() &&
!optionC.isSelected() && !optionD.isSelected()) {
JOptionPane.showMessageDialog(this, "Please select an answer", "Hint", JOptionPane.WARNING_MESSAGE);
return;
}
if (currentQuestionIndex >= questions.size()) {
JOptionPane.showMessageDialog(this, "Question index out of bounds. Please try again.", "Error", JOptionPane.ERROR_MESSAGE);
mainFrame.showQuestionCountPanel();
return;
}
Question question = questions.get(currentQuestionIndex);
String selectedAnswer = getSelectedAnswer();
// Check if answer is correct
if (selectedAnswer.equals(question.getCorrectAnswer())) {
correctAnswers++;
}
// Next question
currentQuestionIndex++;
displayCurrentQuestion();
}
private String getSelectedAnswer() {
if (optionA.isSelected()) return "A";
if (optionB.isSelected()) return "B";
if (optionC.isSelected()) return "C";
if (optionD.isSelected()) return "D";
return "";
}
private void showResult() {
int totalQuestions = questions.size();
int score = correctAnswers; // 答对的题目数量
System.out.println("Quiz completed:");
System.out.println("Correct answers: " + correctAnswers);
System.out.println("Total questions: " + totalQuestions);
System.out.println("Score: " + score + " out of " + totalQuestions);
mainFrame.showResultPanel(score, totalQuestions);
}
private void quitQuiz() {
int result = JOptionPane.showConfirmDialog(this,
"Are you sure you want to quit the quiz?", "Confirm Quit",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.YES_OPTION) {
mainFrame.showGradeSelectionPanel();
}
}
}

@ -0,0 +1,56 @@
package mathquiz.gui;
import mathquiz.AccountType;
/**
* Quiz Session Management Class
* Singleton pattern, manages current user session information
*/
public class QuizSession {
private static QuizSession instance;
private String currentUser;
private AccountType accountType;
private int questionCount;
private QuizSession() {
// Private constructor
}
public static QuizSession getInstance() {
if (instance == null) {
instance = new QuizSession();
}
return instance;
}
public String getCurrentUser() {
return currentUser;
}
public void setCurrentUser(String currentUser) {
this.currentUser = currentUser;
}
public AccountType getAccountType() {
return accountType;
}
public void setAccountType(AccountType accountType) {
this.accountType = accountType;
}
public int getQuestionCount() {
return questionCount;
}
public void setQuestionCount(int questionCount) {
this.questionCount = questionCount;
}
public void clear() {
currentUser = null;
accountType = null;
questionCount = 0;
}
}

@ -0,0 +1,233 @@
package mathquiz.gui;
import mathquiz.backend.EmailService;
import mathquiz.backend.UserService;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Registration Panel
*/
public class RegisterPanel extends JPanel {
private final MainFrame mainFrame;
private JTextField usernameField;
private JTextField emailField;
private JTextField verificationCodeField;
private JButton sendCodeButton;
private JButton registerButton;
private JButton backButton;
private JLabel messageLabel;
private JLabel statusLabel;
private String currentEmail;
private String sentVerificationCode;
public RegisterPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
setLayout(new BorderLayout());
usernameField = new JTextField(20);
emailField = new JTextField(20);
verificationCodeField = new JTextField(20);
sendCodeButton = new JButton("Send Code");
registerButton = new JButton("Complete Registration");
backButton = new JButton("Back to Login");
messageLabel = new JLabel("", JLabel.CENTER);
statusLabel = new JLabel("Please enter username and email address to get verification code", JLabel.CENTER);
messageLabel.setForeground(Color.RED);
statusLabel.setForeground(Color.BLUE);
// Set component styles
sendCodeButton.setPreferredSize(new Dimension(120, 35));
registerButton.setPreferredSize(new Dimension(150, 35));
backButton.setPreferredSize(new Dimension(120, 35));
}
private void setupLayout() {
JPanel mainPanel = new JPanel(new BorderLayout());
// Title
JPanel titlePanel = new JPanel();
JLabel titleLabel = new JLabel("User Registration", JLabel.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 20));
titlePanel.add(titleLabel);
// Input panel
JPanel inputPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
gbc.gridx = 0; gbc.gridy = 0;
inputPanel.add(new JLabel("Username:"), gbc);
gbc.gridx = 1;
inputPanel.add(usernameField, gbc);
gbc.gridx = 2;
inputPanel.add(new JLabel(""), gbc); // Empty space for alignment
gbc.gridx = 0; gbc.gridy = 1;
inputPanel.add(new JLabel("Email Address:"), gbc);
gbc.gridx = 1;
inputPanel.add(emailField, gbc);
gbc.gridx = 2;
inputPanel.add(sendCodeButton, gbc);
gbc.gridx = 0; gbc.gridy = 2;
inputPanel.add(new JLabel("Verification Code:"), gbc);
gbc.gridx = 1;
inputPanel.add(verificationCodeField, gbc);
gbc.gridx = 2;
inputPanel.add(registerButton, gbc);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(backButton);
// Message panel
JPanel messagePanel = new JPanel(new BorderLayout());
messagePanel.add(statusLabel, BorderLayout.NORTH);
messagePanel.add(messageLabel, BorderLayout.SOUTH);
// Assemble
mainPanel.add(titlePanel, BorderLayout.NORTH);
mainPanel.add(inputPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
mainPanel.add(messagePanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void setupEventHandlers() {
sendCodeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sendVerificationCode();
}
});
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
completeRegistration();
}
});
backButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainFrame.showLoginPanel();
}
});
// Enter key handling
verificationCodeField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
completeRegistration();
}
});
}
private void sendVerificationCode() {
String username = usernameField.getText().trim();
String email = emailField.getText().trim();
if (username.isEmpty()) {
showMessage("Please enter username", Color.RED);
return;
}
if (email.isEmpty()) {
showMessage("Please enter email address", Color.RED);
return;
}
if (!isValidEmail(email)) {
showMessage("Please enter a valid email address", Color.RED);
return;
}
try {
EmailService emailService = new EmailService();
String verificationCode = emailService.sendVerificationCode(email);
currentEmail = email;
sentVerificationCode = verificationCode;
showMessage("Verification code has been sent to your email, please check", Color.GREEN);
statusLabel.setText("Verification code sent, please enter code to complete registration");
statusLabel.setForeground(Color.GREEN);
// Disable send button, enable register button
sendCodeButton.setEnabled(false);
registerButton.setEnabled(true);
verificationCodeField.setEnabled(true);
} catch (Exception ex) {
showMessage("Failed to send verification code: " + ex.getMessage(), Color.RED);
}
}
private void completeRegistration() {
String username = usernameField.getText().trim();
String email = emailField.getText().trim();
String code = verificationCodeField.getText().trim();
if (username.isEmpty() || email.isEmpty() || code.isEmpty()) {
showMessage("Please enter username, email and verification code", Color.RED);
return;
}
if (!email.equals(currentEmail)) {
showMessage("Email address does not match", Color.RED);
return;
}
if (!code.equals(sentVerificationCode)) {
showMessage("Verification code is incorrect", Color.RED);
return;
}
try {
UserService userService = new UserService();
userService.registerUser(email, username);
showMessage("Registration successful! Please set password", Color.GREEN);
mainFrame.showPasswordSetupPanel(email);
} catch (Exception ex) {
showMessage("Registration failed: " + ex.getMessage(), Color.RED);
}
}
private boolean isValidEmail(String email) {
return email.contains("@") && email.contains(".");
}
private void showMessage(String message, Color color) {
messageLabel.setText(message);
messageLabel.setForeground(color);
}
public void reset() {
emailField.setText("");
verificationCodeField.setText("");
messageLabel.setText("");
statusLabel.setText("Please enter email address to get verification code");
statusLabel.setForeground(Color.BLUE);
sendCodeButton.setEnabled(true);
registerButton.setEnabled(false);
verificationCodeField.setEnabled(false);
currentEmail = null;
sentVerificationCode = null;
}
}

@ -0,0 +1,126 @@
package mathquiz.gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Result Panel
*/
public class ResultPanel extends JPanel {
private final MainFrame mainFrame;
private JLabel scoreLabel;
private JLabel messageLabel;
private JButton continueButton;
private JButton exitButton;
public ResultPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeComponents();
setupLayout();
setupEventHandlers();
}
private void initializeComponents() {
setLayout(new BorderLayout());
scoreLabel = new JLabel("", JLabel.CENTER);
messageLabel = new JLabel("", JLabel.CENTER);
continueButton = new JButton("Continue Quiz");
exitButton = new JButton("Logout");
// Set styles
scoreLabel.setFont(new Font("Arial", Font.BOLD, 24));
messageLabel.setFont(new Font("Arial", Font.PLAIN, 16));
continueButton.setPreferredSize(new Dimension(120, 40));
exitButton.setPreferredSize(new Dimension(120, 40));
}
private void setupLayout() {
JPanel mainPanel = new JPanel(new BorderLayout());
// Title
JPanel titlePanel = new JPanel();
JLabel titleLabel = new JLabel("Quiz Results", JLabel.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 20));
titlePanel.add(titleLabel);
// Score display panel
JPanel scorePanel = new JPanel(new BorderLayout());
scorePanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
scorePanel.add(scoreLabel, BorderLayout.CENTER);
scorePanel.add(messageLabel, BorderLayout.SOUTH);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(continueButton);
buttonPanel.add(exitButton);
// Assemble
mainPanel.add(titlePanel, BorderLayout.NORTH);
mainPanel.add(scorePanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
}
private void setupEventHandlers() {
continueButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
continueQuiz();
}
});
exitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
exitQuiz();
}
});
}
public void setScore(int score, int totalQuestions) {
// Calculate percentage
int percentage = (int) Math.round((double) score / totalQuestions * 100);
// Display score with percentage (without % symbol)
scoreLabel.setText("Your Score: " + score + "/" + totalQuestions + " (" + percentage + ")");
// Display different messages based on percentage
String message;
Color color;
if (percentage >= 90) {
message = "Excellent! You answered " + score + " out of " + totalQuestions + " questions correctly";
color = Color.GREEN;
} else if (percentage >= 80) {
message = "Good! You answered " + score + " out of " + totalQuestions + " questions correctly";
color = Color.BLUE;
} else if (percentage >= 70) {
message = "Pass! You answered " + score + " out of " + totalQuestions + " questions correctly";
color = Color.ORANGE;
} else {
message = "Need improvement! You answered " + score + " out of " + totalQuestions + " questions correctly";
color = Color.RED;
}
messageLabel.setText(message);
messageLabel.setForeground(color);
}
private void continueQuiz() {
// Return to grade selection interface
mainFrame.showGradeSelectionPanel();
}
private void exitQuiz() {
// Clear session information
QuizSession.getInstance().clear();
// Return to login interface
mainFrame.showLoginPanel();
}
}
Loading…
Cancel
Save