Compare commits
4 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
0464ec4599 | 6 months ago |
|
|
192a32695e | 6 months ago |
|
|
bdfb3f0be7 | 6 months ago |
|
|
c93b6e59c6 | 6 months ago |
@ -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,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,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…
Reference in new issue