docs: Added some javadoc
This commit is contained in:
@@ -19,8 +19,22 @@ import java.util.Optional;
|
|||||||
import cz.jzitnik.query.QueryClient;
|
import cz.jzitnik.query.QueryClient;
|
||||||
import cz.jzitnik.query.QueryOptions;
|
import cz.jzitnik.query.QueryOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for the JecnaClient desktop application.
|
||||||
|
* This class initializes the application's UI theme, sets up the navigation router,
|
||||||
|
* and handles the initial application state, including attempting to restore
|
||||||
|
* a user's session from previously saved credentials.
|
||||||
|
*/
|
||||||
public class Main extends Application {
|
public class Main extends Application {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the JavaFX application, configures the UI, and sets up the routing system.
|
||||||
|
* It attempts to automatically log the user in if stored credentials are available,
|
||||||
|
* navigating to the dashboard on success or to the login screen if authentication fails.
|
||||||
|
*
|
||||||
|
* @param stage the primary window for the application.
|
||||||
|
* @throws Exception if an error occurs during initialization.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws Exception {
|
public void start(Stage stage) throws Exception {
|
||||||
Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet());
|
Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet());
|
||||||
@@ -77,6 +91,11 @@ public class Main extends Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard entry point for launching the JavaFX application.
|
||||||
|
*
|
||||||
|
* @param args command-line arguments passed to the application.
|
||||||
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
launch(args);
|
launch(args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
/*
|
|
||||||
AI GENERATED SLOP
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cz.jzitnik.auth;
|
package cz.jzitnik.auth;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
@@ -19,6 +15,9 @@ import java.util.EnumSet;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Securely stores and retrieves user credentials using AES-GCM encryption.
|
||||||
|
*/
|
||||||
public class CredentialStore {
|
public class CredentialStore {
|
||||||
private static final Path DIR = Paths.get(System.getProperty("user.home"), ".jecnaclient");
|
private static final Path DIR = Paths.get(System.getProperty("user.home"), ".jecnaclient");
|
||||||
private static final Path KEY_FILE = DIR.resolve("secret.key");
|
private static final Path KEY_FILE = DIR.resolve("secret.key");
|
||||||
@@ -26,16 +25,32 @@ public class CredentialStore {
|
|||||||
private static final int KEY_SIZE = 256;
|
private static final int KEY_SIZE = 256;
|
||||||
private static final SecureRandom RAND = new SecureRandom();
|
private static final SecureRandom RAND = new SecureRandom();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents user credentials (username and password).
|
||||||
|
*/
|
||||||
public static final class Credentials {
|
public static final class Credentials {
|
||||||
public final String username;
|
public final String username;
|
||||||
public final String password;
|
public final String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Credentials object.
|
||||||
|
*
|
||||||
|
* @param u The username.
|
||||||
|
* @param p The password.
|
||||||
|
*/
|
||||||
public Credentials(String u, String p) {
|
public Credentials(String u, String p) {
|
||||||
this.username = u;
|
this.username = u;
|
||||||
this.password = p;
|
this.password = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves user credentials to a secure file.
|
||||||
|
*
|
||||||
|
* @param username The username to save.
|
||||||
|
* @param password The password to save.
|
||||||
|
* @throws Exception If an error occurs during saving.
|
||||||
|
*/
|
||||||
public static void saveCredentials(String username, String password) throws Exception {
|
public static void saveCredentials(String username, String password) throws Exception {
|
||||||
if (username == null || password == null) return;
|
if (username == null || password == null) return;
|
||||||
ensureDir();
|
ensureDir();
|
||||||
@@ -60,6 +75,12 @@ public class CredentialStore {
|
|||||||
trySetOwnerOnly(CRED_FILE);
|
trySetOwnerOnly(CRED_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads user credentials from the secure file.
|
||||||
|
*
|
||||||
|
* @return An Optional containing the credentials if found and decrypted successfully, or empty otherwise.
|
||||||
|
* @throws Exception If an error occurs during loading or decryption.
|
||||||
|
*/
|
||||||
public static Optional<Credentials> loadCredentials() throws Exception {
|
public static Optional<Credentials> loadCredentials() throws Exception {
|
||||||
if (!Files.exists(CRED_FILE)) return Optional.empty();
|
if (!Files.exists(CRED_FILE)) return Optional.empty();
|
||||||
if (!Files.exists(KEY_FILE)) return Optional.empty();
|
if (!Files.exists(KEY_FILE)) return Optional.empty();
|
||||||
@@ -87,12 +108,18 @@ public class CredentialStore {
|
|||||||
return Optional.of(new Credentials(u, p));
|
return Optional.of(new Credentials(u, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears stored credentials by deleting the encrypted file.
|
||||||
|
*/
|
||||||
public static void clearCredentials() {
|
public static void clearCredentials() {
|
||||||
try {
|
try {
|
||||||
Files.deleteIfExists(CRED_FILE);
|
Files.deleteIfExists(CRED_FILE);
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures the credentials directory exists with appropriate permissions.
|
||||||
|
*/
|
||||||
private static void ensureDir() throws IOException {
|
private static void ensureDir() throws IOException {
|
||||||
if (!Files.exists(DIR)) {
|
if (!Files.exists(DIR)) {
|
||||||
Files.createDirectories(DIR);
|
Files.createDirectories(DIR);
|
||||||
@@ -100,6 +127,9 @@ public class CredentialStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the encryption key or creates a new one if it doesn't exist.
|
||||||
|
*/
|
||||||
private static SecretKey loadOrCreateKey() throws Exception {
|
private static SecretKey loadOrCreateKey() throws Exception {
|
||||||
if (Files.exists(KEY_FILE)) {
|
if (Files.exists(KEY_FILE)) {
|
||||||
byte[] k = Files.readAllBytes(KEY_FILE);
|
byte[] k = Files.readAllBytes(KEY_FILE);
|
||||||
@@ -113,6 +143,9 @@ public class CredentialStore {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-effort attempt to set POSIX file permissions to owner-only.
|
||||||
|
*/
|
||||||
private static void trySetOwnerOnly(Path p) {
|
private static void trySetOwnerOnly(Path p) {
|
||||||
try {
|
try {
|
||||||
// POSIX systems
|
// POSIX systems
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the absences view.
|
||||||
|
* Displays student absences, including a summary and detailed breakdown by date.
|
||||||
|
*/
|
||||||
@Route(path = "/absences", fxml = "/absences.fxml")
|
@Route(path = "/absences", fxml = "/absences.fxml")
|
||||||
public class AbsencesController extends DashboardBaseController {
|
public class AbsencesController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -35,12 +39,22 @@ public class AbsencesController extends DashboardBaseController {
|
|||||||
|
|
||||||
private AbsencesPage currentPage;
|
private AbsencesPage currentPage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the controller when navigating to this view.
|
||||||
|
* Initiates loading of absence data.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties, not explicitly used here.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
loadAbsences();
|
loadAbsences();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the absences page from the API and updates the UI.
|
||||||
|
* Shows a loading indicator while fetching.
|
||||||
|
*/
|
||||||
private void loadAbsences() {
|
private void loadAbsences() {
|
||||||
loadingIndicator.setVisible(true);
|
loadingIndicator.setVisible(true);
|
||||||
scrollPane.setVisible(false);
|
scrollPane.setVisible(false);
|
||||||
@@ -57,6 +71,11 @@ public class AbsencesController extends DashboardBaseController {
|
|||||||
}, QueryOptions.defaultOptions()).thenAccept(this::handleAbsencesResult);
|
}, QueryOptions.defaultOptions()).thenAccept(this::handleAbsencesResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the result of the absences fetch operation.
|
||||||
|
*
|
||||||
|
* @param result The result of the fetch.
|
||||||
|
*/
|
||||||
private void handleAbsencesResult(QueryResult<AbsencesPage> result) {
|
private void handleAbsencesResult(QueryResult<AbsencesPage> result) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
loadingIndicator.setVisible(false);
|
loadingIndicator.setVisible(false);
|
||||||
@@ -75,6 +94,9 @@ public class AbsencesController extends DashboardBaseController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the absence summary and detail cards.
|
||||||
|
*/
|
||||||
private void renderAbsences() {
|
private void renderAbsences() {
|
||||||
summaryBox.getChildren().clear();
|
summaryBox.getChildren().clear();
|
||||||
detailFlow.getChildren().clear();
|
detailFlow.getChildren().clear();
|
||||||
@@ -111,6 +133,14 @@ public class AbsencesController extends DashboardBaseController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a summary card UI component.
|
||||||
|
*
|
||||||
|
* @param title The title of the summary.
|
||||||
|
* @param count The numerical value to display.
|
||||||
|
* @param styleClass The CSS style class to apply for coloring.
|
||||||
|
* @return The created VBox component.
|
||||||
|
*/
|
||||||
private VBox createSummaryCard(String title, int count, String styleClass) {
|
private VBox createSummaryCard(String title, int count, String styleClass) {
|
||||||
VBox card = new VBox(5);
|
VBox card = new VBox(5);
|
||||||
card.setAlignment(Pos.CENTER);
|
card.setAlignment(Pos.CENTER);
|
||||||
@@ -126,6 +156,13 @@ public class AbsencesController extends DashboardBaseController {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an absence detail card UI component for a specific day.
|
||||||
|
*
|
||||||
|
* @param day The date of the absence.
|
||||||
|
* @param info The absence information for the day.
|
||||||
|
* @return The created VBox component.
|
||||||
|
*/
|
||||||
private VBox createAbsenceCard(LocalDate day, AbsenceInfo info) {
|
private VBox createAbsenceCard(LocalDate day, AbsenceInfo info) {
|
||||||
VBox card = new VBox(8);
|
VBox card = new VBox(8);
|
||||||
card.getStyleClass().addAll("card", "absence-card");
|
card.getStyleClass().addAll("card", "absence-card");
|
||||||
@@ -162,11 +199,20 @@ public class AbsencesController extends DashboardBaseController {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a date for display.
|
||||||
|
*
|
||||||
|
* @param date The date to format.
|
||||||
|
* @return The formatted date string.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private String formatDate(LocalDate date) {
|
private String formatDate(LocalDate date) {
|
||||||
return date.getDayOfMonth() + ". " + date.getMonthNumber() + ". " + date.getYear();
|
return date.getDayOfMonth() + ". " + date.getMonthNumber() + ". " + date.getYear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the dashboard.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBackToDashboard() {
|
protected void onBackToDashboard() {
|
||||||
Router.getInstance().navigate("/dashboard");
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ import javafx.scene.layout.*;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller responsible for displaying detailed information about a specific classroom.
|
||||||
|
* This includes fetching room data from the Jecna API and rendering the room's details,
|
||||||
|
* such as its name, code, floor, and timetable.
|
||||||
|
*/
|
||||||
@Route(path = "/classroom_detail", fxml = "/classroom_detail.fxml")
|
@Route(path = "/classroom_detail", fxml = "/classroom_detail.fxml")
|
||||||
public class ClassroomDetailController extends DashboardBaseController {
|
public class ClassroomDetailController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -32,6 +37,11 @@ public class ClassroomDetailController extends DashboardBaseController {
|
|||||||
|
|
||||||
private String roomCode;
|
private String roomCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the view with the specified room code.
|
||||||
|
*
|
||||||
|
* @param props a map containing the "code" of the room to display.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
@@ -45,6 +55,12 @@ public class ClassroomDetailController extends DashboardBaseController {
|
|||||||
loadRoom();
|
loadRoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
@Override
|
||||||
|
protected void onBack() {
|
||||||
|
Router.getInstance().navigate("/classrooms");
|
||||||
|
}
|
||||||
|
|
||||||
private void loadRoom() {
|
private void loadRoom() {
|
||||||
loadingIndicator.setVisible(true);
|
loadingIndicator.setVisible(true);
|
||||||
scrollPane.setVisible(false);
|
scrollPane.setVisible(false);
|
||||||
@@ -121,10 +137,4 @@ public class ClassroomDetailController extends DashboardBaseController {
|
|||||||
|
|
||||||
return row + 1;
|
return row + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
|
||||||
@Override
|
|
||||||
protected void onBack() {
|
|
||||||
Router.getInstance().navigate("/classrooms");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the classrooms view, which displays a list of available classrooms.
|
||||||
|
* Users can search and filter classrooms, and clicking a classroom navigates
|
||||||
|
* to its detailed view.
|
||||||
|
*/
|
||||||
@Route(path = "/classrooms", fxml = "/classrooms.fxml")
|
@Route(path = "/classrooms", fxml = "/classrooms.fxml")
|
||||||
public class ClassroomsController extends DashboardBaseController {
|
public class ClassroomsController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -33,6 +38,11 @@ public class ClassroomsController extends DashboardBaseController {
|
|||||||
|
|
||||||
private List<RoomReference> allRooms = new ArrayList<>();
|
private List<RoomReference> allRooms = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the view, sets up the search filter, and triggers loading of the classrooms.
|
||||||
|
*
|
||||||
|
* @param props navigation properties.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
@@ -115,6 +125,9 @@ public class ClassroomsController extends DashboardBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates back to the dashboard.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBackToDashboard() {
|
protected void onBackToDashboard() {
|
||||||
Router.getInstance().navigate("/dashboard");
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import cz.jzitnik.router.Router;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base controller for dashboard views.
|
||||||
|
* Handles common functionality such as navigation and loading user profile information.
|
||||||
|
*/
|
||||||
public class DashboardBaseController implements Routable {
|
public class DashboardBaseController implements Routable {
|
||||||
|
|
||||||
@InjectState
|
@InjectState
|
||||||
@@ -48,9 +52,18 @@ public class DashboardBaseController implements Routable {
|
|||||||
protected void onDoNothing() {
|
protected void onDoNothing() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the home view.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBack() { Router.getInstance().navigate("/home"); }
|
protected void onBack() { Router.getInstance().navigate("/home"); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when navigating to a dashboard view.
|
||||||
|
* Initializes the welcome and class labels and fetches user profile data.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
if (welcomeLabel != null && classLabel != null) {
|
if (welcomeLabel != null && classLabel != null) {
|
||||||
|
|||||||
@@ -7,14 +7,26 @@ import javafx.fxml.FXML;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the main dashboard view.
|
||||||
|
*/
|
||||||
@Route(path = "/dashboard", fxml = "/dashboard_modern.fxml")
|
@Route(path = "/dashboard", fxml = "/dashboard_modern.fxml")
|
||||||
public class DashboardController extends DashboardBaseController {
|
public class DashboardController extends DashboardBaseController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when navigating to the dashboard view.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the logout action.
|
||||||
|
* Clears application state, credentials, and navigates back to the login screen.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public void onLogoutClick() {
|
public void onLogoutClick() {
|
||||||
appState.clear();
|
appState.clear();
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the grades view.
|
||||||
|
* Displays student grades grouped by subject and allows grade prediction.
|
||||||
|
*/
|
||||||
@Route(path = "/grades", fxml = "/grades.fxml")
|
@Route(path = "/grades", fxml = "/grades.fxml")
|
||||||
public class GradesController extends DashboardBaseController {
|
public class GradesController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -35,8 +39,20 @@ public class GradesController extends DashboardBaseController {
|
|||||||
private GradesPage currentPage;
|
private GradesPage currentPage;
|
||||||
private final Map<String, List<PredictedGrade>> predictions = new HashMap<>();
|
private final Map<String, List<PredictedGrade>> predictions = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a predicted grade.
|
||||||
|
*
|
||||||
|
* @param value The grade value (1-5).
|
||||||
|
* @param isSmall Whether the grade has a smaller weight.
|
||||||
|
*/
|
||||||
public record PredictedGrade(int value, boolean isSmall) {}
|
public record PredictedGrade(int value, boolean isSmall) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the controller when navigating to this view.
|
||||||
|
* Clears previous predictions and initiates loading of grades data.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties, not explicitly used here.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
@@ -44,6 +60,10 @@ public class GradesController extends DashboardBaseController {
|
|||||||
loadGrades();
|
loadGrades();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the grades page from the API and updates the UI.
|
||||||
|
* Shows a loading indicator while fetching.
|
||||||
|
*/
|
||||||
private void loadGrades() {
|
private void loadGrades() {
|
||||||
loadingIndicator.setVisible(true);
|
loadingIndicator.setVisible(true);
|
||||||
scrollPane.setVisible(false);
|
scrollPane.setVisible(false);
|
||||||
@@ -59,6 +79,11 @@ public class GradesController extends DashboardBaseController {
|
|||||||
}, QueryOptions.defaultOptions()).thenAccept(this::handleGradesResult);
|
}, QueryOptions.defaultOptions()).thenAccept(this::handleGradesResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the result of the grades fetch operation.
|
||||||
|
*
|
||||||
|
* @param result The result of the fetch.
|
||||||
|
*/
|
||||||
private void handleGradesResult(QueryResult<GradesPage> result) {
|
private void handleGradesResult(QueryResult<GradesPage> result) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
loadingIndicator.setVisible(false);
|
loadingIndicator.setVisible(false);
|
||||||
@@ -78,6 +103,9 @@ public class GradesController extends DashboardBaseController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders all subjects and their grades.
|
||||||
|
*/
|
||||||
private void renderAllSubjects() {
|
private void renderAllSubjects() {
|
||||||
contentBox.getChildren().clear();
|
contentBox.getChildren().clear();
|
||||||
contentBox.setAlignment(Pos.TOP_CENTER);
|
contentBox.setAlignment(Pos.TOP_CENTER);
|
||||||
@@ -95,6 +123,13 @@ public class GradesController extends DashboardBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the average grade for a subject, including predictions.
|
||||||
|
*
|
||||||
|
* @param subject The subject.
|
||||||
|
* @param preds List of predicted grades.
|
||||||
|
* @return The calculated average, or null if no grades.
|
||||||
|
*/
|
||||||
private Float calculateAverage(Subject subject, List<PredictedGrade> preds) {
|
private Float calculateAverage(Subject subject, List<PredictedGrade> preds) {
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
float count = 0;
|
float count = 0;
|
||||||
@@ -122,6 +157,12 @@ public class GradesController extends DashboardBaseController {
|
|||||||
return count > 0 ? sum / count : null;
|
return count > 0 ? sum / count : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a subject card UI component.
|
||||||
|
*
|
||||||
|
* @param subject The subject.
|
||||||
|
* @return The created VBox component.
|
||||||
|
*/
|
||||||
private VBox createSubjectCard(Subject subject) {
|
private VBox createSubjectCard(Subject subject) {
|
||||||
String subjectNameFull = subject.getName().getFull();
|
String subjectNameFull = subject.getName().getFull();
|
||||||
List<PredictedGrade> preds = predictions.getOrDefault(subjectNameFull, new ArrayList<>());
|
List<PredictedGrade> preds = predictions.getOrDefault(subjectNameFull, new ArrayList<>());
|
||||||
@@ -208,6 +249,12 @@ public class GradesController extends DashboardBaseController {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a dialog to add a predicted grade.
|
||||||
|
*
|
||||||
|
* @param subjectNameFull Full subject name.
|
||||||
|
* @param preds List of existing predictions for the subject.
|
||||||
|
*/
|
||||||
private void showPredictionDialog(String subjectNameFull, List<PredictedGrade> preds) {
|
private void showPredictionDialog(String subjectNameFull, List<PredictedGrade> preds) {
|
||||||
Dialog<PredictedGrade> dialog = new Dialog<>();
|
Dialog<PredictedGrade> dialog = new Dialog<>();
|
||||||
dialog.setTitle("Nová predikce");
|
dialog.setTitle("Nová predikce");
|
||||||
@@ -247,6 +294,18 @@ public class GradesController extends DashboardBaseController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a grade badge UI component.
|
||||||
|
*
|
||||||
|
* @param value The grade value.
|
||||||
|
* @param isSmall Whether it's a small grade.
|
||||||
|
* @param valueChar The character representation of the grade (if not numeric).
|
||||||
|
* @param isPredicted Whether this is a predicted grade.
|
||||||
|
* @param desc Description of the grade.
|
||||||
|
* @param teacher Teacher who gave the grade.
|
||||||
|
* @param date Date the grade was received.
|
||||||
|
* @return The created StackPane component.
|
||||||
|
*/
|
||||||
private StackPane createGradeBadge(int value, boolean isSmall, char valueChar, boolean isPredicted, String desc, String teacher, String date) {
|
private StackPane createGradeBadge(int value, boolean isSmall, char valueChar, boolean isPredicted, String desc, String teacher, String date) {
|
||||||
StackPane badge = new StackPane();
|
StackPane badge = new StackPane();
|
||||||
String valStr = String.valueOf(valueChar);
|
String valStr = String.valueOf(valueChar);
|
||||||
@@ -302,6 +361,13 @@ public class GradesController extends DashboardBaseController {
|
|||||||
return badge;
|
return badge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a dialog with grade details.
|
||||||
|
*
|
||||||
|
* @param desc Description of the grade.
|
||||||
|
* @param teacher Teacher who gave the grade.
|
||||||
|
* @param date Date the grade was received.
|
||||||
|
*/
|
||||||
private void showGradeDetailsDialog(String desc, String teacher, String date) {
|
private void showGradeDetailsDialog(String desc, String teacher, String date) {
|
||||||
Dialog<Void> dialog = new Dialog<>();
|
Dialog<Void> dialog = new Dialog<>();
|
||||||
dialog.setTitle("Detail známky");
|
dialog.setTitle("Detail známky");
|
||||||
@@ -325,6 +391,9 @@ public class GradesController extends DashboardBaseController {
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the dashboard.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBackToDashboard() {
|
protected void onBackToDashboard() {
|
||||||
Router.getInstance().navigate("/dashboard");
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import javafx.scene.control.Label;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the home view.
|
||||||
|
*/
|
||||||
@Route(path = "/home", fxml = "/home.fxml")
|
@Route(path = "/home", fxml = "/home.fxml")
|
||||||
public class HomeController implements Routable {
|
public class HomeController implements Routable {
|
||||||
|
|
||||||
@@ -19,6 +22,12 @@ public class HomeController implements Routable {
|
|||||||
@FXML
|
@FXML
|
||||||
private Label welcomeLabel;
|
private Label welcomeLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when navigating to the home view.
|
||||||
|
* Updates the welcome label with the username.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
String username = appState.getUsername();
|
String username = appState.getUsername();
|
||||||
@@ -27,6 +36,9 @@ public class HomeController implements Routable {
|
|||||||
welcomeLabel.setText("Welcome, " + username + "!");
|
welcomeLabel.setText("Welcome, " + username + "!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the logout action.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public void onLogoutClick() {
|
public void onLogoutClick() {
|
||||||
appState.clear();
|
appState.clear();
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import javafx.scene.control.*;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the login view.
|
||||||
|
*/
|
||||||
@Route(path = "/login", fxml = "/login.fxml")
|
@Route(path = "/login", fxml = "/login.fxml")
|
||||||
public class LoginController implements Routable {
|
public class LoginController implements Routable {
|
||||||
|
|
||||||
@@ -29,10 +32,18 @@ public class LoginController implements Routable {
|
|||||||
@FXML
|
@FXML
|
||||||
private ProgressIndicator loadingIndicator;
|
private ProgressIndicator loadingIndicator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when navigating to the login view.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the login action. Initiates the login process in a background thread.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public void onLoginClick() {
|
public void onLoginClick() {
|
||||||
final String username = usernameField.getText();
|
final String username = usernameField.getText();
|
||||||
@@ -63,6 +74,9 @@ public class LoginController implements Routable {
|
|||||||
}, "login-thread").start();
|
}, "login-thread").start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles username action (e.g., pressing enter) by requesting focus on the password field.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public void onUsernameAction() {
|
public void onUsernameAction() {
|
||||||
passwordField.requestFocus();
|
passwordField.requestFocus();
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import javafx.scene.web.WebView;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the Moodle integration view.
|
||||||
|
* Displays a WebView with the Moodle website and handles automatic login.
|
||||||
|
*/
|
||||||
@Route(path = "/moodle", fxml = "/moodle.fxml")
|
@Route(path = "/moodle", fxml = "/moodle.fxml")
|
||||||
public class MoodleController extends DashboardBaseController {
|
public class MoodleController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -33,6 +37,12 @@ public class MoodleController extends DashboardBaseController {
|
|||||||
|
|
||||||
private WebEngine webEngine;
|
private WebEngine webEngine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the controller when navigating to this view.
|
||||||
|
* Sets up the WebView, auto-login logic, and navigation buttons.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties, not explicitly used here.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
@@ -104,6 +114,9 @@ public class MoodleController extends DashboardBaseController {
|
|||||||
webEngine.load("https://moodle.spsejecna.cz/");
|
webEngine.load("https://moodle.spsejecna.cz/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the back and forward navigation buttons state.
|
||||||
|
*/
|
||||||
private void updateNavigationButtons() {
|
private void updateNavigationButtons() {
|
||||||
WebHistory history = webEngine.getHistory();
|
WebHistory history = webEngine.getHistory();
|
||||||
int currentIndex = history.getCurrentIndex();
|
int currentIndex = history.getCurrentIndex();
|
||||||
@@ -129,6 +142,9 @@ public class MoodleController extends DashboardBaseController {
|
|||||||
btnForward.setDisable(validForwardIndex == -1);
|
btnForward.setDisable(validForwardIndex == -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates back in the WebView history.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBrowserBack() {
|
protected void onBrowserBack() {
|
||||||
WebHistory history = webEngine.getHistory();
|
WebHistory history = webEngine.getHistory();
|
||||||
@@ -142,6 +158,9 @@ public class MoodleController extends DashboardBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates forward in the WebView history.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBrowserForward() {
|
protected void onBrowserForward() {
|
||||||
WebHistory history = webEngine.getHistory();
|
WebHistory history = webEngine.getHistory();
|
||||||
@@ -155,11 +174,17 @@ public class MoodleController extends DashboardBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the current page in the WebView.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBrowserReload() {
|
protected void onBrowserReload() {
|
||||||
webEngine.reload();
|
webEngine.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the dashboard.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBackToDashboard() {
|
protected void onBackToDashboard() {
|
||||||
Router.getInstance().navigate("/dashboard");
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import javafx.scene.layout.*;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the teacher profile view.
|
||||||
|
* Displays details about a specific teacher, including contact information and their timetable.
|
||||||
|
*/
|
||||||
@Route(path = "/teacher_profile", fxml = "/teacher_profile.fxml")
|
@Route(path = "/teacher_profile", fxml = "/teacher_profile.fxml")
|
||||||
public class TeacherProfileController extends DashboardBaseController {
|
public class TeacherProfileController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -44,6 +48,12 @@ public class TeacherProfileController extends DashboardBaseController {
|
|||||||
|
|
||||||
private String teacherTag;
|
private String teacherTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the controller when navigating to this view.
|
||||||
|
* Sets the teacher tag from navigation properties and initiates data loading.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties, must contain a "tag" key with the teacher's tag.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
@@ -57,6 +67,10 @@ public class TeacherProfileController extends DashboardBaseController {
|
|||||||
loadTeacher();
|
loadTeacher();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the teacher profile from the API and updates the UI.
|
||||||
|
* Shows a loading indicator while fetching.
|
||||||
|
*/
|
||||||
private void loadTeacher() {
|
private void loadTeacher() {
|
||||||
loadingIndicator.setVisible(true);
|
loadingIndicator.setVisible(true);
|
||||||
scrollPane.setVisible(false);
|
scrollPane.setVisible(false);
|
||||||
@@ -85,6 +99,11 @@ public class TeacherProfileController extends DashboardBaseController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the teacher profile information into the UI.
|
||||||
|
*
|
||||||
|
* @param teacher The teacher object to display.
|
||||||
|
*/
|
||||||
private void renderTeacher(Teacher teacher) {
|
private void renderTeacher(Teacher teacher) {
|
||||||
headerNameLabel.setText(teacher.getFullName());
|
headerNameLabel.setText(teacher.getFullName());
|
||||||
fullNameLabel.setText(teacher.getFullName());
|
fullNameLabel.setText(teacher.getFullName());
|
||||||
@@ -127,6 +146,14 @@ public class TeacherProfileController extends DashboardBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a row of detail information to the details grid.
|
||||||
|
*
|
||||||
|
* @param labelText The label text for the detail.
|
||||||
|
* @param value The value of the detail.
|
||||||
|
* @param row The row index in the grid.
|
||||||
|
* @return The next row index.
|
||||||
|
*/
|
||||||
private int addDetailRow(String labelText, String value, int row) {
|
private int addDetailRow(String labelText, String value, int row) {
|
||||||
if (value == null || value.isBlank()) {
|
if (value == null || value.isBlank()) {
|
||||||
return row;
|
return row;
|
||||||
@@ -145,6 +172,9 @@ public class TeacherProfileController extends DashboardBaseController {
|
|||||||
return row + 1;
|
return row + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the teachers list view.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
@Override
|
@Override
|
||||||
protected void onBack() {
|
protected void onBack() {
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the teachers list view.
|
||||||
|
* Provides functionality to display, search, and navigate to teacher profiles.
|
||||||
|
*/
|
||||||
@Route(path = "/teachers", fxml = "/teachers.fxml")
|
@Route(path = "/teachers", fxml = "/teachers.fxml")
|
||||||
public class TeachersController extends DashboardBaseController {
|
public class TeachersController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -32,6 +36,12 @@ public class TeachersController extends DashboardBaseController {
|
|||||||
|
|
||||||
private List<TeacherReference> allTeachers = new ArrayList<>();
|
private List<TeacherReference> allTeachers = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the controller when navigating to this view.
|
||||||
|
* Sets up the search filter and initiates data loading.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties, not explicitly used here.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
@@ -43,6 +53,10 @@ public class TeachersController extends DashboardBaseController {
|
|||||||
loadTeachers();
|
loadTeachers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the list of teachers from the API and updates the UI.
|
||||||
|
* Shows a loading indicator while fetching.
|
||||||
|
*/
|
||||||
private void loadTeachers() {
|
private void loadTeachers() {
|
||||||
loadingIndicator.setVisible(true);
|
loadingIndicator.setVisible(true);
|
||||||
scrollPane.setVisible(false);
|
scrollPane.setVisible(false);
|
||||||
@@ -76,6 +90,11 @@ public class TeachersController extends DashboardBaseController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the list of teachers based on the provided search query.
|
||||||
|
*
|
||||||
|
* @param query The search string to filter by.
|
||||||
|
*/
|
||||||
private void filterTeachers(String query) {
|
private void filterTeachers(String query) {
|
||||||
teachersFlow.getChildren().clear();
|
teachersFlow.getChildren().clear();
|
||||||
|
|
||||||
@@ -113,6 +132,9 @@ public class TeachersController extends DashboardBaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the dashboard.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBackToDashboard() {
|
protected void onBackToDashboard() {
|
||||||
Router.getInstance().navigate("/dashboard");
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import kotlinx.datetime.DayOfWeek;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling the timetable view.
|
||||||
|
* Displays the weekly timetable for the student.
|
||||||
|
*/
|
||||||
@Route(path = "/timetable", fxml = "/timetable.fxml")
|
@Route(path = "/timetable", fxml = "/timetable.fxml")
|
||||||
public class TimetableController extends DashboardBaseController {
|
public class TimetableController extends DashboardBaseController {
|
||||||
|
|
||||||
@@ -35,12 +39,22 @@ public class TimetableController extends DashboardBaseController {
|
|||||||
|
|
||||||
private TimetablePage currentPage;
|
private TimetablePage currentPage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the controller when navigating to this view.
|
||||||
|
* Initiates loading of timetable data.
|
||||||
|
*
|
||||||
|
* @param props Navigation properties, not explicitly used here.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> props) {
|
public void onNavigate(Map<String, Object> props) {
|
||||||
super.onNavigate(props);
|
super.onNavigate(props);
|
||||||
loadTimetable();
|
loadTimetable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the timetable page from the API and updates the UI.
|
||||||
|
* Shows a loading indicator while fetching.
|
||||||
|
*/
|
||||||
private void loadTimetable() {
|
private void loadTimetable() {
|
||||||
loadingIndicator.setVisible(true);
|
loadingIndicator.setVisible(true);
|
||||||
scrollPane.setVisible(false);
|
scrollPane.setVisible(false);
|
||||||
@@ -56,6 +70,11 @@ public class TimetableController extends DashboardBaseController {
|
|||||||
}, QueryOptions.defaultOptions()).thenAccept(this::handleTimetableResult);
|
}, QueryOptions.defaultOptions()).thenAccept(this::handleTimetableResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the result of the timetable fetch operation.
|
||||||
|
*
|
||||||
|
* @param result The result of the fetch.
|
||||||
|
*/
|
||||||
private void handleTimetableResult(QueryResult<TimetablePage> result) {
|
private void handleTimetableResult(QueryResult<TimetablePage> result) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
loadingIndicator.setVisible(false);
|
loadingIndicator.setVisible(false);
|
||||||
@@ -75,6 +94,9 @@ public class TimetableController extends DashboardBaseController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the timetable grid using the TimetableRenderer.
|
||||||
|
*/
|
||||||
private void renderTimetable() {
|
private void renderTimetable() {
|
||||||
timetableGrid.getChildren().clear();
|
timetableGrid.getChildren().clear();
|
||||||
timetableGrid.getColumnConstraints().clear();
|
timetableGrid.getColumnConstraints().clear();
|
||||||
@@ -85,6 +107,9 @@ public class TimetableController extends DashboardBaseController {
|
|||||||
TimetableRenderer.renderTimetable(timetableGrid, timetable);
|
TimetableRenderer.renderTimetable(timetableGrid, timetable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles navigation back to the dashboard.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
protected void onBackToDashboard() {
|
protected void onBackToDashboard() {
|
||||||
Router.getInstance().navigate("/dashboard");
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
|||||||
@@ -4,11 +4,23 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client for handling data fetching with caching and background updates.
|
||||||
|
*/
|
||||||
public class QueryClient {
|
public class QueryClient {
|
||||||
|
|
||||||
private final Map<String, CachedItem<?>> cache = new ConcurrentHashMap<>();
|
private final Map<String, CachedItem<?>> cache = new ConcurrentHashMap<>();
|
||||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches data with caching support.
|
||||||
|
*
|
||||||
|
* @param key The cache key.
|
||||||
|
* @param fetcher Supplier for fetching data if not cached or stale.
|
||||||
|
* @param opts Query options for caching behavior.
|
||||||
|
* @param <T> The type of data.
|
||||||
|
* @return A CompletableFuture with the result.
|
||||||
|
*/
|
||||||
public <T> CompletableFuture<QueryResult<T>> fetch(String key, Supplier<T> fetcher, QueryOptions opts) {
|
public <T> CompletableFuture<QueryResult<T>> fetch(String key, Supplier<T> fetcher, QueryOptions opts) {
|
||||||
@SuppressWarnings("unchecked") CachedItem<T> item = (CachedItem<T>) cache.get(key);
|
@SuppressWarnings("unchecked") CachedItem<T> item = (CachedItem<T>) cache.get(key);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@@ -53,14 +65,29 @@ public class QueryClient {
|
|||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually sets an item in the cache.
|
||||||
|
*
|
||||||
|
* @param key The cache key.
|
||||||
|
* @param value The value to cache.
|
||||||
|
* @param <T> The type of data.
|
||||||
|
*/
|
||||||
public <T> void set(String key, T value) {
|
public <T> void set(String key, T value) {
|
||||||
cache.put(key, new CachedItem<>(value, System.currentTimeMillis()));
|
cache.put(key, new CachedItem<>(value, System.currentTimeMillis()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates a cache item.
|
||||||
|
*
|
||||||
|
* @param key The cache key to remove.
|
||||||
|
*/
|
||||||
public void invalidate(String key) {
|
public void invalidate(String key) {
|
||||||
cache.remove(key);
|
cache.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts down the scheduler.
|
||||||
|
*/
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
scheduler.shutdownNow();
|
scheduler.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
package cz.jzitnik.query;
|
package cz.jzitnik.query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options for query operations, such as caching and retries.
|
||||||
|
*/
|
||||||
public class QueryOptions {
|
public class QueryOptions {
|
||||||
// time until data is considered stale (ms)
|
/** Time until data is considered stale (ms). Default: 5 minutes. */
|
||||||
public long staleTime = 5 * 60 * 1000; // 5 minutes
|
public long staleTime = 5 * 60 * 1000;
|
||||||
// time until unused cache entry is evicted (ms)
|
/** Time until unused cache entry is evicted (ms). Default: 30 minutes. */
|
||||||
public long cacheTime = 30 * 60 * 1000; // 30 minutes
|
public long cacheTime = 30 * 60 * 1000;
|
||||||
// if >0, interval to refetch in background (ms)
|
/** If > 0, interval to refetch in background (ms). Default: 0 (disabled). */
|
||||||
public long refetchInterval = 0;
|
public long refetchInterval = 0;
|
||||||
// number of retry attempts on failure
|
/** Number of retry attempts on failure. Default: 0. */
|
||||||
public int retryAttempts = 0;
|
public int retryAttempts = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new default query options.
|
||||||
|
*/
|
||||||
public QueryOptions() {}
|
public QueryOptions() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns default query options.
|
||||||
|
*
|
||||||
|
* @return Default QueryOptions instance.
|
||||||
|
*/
|
||||||
public static QueryOptions defaultOptions() {
|
public static QueryOptions defaultOptions() {
|
||||||
return new QueryOptions();
|
return new QueryOptions();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,53 @@ package cz.jzitnik.query;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of a query operation.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the data.
|
||||||
|
*/
|
||||||
public class QueryResult<T> {
|
public class QueryResult<T> {
|
||||||
private final T data;
|
private final T data;
|
||||||
private final Throwable error;
|
private final Throwable error;
|
||||||
private final Instant fetchedAt;
|
private final Instant fetchedAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new QueryResult.
|
||||||
|
*
|
||||||
|
* @param data The query data, or null if an error occurred.
|
||||||
|
* @param error The error, or null if successful.
|
||||||
|
*/
|
||||||
public QueryResult(T data, Throwable error) {
|
public QueryResult(T data, Throwable error) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.fetchedAt = Instant.now();
|
this.fetchedAt = Instant.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the data.
|
||||||
|
*
|
||||||
|
* @return Optional containing the data.
|
||||||
|
*/
|
||||||
public Optional<T> getData() { return Optional.ofNullable(data); }
|
public Optional<T> getData() { return Optional.ofNullable(data); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the error.
|
||||||
|
*
|
||||||
|
* @return Optional containing the error.
|
||||||
|
*/
|
||||||
public Optional<Throwable> getError() { return Optional.ofNullable(error); }
|
public Optional<Throwable> getError() { return Optional.ofNullable(error); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fetch time.
|
||||||
|
*
|
||||||
|
* @return The fetch time.
|
||||||
|
*/
|
||||||
public Instant getFetchedAt() { return fetchedAt; }
|
public Instant getFetchedAt() { return fetchedAt; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the query was successful.
|
||||||
|
*
|
||||||
|
* @return True if successful, false otherwise.
|
||||||
|
*/
|
||||||
public boolean isSuccess() { return error == null; }
|
public boolean isSuccess() { return error == null; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple router for navigating between views in a JavaFX application.
|
||||||
|
*/
|
||||||
public class Router {
|
public class Router {
|
||||||
private static Router instance;
|
private static Router instance;
|
||||||
private Pane rootContainer;
|
private Pane rootContainer;
|
||||||
@@ -18,6 +21,11 @@ public class Router {
|
|||||||
|
|
||||||
private Router() {}
|
private Router() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the singleton instance of the router.
|
||||||
|
*
|
||||||
|
* @return The Router instance.
|
||||||
|
*/
|
||||||
public static Router getInstance() {
|
public static Router getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new Router();
|
instance = new Router();
|
||||||
@@ -25,10 +33,20 @@ public class Router {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the root container pane for the router.
|
||||||
|
*
|
||||||
|
* @param rootContainer The root container.
|
||||||
|
*/
|
||||||
public void setRootContainer(Pane rootContainer) {
|
public void setRootContainer(Pane rootContainer) {
|
||||||
this.rootContainer = rootContainer;
|
this.rootContainer = rootContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers routes annotated with @Route in the specified base package.
|
||||||
|
*
|
||||||
|
* @param basePackage The base package to scan for routes.
|
||||||
|
*/
|
||||||
public void registerAnnotatedRoutes(String basePackage) {
|
public void registerAnnotatedRoutes(String basePackage) {
|
||||||
Reflections r = new Reflections(basePackage);
|
Reflections r = new Reflections(basePackage);
|
||||||
Set<Class<?>> controllers = r.getTypesAnnotatedWith(Route.class);
|
Set<Class<?>> controllers = r.getTypesAnnotatedWith(Route.class);
|
||||||
@@ -40,10 +58,21 @@ public class Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to a specific path.
|
||||||
|
*
|
||||||
|
* @param path The path to navigate to.
|
||||||
|
*/
|
||||||
public void navigate(String path) {
|
public void navigate(String path) {
|
||||||
navigate(path, new HashMap<>());
|
navigate(path, new HashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to a specific path with navigation properties.
|
||||||
|
*
|
||||||
|
* @param path The path to navigate to.
|
||||||
|
* @param props Navigation properties.
|
||||||
|
*/
|
||||||
public void navigate(String path, Map<String, Object> props) {
|
public void navigate(String path, Map<String, Object> props) {
|
||||||
if (rootContainer == null) {
|
if (rootContainer == null) {
|
||||||
throw new IllegalStateException("Root container not set in Router.");
|
throw new IllegalStateException("Root container not set in Router.");
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import cz.jzitnik.auth.CredentialStore;
|
|||||||
import io.github.tomhula.jecnaapi.java.JecnaClientJavaWrapper;
|
import io.github.tomhula.jecnaapi.java.JecnaClientJavaWrapper;
|
||||||
import cz.jzitnik.query.QueryClient;
|
import cz.jzitnik.query.QueryClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the current application state, including user authentication and API interactions.
|
||||||
|
* This class serves as the central hub for tracking the logged-in user, managing
|
||||||
|
* the active Jecna API client session, and providing access to query tools.
|
||||||
|
*/
|
||||||
@State
|
@State
|
||||||
public class AppState {
|
public class AppState {
|
||||||
|
|
||||||
@@ -12,26 +17,59 @@ public class AppState {
|
|||||||
private JecnaClientJavaWrapper client;
|
private JecnaClientJavaWrapper client;
|
||||||
private QueryClient queryClient;
|
private QueryClient queryClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the username of the currently logged-in user.
|
||||||
|
*
|
||||||
|
* @return the username, or null if no user is logged in.
|
||||||
|
*/
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the password of the currently logged-in user.
|
||||||
|
*
|
||||||
|
* @return the password, or null if no user is logged in.
|
||||||
|
*/
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the active Jecna API client wrapper.
|
||||||
|
*
|
||||||
|
* @return the Jecna API client instance, or null if not initialized/logged in.
|
||||||
|
*/
|
||||||
public JecnaClientJavaWrapper getClient() {
|
public JecnaClientJavaWrapper getClient() {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the query client used for fetching specific data.
|
||||||
|
*
|
||||||
|
* @return the QueryClient instance.
|
||||||
|
*/
|
||||||
public QueryClient getQueryClient() {
|
public QueryClient getQueryClient() {
|
||||||
return queryClient;
|
return queryClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the query client for this state.
|
||||||
|
*
|
||||||
|
* @param qc the QueryClient instance to use.
|
||||||
|
*/
|
||||||
public void setQueryClient(QueryClient qc) {
|
public void setQueryClient(QueryClient qc) {
|
||||||
this.queryClient = qc;
|
this.queryClient = qc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to log the user into the Jecna API with the provided credentials.
|
||||||
|
* If successful, it stores the credentials securely for future sessions.
|
||||||
|
*
|
||||||
|
* @param username the user's username.
|
||||||
|
* @param password the user's password.
|
||||||
|
* @return true if login was successful, false otherwise.
|
||||||
|
*/
|
||||||
public boolean login(String username, String password) {
|
public boolean login(String username, String password) {
|
||||||
if (username == null || username.isEmpty() || password == null) return false;
|
if (username == null || username.isEmpty() || password == null) return false;
|
||||||
|
|
||||||
@@ -61,6 +99,10 @@ public class AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current application state, including logging out the user
|
||||||
|
* and resetting the API client.
|
||||||
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
this.username = null;
|
this.username = null;
|
||||||
this.password = null;
|
this.password = null;
|
||||||
|
|||||||
@@ -11,8 +11,14 @@ import javafx.scene.paint.Color;
|
|||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A UI component that displays an animated aurora-like background.
|
||||||
|
*/
|
||||||
public class AuroraBackground extends StackPane {
|
public class AuroraBackground extends StackPane {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the aurora background with animated blobs of color.
|
||||||
|
*/
|
||||||
public AuroraBackground() {
|
public AuroraBackground() {
|
||||||
Color color1 = Color.web("#00FFFF", 0.10); // Cyan
|
Color color1 = Color.web("#00FFFF", 0.10); // Cyan
|
||||||
Color color2 = Color.web("#FF00FF", 0.10); // Magenta
|
Color color2 = Color.web("#FF00FF", 0.10); // Magenta
|
||||||
@@ -38,6 +44,16 @@ public class AuroraBackground extends StackPane {
|
|||||||
setupAnimation(blob3, 0, -200, 0, 200, 15);
|
setupAnimation(blob3, 0, -200, 0, 200, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the animation for a color blob.
|
||||||
|
*
|
||||||
|
* @param node The circle node to animate.
|
||||||
|
* @param startX Starting X translation.
|
||||||
|
* @param startY Starting Y translation.
|
||||||
|
* @param endX Ending X translation.
|
||||||
|
* @param endY Ending Y translation.
|
||||||
|
* @param durationSeconds Duration of the animation in seconds.
|
||||||
|
*/
|
||||||
private void setupAnimation(Circle node, double startX, double startY, double endX, double endY, int durationSeconds) {
|
private void setupAnimation(Circle node, double startX, double startY, double endX, double endY, int durationSeconds) {
|
||||||
Timeline timeline = new Timeline(
|
Timeline timeline = new Timeline(
|
||||||
new KeyFrame(Duration.ZERO,
|
new KeyFrame(Duration.ZERO,
|
||||||
|
|||||||
@@ -8,8 +8,20 @@ import java.net.URL;
|
|||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A little utility to grab images from the school website.
|
||||||
|
* Since the server can be picky about requests, we have to pretend
|
||||||
|
* to be a real browser to get the files successfully.
|
||||||
|
*/
|
||||||
public class ImageFetcher {
|
public class ImageFetcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches an image from the school server, handling decompression
|
||||||
|
* and proper headers to avoid being blocked.
|
||||||
|
*
|
||||||
|
* @param imagePath The relative path to the image on the school server.
|
||||||
|
* @return The loaded JavaFX Image, or null if something went wrong.
|
||||||
|
*/
|
||||||
public static Image fetchImage(String imagePath) {
|
public static Image fetchImage(String imagePath) {
|
||||||
if (imagePath == null || imagePath.isEmpty()) {
|
if (imagePath == null || imagePath.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -7,8 +7,18 @@ import javafx.scene.control.ScrollPane;
|
|||||||
import javafx.scene.input.ScrollEvent;
|
import javafx.scene.input.ScrollEvent;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helps make scrolling through the app feel a bit more premium.
|
||||||
|
* Instead of jumping straight to the new position, this gently animates
|
||||||
|
* the scroll, making it much easier on the eyes.
|
||||||
|
*/
|
||||||
public class ScrollUtils {
|
public class ScrollUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables smooth, animated scrolling for a given ScrollPane.
|
||||||
|
*
|
||||||
|
* @param scrollPane The ScrollPane we want to make feel smoother.
|
||||||
|
*/
|
||||||
public static void enableSmoothScrolling(ScrollPane scrollPane) {
|
public static void enableSmoothScrolling(ScrollPane scrollPane) {
|
||||||
scrollPane.addEventFilter(ScrollEvent.SCROLL, event -> {
|
scrollPane.addEventFilter(ScrollEvent.SCROLL, event -> {
|
||||||
double deltaY = event.getDeltaY();
|
double deltaY = event.getDeltaY();
|
||||||
|
|||||||
@@ -21,8 +21,17 @@ import kotlinx.datetime.DayOfWeek;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for rendering the timetable in a JavaFX GridPane.
|
||||||
|
*/
|
||||||
public class TimetableRenderer {
|
public class TimetableRenderer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the timetable into the provided GridPane.
|
||||||
|
*
|
||||||
|
* @param timetableGrid The GridPane to render into.
|
||||||
|
* @param timetable The timetable data to render.
|
||||||
|
*/
|
||||||
public static void renderTimetable(GridPane timetableGrid, Timetable timetable) {
|
public static void renderTimetable(GridPane timetableGrid, Timetable timetable) {
|
||||||
timetableGrid.getChildren().clear();
|
timetableGrid.getChildren().clear();
|
||||||
timetableGrid.getColumnConstraints().clear();
|
timetableGrid.getColumnConstraints().clear();
|
||||||
@@ -92,6 +101,12 @@ public class TimetableRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a cell UI component for a lesson spot.
|
||||||
|
*
|
||||||
|
* @param spot The lesson spot data.
|
||||||
|
* @return The created VBox component.
|
||||||
|
*/
|
||||||
private static VBox createLessonCell(LessonSpot spot) {
|
private static VBox createLessonCell(LessonSpot spot) {
|
||||||
VBox cell = new VBox(5);
|
VBox cell = new VBox(5);
|
||||||
cell.setAlignment(Pos.CENTER);
|
cell.setAlignment(Pos.CENTER);
|
||||||
@@ -131,6 +146,12 @@ public class TimetableRenderer {
|
|||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds lesson information to a cell UI component.
|
||||||
|
*
|
||||||
|
* @param cell The cell UI component.
|
||||||
|
* @param lesson The lesson data.
|
||||||
|
*/
|
||||||
private static void addLessonInfoToCell(VBox cell, Lesson lesson) {
|
private static void addLessonInfoToCell(VBox cell, Lesson lesson) {
|
||||||
Label subjectLbl = new Label(lesson.getSubjectName().getShort() != null ? lesson.getSubjectName().getShort() : lesson.getSubjectName().getFull());
|
Label subjectLbl = new Label(lesson.getSubjectName().getShort() != null ? lesson.getSubjectName().getShort() : lesson.getSubjectName().getFull());
|
||||||
subjectLbl.getStyleClass().add("timetable-subject");
|
subjectLbl.getStyleClass().add("timetable-subject");
|
||||||
@@ -153,6 +174,11 @@ public class TimetableRenderer {
|
|||||||
cell.getChildren().addAll(subjectLbl, bottomInfo);
|
cell.getChildren().addAll(subjectLbl, bottomInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a dialog with detailed lesson information.
|
||||||
|
*
|
||||||
|
* @param spot The lesson spot data.
|
||||||
|
*/
|
||||||
private static void showLessonDetails(LessonSpot spot) {
|
private static void showLessonDetails(LessonSpot spot) {
|
||||||
Dialog<Void> dialog = new Dialog<>();
|
Dialog<Void> dialog = new Dialog<>();
|
||||||
dialog.setTitle("Detail hodiny");
|
dialog.setTitle("Detail hodiny");
|
||||||
@@ -209,6 +235,12 @@ public class TimetableRenderer {
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates DayOfWeek to a Czech string.
|
||||||
|
*
|
||||||
|
* @param day The DayOfWeek.
|
||||||
|
* @return The translated day name.
|
||||||
|
*/
|
||||||
private static String translateDay(DayOfWeek day) {
|
private static String translateDay(DayOfWeek day) {
|
||||||
return switch (day) {
|
return switch (day) {
|
||||||
case MONDAY -> "Pondělí";
|
case MONDAY -> "Pondělí";
|
||||||
|
|||||||
Reference in New Issue
Block a user