mirror of
https://github.com/sabazadam/Se302.git
synced 2025-12-31 12:21:22 +00:00
Implement initial application UI including schedule generation and import views, a main controller, and update Maven compiler
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -51,8 +51,8 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.example.se302.controller;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
@@ -8,7 +9,7 @@ import org.example.se302.service.DataManager;
|
||||
|
||||
/**
|
||||
* Main controller for the application window.
|
||||
* Manages the TabPane and status bar.
|
||||
* Manages the TabPane, status bar, and theme toggling.
|
||||
*/
|
||||
public class MainController {
|
||||
|
||||
@@ -33,7 +34,11 @@ public class MainController {
|
||||
@FXML
|
||||
private Label statusLabel;
|
||||
|
||||
@FXML
|
||||
private Button themeToggleButton;
|
||||
|
||||
private DataManager dataManager;
|
||||
private boolean isDarkMode = false;
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
@@ -78,8 +83,28 @@ public class MainController {
|
||||
"Loaded: %d Students, %d Courses, %d Classrooms",
|
||||
dataManager.getTotalStudents(),
|
||||
dataManager.getTotalCourses(),
|
||||
dataManager.getTotalClassrooms()
|
||||
));
|
||||
dataManager.getTotalClassrooms()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle between light and dark themes.
|
||||
*/
|
||||
@FXML
|
||||
private void onToggleTheme() {
|
||||
var root = mainTabPane.getScene().getRoot();
|
||||
var styleClass = root.getStyleClass();
|
||||
|
||||
if (isDarkMode) {
|
||||
// Switch to light mode
|
||||
styleClass.remove("dark");
|
||||
themeToggleButton.setText("🌙 Dark Mode");
|
||||
isDarkMode = false;
|
||||
} else {
|
||||
// Switch to dark mode
|
||||
styleClass.add("dark");
|
||||
themeToggleButton.setText("☀️ Light Mode");
|
||||
isDarkMode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,228 @@
|
||||
/* ===== Root Color Palette ===== */
|
||||
/* ========================================
|
||||
ExamFlow v2.0 - Modern UI Stylesheet
|
||||
Based on Tailwind-inspired design system
|
||||
======================================== */
|
||||
|
||||
/* ===== Root Color Palette (Indigo & Slate) ===== */
|
||||
.root {
|
||||
-fx-primary-color: #2196F3;
|
||||
-fx-primary-dark: #1976D2;
|
||||
-fx-accent-color: #FFC107;
|
||||
-fx-success-color: #4CAF50;
|
||||
-fx-error-color: #F44336;
|
||||
-fx-background: #FAFAFA;
|
||||
-fx-text-base-color: #212121;
|
||||
/* Primary Colors (Indigo) */
|
||||
-fx-primary: #4F46E5;
|
||||
/* indigo-600 */
|
||||
-fx-primary-dark: #4338CA;
|
||||
/* indigo-700 */
|
||||
-fx-primary-light: #6366F1;
|
||||
/* indigo-500 */
|
||||
-fx-primary-50: #EEF2FF;
|
||||
/* indigo-50 */
|
||||
-fx-primary-100: #E0E7FF;
|
||||
/* indigo-100 */
|
||||
|
||||
/* Slate Grays */
|
||||
-fx-slate-50: #F8FAFC;
|
||||
-fx-slate-100: #F1F5F9;
|
||||
-fx-slate-200: #E2E8F0;
|
||||
-fx-slate-300: #CBD5E1;
|
||||
-fx-slate-400: #94A3B8;
|
||||
-fx-slate-500: #64748B;
|
||||
-fx-slate-600: #475569;
|
||||
-fx-slate-700: #334155;
|
||||
-fx-slate-800: #1E293B;
|
||||
-fx-slate-900: #0F172A;
|
||||
|
||||
/* Semantic Colors */
|
||||
-fx-success: #10B981;
|
||||
/* emerald-500 */
|
||||
-fx-success-light: #D1FAE5;
|
||||
/* emerald-100 */
|
||||
-fx-warning: #F59E0B;
|
||||
/* amber-500 */
|
||||
-fx-warning-light: #FEF3C7;
|
||||
/* amber-100 */
|
||||
-fx-error: #EF4444;
|
||||
/* red-500 */
|
||||
-fx-error-light: #FEE2E2;
|
||||
/* red-100 */
|
||||
-fx-info: #3B82F6;
|
||||
/* blue-500 */
|
||||
-fx-info-light: #DBEAFE;
|
||||
/* blue-100 */
|
||||
|
||||
/* Background & Surface */
|
||||
-fx-background: #F8FAFC;
|
||||
/* slate-50 */
|
||||
-fx-surface: #FFFFFF;
|
||||
|
||||
/* Text Colors */
|
||||
-fx-text-primary: #0F172A;
|
||||
/* slate-900 */
|
||||
-fx-text-secondary: #475569;
|
||||
/* slate-600 */
|
||||
-fx-text-tertiary: #94A3B8;
|
||||
/* slate-400 */
|
||||
|
||||
/* Shadows */
|
||||
-fx-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
-fx-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
-fx-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
-fx-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
/* Font Settings */
|
||||
-fx-font-family: "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
|
||||
/* ===== General Styling ===== */
|
||||
/* ===== Dark Mode Color Palette ===== */
|
||||
.root.dark {
|
||||
/* Primary Colors (Same indigo, but brighter for dark bg) */
|
||||
-fx-primary: #6366F1;
|
||||
/* indigo-500 - brighter */
|
||||
-fx-primary-dark: #4F46E5;
|
||||
/* indigo-600 */
|
||||
-fx-primary-light: #818CF8;
|
||||
/* indigo-400 */
|
||||
-fx-primary-50: #312E81;
|
||||
/* indigo-900 - inverted */
|
||||
-fx-primary-100: #3730A3;
|
||||
/* indigo-800 - inverted */
|
||||
|
||||
/* Dark Slate Palette (inverted) */
|
||||
-fx-slate-50: #0F172A;
|
||||
/* slate-900 */
|
||||
-fx-slate-100: #1E293B;
|
||||
/* slate-800 */
|
||||
-fx-slate-200: #334155;
|
||||
/* slate-700 */
|
||||
-fx-slate-300: #475569;
|
||||
/* slate-600 */
|
||||
-fx-slate-400: #64748B;
|
||||
/* slate-500 */
|
||||
-fx-slate-500: #94A3B8;
|
||||
/* slate-400 */
|
||||
-fx-slate-600: #CBD5E1;
|
||||
/* slate-300 */
|
||||
-fx-slate-700: #E2E8F0;
|
||||
/* slate-200 */
|
||||
-fx-slate-800: #F1F5F9;
|
||||
/* slate-100 */
|
||||
-fx-slate-900: #F8FAFC;
|
||||
/* slate-50 */
|
||||
|
||||
/* Semantic Colors (brighter for dark) */
|
||||
-fx-success: #34D399;
|
||||
/* emerald-400 */
|
||||
-fx-success-light: #064E3B;
|
||||
/* emerald-900 */
|
||||
-fx-warning: #FBBF24;
|
||||
/* amber-400 */
|
||||
-fx-warning-light: #78350F;
|
||||
/* amber-900 */
|
||||
-fx-error: #F87171;
|
||||
/* red-400 */
|
||||
-fx-error-light: #7F1D1D;
|
||||
/* red-900 */
|
||||
-fx-info: #60A5FA;
|
||||
/* blue-400 */
|
||||
-fx-info-light: #1E3A8A;
|
||||
/* blue-900 */
|
||||
|
||||
/* Background & Surface (dark) */
|
||||
-fx-background: #0F172A;
|
||||
/* slate-900 */
|
||||
-fx-surface: #1E293B;
|
||||
/* slate-800 */
|
||||
|
||||
/* Text Colors (inverted) */
|
||||
-fx-text-primary: #F8FAFC;
|
||||
/* slate-50 */
|
||||
-fx-text-secondary: #CBD5E1;
|
||||
/* slate-300 */
|
||||
-fx-text-tertiary: #64748B;
|
||||
/* slate-500 */
|
||||
|
||||
/* Shadows (more pronounced in dark) */
|
||||
-fx-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
-fx-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
||||
-fx-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
|
||||
-fx-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
/* ===== General Layouts ===== */
|
||||
.content-pane {
|
||||
-fx-background-color: white;
|
||||
-fx-background-color: -fx-slate-50;
|
||||
-fx-padding: 40;
|
||||
}
|
||||
|
||||
/* ===== Header Styling ===== */
|
||||
.header {
|
||||
-fx-background-color: -fx-primary-color;
|
||||
-fx-padding: 15;
|
||||
}
|
||||
|
||||
.title-label {
|
||||
-fx-font-size: 24px;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: white;
|
||||
}
|
||||
|
||||
/* ===== Section Titles ===== */
|
||||
/* ===== Typography ===== */
|
||||
.section-title {
|
||||
-fx-font-size: 20px;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: -fx-primary-dark;
|
||||
-fx-font-size: 28px;
|
||||
-fx-font-weight: 900;
|
||||
/* black */
|
||||
-fx-text-fill: -fx-text-primary;
|
||||
-fx-padding: 0 0 16 0;
|
||||
}
|
||||
|
||||
.subsection-title {
|
||||
-fx-font-size: 14px;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: #424242;
|
||||
-fx-font-size: 16px;
|
||||
-fx-font-weight: 700;
|
||||
/* bold */
|
||||
-fx-text-fill: -fx-slate-800;
|
||||
-fx-padding: 0 0 8 0;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
-fx-font-size: 18px;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: #616161;
|
||||
-fx-font-size: 20px;
|
||||
-fx-font-weight: 700;
|
||||
-fx-text-fill: -fx-slate-700;
|
||||
}
|
||||
|
||||
/* ===== Button Styling ===== */
|
||||
.description-label {
|
||||
-fx-font-size: 16px;
|
||||
-fx-text-fill: -fx-slate-600;
|
||||
-fx-padding: 0 0 8 0;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
-fx-font-size: 28px;
|
||||
-fx-font-weight: 900;
|
||||
-fx-text-fill: -fx-primary;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
-fx-font-size: 11px;
|
||||
-fx-font-weight: 700;
|
||||
-fx-text-fill: -fx-slate-400;
|
||||
-fx-padding: 4 0 0 0;
|
||||
}
|
||||
|
||||
/* ===== Buttons ===== */
|
||||
.button {
|
||||
-fx-background-color: white;
|
||||
-fx-border-color: -fx-primary-color;
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-text-fill: -fx-primary-color;
|
||||
-fx-padding: 8 16 8 16;
|
||||
-fx-border-radius: 12px;
|
||||
-fx-background-radius: 12px;
|
||||
-fx-text-fill: -fx-slate-600;
|
||||
-fx-padding: 10 20;
|
||||
-fx-cursor: hand;
|
||||
-fx-font-weight: 700;
|
||||
/* bold */
|
||||
-fx-font-size: 13px;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.05), 2, 0, 0, 1);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
-fx-background-color: #E3F2FD;
|
||||
-fx-background-color: -fx-slate-100;
|
||||
-fx-border-color: -fx-slate-300;
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
-fx-background-color: -fx-primary-color;
|
||||
-fx-background-color: -fx-primary;
|
||||
-fx-text-fill: white;
|
||||
-fx-border-color: -fx-primary-color;
|
||||
-fx-border-color: -fx-primary;
|
||||
-fx-font-weight: 900;
|
||||
/* black */
|
||||
-fx-padding: 12 24;
|
||||
-fx-effect: dropshadow(gaussian, derive(-fx-primary, -20%), 10, 0.3, 0, 4);
|
||||
}
|
||||
|
||||
.primary-button:hover {
|
||||
@@ -72,243 +230,386 @@
|
||||
-fx-border-color: -fx-primary-dark;
|
||||
}
|
||||
|
||||
.small-button {
|
||||
-fx-font-size: 11px;
|
||||
-fx-padding: 4 12 4 12;
|
||||
.secondary-button {
|
||||
-fx-background-color: -fx-slate-900;
|
||||
-fx-text-fill: white;
|
||||
-fx-border-color: -fx-slate-900;
|
||||
-fx-font-weight: 700;
|
||||
}
|
||||
|
||||
/* ===== Table View Styling ===== */
|
||||
.table-view {
|
||||
-fx-background-color: white;
|
||||
-fx-border-color: #E0E0E0;
|
||||
.secondary-button:hover {
|
||||
-fx-background-color: -fx-slate-800;
|
||||
-fx-border-color: -fx-slate-800;
|
||||
}
|
||||
|
||||
.small-button {
|
||||
-fx-font-size: 11px;
|
||||
-fx-padding: 6 12;
|
||||
-fx-border-radius: 8px;
|
||||
-fx-background-radius: 8px;
|
||||
}
|
||||
|
||||
/* ===== Card & Panel Styling ===== */
|
||||
.card {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-background-radius: 24px;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 24px;
|
||||
-fx-padding: 32;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.05), 4, 0, 0, 2);
|
||||
}
|
||||
|
||||
.config-panel {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-background-radius: 20px;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 20px;
|
||||
-fx-padding: 24;
|
||||
}
|
||||
|
||||
.summary-row {
|
||||
-fx-background-color: -fx-primary-50;
|
||||
-fx-background-radius: 12px;
|
||||
-fx-padding: 12 16;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
-fx-font-weight: 700;
|
||||
-fx-text-fill: -fx-primary;
|
||||
}
|
||||
|
||||
.stats-panel {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-background-radius: 20px;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 20px;
|
||||
-fx-padding: 24;
|
||||
}
|
||||
|
||||
/* ===== Tables ===== */
|
||||
.table-view {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 24px;
|
||||
-fx-background-radius: 24px;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.05), 4, 0, 0, 2);
|
||||
}
|
||||
|
||||
.table-view .column-header {
|
||||
-fx-background-color: -fx-primary-color;
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: bold;
|
||||
-fx-padding: 10;
|
||||
-fx-background-color: derive(-fx-slate-50, 20%);
|
||||
-fx-text-fill: -fx-slate-500;
|
||||
-fx-font-weight: 900;
|
||||
/* black */
|
||||
-fx-font-size: 10px;
|
||||
-fx-padding: 16 24;
|
||||
}
|
||||
|
||||
.table-view .column-header-background {
|
||||
-fx-background-color: -fx-primary-color;
|
||||
-fx-background-color: derive(-fx-slate-50, 20%);
|
||||
}
|
||||
|
||||
.table-view .column-header .label {
|
||||
-fx-text-fill: -fx-slate-500;
|
||||
-fx-font-weight: 900;
|
||||
}
|
||||
|
||||
.table-view .table-cell {
|
||||
-fx-padding: 8;
|
||||
-fx-padding: 20 24;
|
||||
-fx-border-color: transparent;
|
||||
-fx-text-fill: -fx-slate-800;
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
|
||||
.table-row-cell:odd {
|
||||
-fx-background-color: white;
|
||||
.table-row-cell {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-100;
|
||||
-fx-border-width: 0 0 1 0;
|
||||
}
|
||||
|
||||
.table-row-cell:even {
|
||||
-fx-background-color: #F5F5F5;
|
||||
.table-row-cell:filled:hover {
|
||||
-fx-background-color: derive(-fx-primary-50, 30%);
|
||||
}
|
||||
|
||||
.table-row-cell:selected {
|
||||
-fx-background-color: #BBDEFB;
|
||||
.table-row-cell:filled:selected {
|
||||
-fx-background-color: -fx-primary-50;
|
||||
-fx-text-fill: -fx-primary-dark;
|
||||
}
|
||||
|
||||
.table-row-cell:hover {
|
||||
-fx-background-color: #E3F2FD;
|
||||
}
|
||||
|
||||
/* ===== Text Field Styling ===== */
|
||||
.text-field {
|
||||
-fx-border-color: #BDBDBD;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-padding: 8;
|
||||
}
|
||||
|
||||
.text-field:focused {
|
||||
-fx-border-color: -fx-primary-color;
|
||||
-fx-border-width: 2px;
|
||||
}
|
||||
|
||||
/* ===== Text Area Styling ===== */
|
||||
/* ===== Text Inputs ===== */
|
||||
.text-field,
|
||||
.text-area {
|
||||
-fx-border-color: #BDBDBD;
|
||||
-fx-background-color: -fx-slate-100;
|
||||
-fx-border-color: transparent;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-border-radius: 12px;
|
||||
-fx-background-radius: 12px;
|
||||
-fx-padding: 10 16;
|
||||
-fx-font-size: 13px;
|
||||
-fx-text-fill: -fx-slate-800;
|
||||
}
|
||||
|
||||
.text-field:focused,
|
||||
.text-area:focused {
|
||||
-fx-border-color: -fx-primary-color;
|
||||
-fx-border-color: -fx-primary;
|
||||
-fx-border-width: 2px;
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-effect: dropshadow(gaussian, derive(-fx-primary, -20%), 4, 0.3, 0, 0);
|
||||
}
|
||||
|
||||
.text-area .content {
|
||||
-fx-background-color: white;
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
/* ===== Combo Box Styling ===== */
|
||||
.combo-box {
|
||||
-fx-border-color: #BDBDBD;
|
||||
/* ===== Spinner ===== */
|
||||
.spinner {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-border-radius: 12px;
|
||||
-fx-background-radius: 12px;
|
||||
}
|
||||
|
||||
.spinner .text-field {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 0;
|
||||
-fx-background-radius: 12px;
|
||||
}
|
||||
|
||||
/* ===== ComboBox ===== */
|
||||
.combo-box {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 12px;
|
||||
-fx-background-radius: 12px;
|
||||
-fx-padding: 4;
|
||||
}
|
||||
|
||||
.combo-box:focused {
|
||||
-fx-border-color: -fx-primary-color;
|
||||
-fx-border-color: -fx-primary;
|
||||
-fx-border-width: 2px;
|
||||
}
|
||||
|
||||
/* ===== Tab Pane Styling ===== */
|
||||
.tab-pane {
|
||||
.combo-box .list-cell {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: -fx-slate-800;
|
||||
-fx-padding: 8 12;
|
||||
}
|
||||
|
||||
/* ===== DatePicker ===== */
|
||||
.date-picker {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 12px;
|
||||
-fx-background-radius: 12px;
|
||||
}
|
||||
|
||||
.date-picker:focused {
|
||||
-fx-border-color: -fx-primary;
|
||||
-fx-border-width: 2px;
|
||||
}
|
||||
|
||||
.date-picker .text-field {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 0;
|
||||
}
|
||||
|
||||
/* ===== CheckBox ===== */
|
||||
.check-box {
|
||||
-fx-text-fill: -fx-slate-700;
|
||||
-fx-font-weight: 600;
|
||||
}
|
||||
|
||||
.check-box .box {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-300;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-radius: 6px;
|
||||
-fx-background-radius: 6px;
|
||||
}
|
||||
|
||||
.check-box:selected .box {
|
||||
-fx-background-color: -fx-primary;
|
||||
-fx-border-color: -fx-primary;
|
||||
}
|
||||
|
||||
.check-box:selected .mark {
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
.tab {
|
||||
-fx-background-color: #F5F5F5;
|
||||
-fx-background-radius: 0;
|
||||
-fx-padding: 10 20 10 20;
|
||||
/* ===== Progress Bar & Indicator ===== */
|
||||
.progress-bar {
|
||||
-fx-background-color: -fx-slate-100;
|
||||
-fx-background-radius: 12px;
|
||||
}
|
||||
|
||||
.tab:selected {
|
||||
-fx-background-color: white;
|
||||
-fx-border-color: -fx-primary-color transparent transparent transparent;
|
||||
-fx-border-width: 3px 0 0 0;
|
||||
.progress-bar .bar {
|
||||
-fx-background-color: -fx-primary;
|
||||
-fx-background-radius: 12px;
|
||||
-fx-padding: 4px;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
-fx-background-color: #EEEEEE;
|
||||
.progress-indicator {
|
||||
-fx-progress-color: -fx-primary;
|
||||
}
|
||||
|
||||
.tab .tab-label {
|
||||
-fx-text-fill: #616161;
|
||||
-fx-font-weight: normal;
|
||||
/* ===== Scroll Pane & Scroll Bars ===== */
|
||||
.scroll-pane {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: transparent;
|
||||
}
|
||||
|
||||
.tab:selected .tab-label {
|
||||
-fx-text-fill: -fx-primary-dark;
|
||||
-fx-font-weight: bold;
|
||||
.scroll-pane .viewport {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
/* ===== Status Bar Styling ===== */
|
||||
.status-bar {
|
||||
-fx-background-color: #EEEEEE;
|
||||
-fx-border-color: #BDBDBD transparent transparent transparent;
|
||||
-fx-border-width: 1px 0 0 0;
|
||||
.scroll-bar {
|
||||
-fx-background-color: transparent;
|
||||
-fx-padding: 2;
|
||||
}
|
||||
|
||||
/* ===== Detail Panel Styling ===== */
|
||||
.detail-panel {
|
||||
-fx-background-color: #F5F5F5;
|
||||
-fx-border-color: #E0E0E0;
|
||||
-fx-border-width: 0 0 0 1px;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.1), 10, 0, -2, 0);
|
||||
.scroll-bar .thumb {
|
||||
-fx-background-color: -fx-slate-300;
|
||||
-fx-background-radius: 8px;
|
||||
}
|
||||
|
||||
/* ===== Summary Box Styling ===== */
|
||||
.summary-box {
|
||||
-fx-background-color: #E3F2FD;
|
||||
-fx-padding: 10;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
.scroll-bar .thumb:hover {
|
||||
-fx-background-color: -fx-slate-400;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: -fx-primary-dark;
|
||||
.scroll-bar .track {
|
||||
-fx-background-color: -fx-slate-100;
|
||||
-fx-background-radius: 8px;
|
||||
}
|
||||
|
||||
/* ===== Schedule Grid ===== */
|
||||
.schedule-grid {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 24px;
|
||||
-fx-background-radius: 24px;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.05), 4, 0, 0, 2);
|
||||
}
|
||||
|
||||
.grid-header {
|
||||
-fx-background-color: derive(-fx-slate-50, 20%);
|
||||
-fx-font-weight: 900;
|
||||
-fx-font-size: 11px;
|
||||
-fx-text-fill: -fx-slate-500;
|
||||
-fx-padding: 16;
|
||||
-fx-alignment: center;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-padding: 12;
|
||||
-fx-alignment: top-left;
|
||||
-fx-border-color: -fx-slate-100;
|
||||
-fx-border-width: 0 1 1 0;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: -fx-slate-400;
|
||||
-fx-font-style: italic;
|
||||
-fx-padding: 40;
|
||||
}
|
||||
|
||||
/* ===== Status Labels ===== */
|
||||
.status-label {
|
||||
-fx-font-size: 12px;
|
||||
-fx-font-style: italic;
|
||||
-fx-font-size: 13px;
|
||||
-fx-font-weight: 600;
|
||||
-fx-text-fill: -fx-slate-600;
|
||||
}
|
||||
|
||||
/* ===== Separator Styling ===== */
|
||||
/* ===== Separators ===== */
|
||||
.separator {
|
||||
-fx-background-color: #E0E0E0;
|
||||
-fx-background-color: -fx-slate-200;
|
||||
}
|
||||
|
||||
/* ===== Date Picker Styling ===== */
|
||||
.date-picker {
|
||||
-fx-border-color: #BDBDBD;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
.separator .line {
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px 0 0 0;
|
||||
}
|
||||
|
||||
.date-picker:focused {
|
||||
-fx-border-color: -fx-primary-color;
|
||||
/* ===== Tab Pane (if used) ===== */
|
||||
.tab-pane {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: transparent;
|
||||
}
|
||||
|
||||
/* ===== Grid Pane for Calendar ===== */
|
||||
.schedule-grid {
|
||||
-fx-background-color: white;
|
||||
-fx-border-color: #BDBDBD;
|
||||
-fx-border-width: 1px;
|
||||
.tab {
|
||||
-fx-background-color: transparent;
|
||||
-fx-background-radius: 12px 12px 0 0;
|
||||
-fx-padding: 12 24;
|
||||
-fx-border-width: 0;
|
||||
}
|
||||
|
||||
.grid-header {
|
||||
-fx-background-color: #E3F2FD;
|
||||
-fx-font-weight: bold;
|
||||
-fx-padding: 10;
|
||||
-fx-alignment: center;
|
||||
-fx-min-width: 100;
|
||||
-fx-min-height: 40;
|
||||
.tab:selected {
|
||||
-fx-background-color: -fx-primary-50;
|
||||
-fx-border-color: -fx-primary;
|
||||
-fx-border-width: 0 0 3 0;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
-fx-padding: 10;
|
||||
-fx-alignment: center;
|
||||
-fx-min-width: 100;
|
||||
-fx-min-height: 60;
|
||||
-fx-background-color: white;
|
||||
.tab .tab-label {
|
||||
-fx-text-fill: -fx-slate-500;
|
||||
-fx-font-weight: 600;
|
||||
-fx-font-size: 13px;
|
||||
}
|
||||
|
||||
/* ===== List View Styling ===== */
|
||||
.tab:selected .tab-label {
|
||||
-fx-text-fill: -fx-primary;
|
||||
-fx-font-weight: 900;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
-fx-background-color: -fx-slate-100;
|
||||
}
|
||||
|
||||
/* ===== Tooltips ===== */
|
||||
.tooltip {
|
||||
-fx-background-color: -fx-slate-900;
|
||||
-fx-text-fill: white;
|
||||
-fx-font-size: 12px;
|
||||
-fx-font-weight: 600;
|
||||
-fx-padding: 8 12;
|
||||
-fx-background-radius: 8px;
|
||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.3), 8, 0.2, 0, 4);
|
||||
}
|
||||
|
||||
/* ===== List View ===== */
|
||||
.list-view {
|
||||
-fx-border-color: #E0E0E0;
|
||||
-fx-background-color: -fx-surface;
|
||||
-fx-border-color: -fx-slate-200;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 16px;
|
||||
-fx-background-radius: 16px;
|
||||
}
|
||||
|
||||
.list-cell {
|
||||
-fx-padding: 8;
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: -fx-slate-800;
|
||||
-fx-padding: 12 16;
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
|
||||
.list-cell:filled:selected {
|
||||
-fx-background-color: -fx-primary-color;
|
||||
-fx-background-color: -fx-primary;
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: 700;
|
||||
}
|
||||
|
||||
.list-cell:filled:hover {
|
||||
-fx-background-color: #E3F2FD;
|
||||
-fx-background-color: -fx-primary-50;
|
||||
}
|
||||
|
||||
/* ===== Scroll Bar Styling ===== */
|
||||
.scroll-bar {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.scroll-bar .thumb {
|
||||
-fx-background-color: #BDBDBD;
|
||||
-fx-background-radius: 4px;
|
||||
}
|
||||
|
||||
.scroll-bar .thumb:hover {
|
||||
-fx-background-color: #9E9E9E;
|
||||
}
|
||||
|
||||
/* ===== Progress Bar ===== */
|
||||
.progress-bar {
|
||||
-fx-accent: -fx-primary-color;
|
||||
}
|
||||
|
||||
/* ===== Label Link Style ===== */
|
||||
.label-link {
|
||||
-fx-text-fill: -fx-primary-color;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.label-link:hover {
|
||||
-fx-underline: true;
|
||||
/* ===== Animations & Transitions ===== */
|
||||
* {
|
||||
-fx-transition: all 0.2s ease-in-out;
|
||||
}
|
||||
@@ -9,71 +9,108 @@
|
||||
fx:controller="org.example.se302.controller.ImportController"
|
||||
fitToWidth="true" fitToHeight="true"
|
||||
hbarPolicy="AS_NEEDED" vbarPolicy="AS_NEEDED">
|
||||
<VBox spacing="20" styleClass="content-pane">
|
||||
<VBox spacing="24" styleClass="content-pane">
|
||||
<padding>
|
||||
<Insets top="20" right="20" bottom="20" left="20"/>
|
||||
<Insets top="40" right="40" bottom="40" left="40"/>
|
||||
</padding>
|
||||
|
||||
<!-- Header -->
|
||||
<Label text="Import Data from CSV Files" styleClass="section-title"/>
|
||||
<Label text="Select CSV files to import student, course, classroom, and enrollment data." wrapText="true"/>
|
||||
|
||||
<Separator/>
|
||||
|
||||
<!-- Students Import Section -->
|
||||
<VBox spacing="8">
|
||||
<Label text="Student Data" styleClass="subsection-title"/>
|
||||
<HBox spacing="10" alignment="CENTER_LEFT">
|
||||
<TextField fx:id="studentFileField" promptText="No file selected" editable="false" HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="studentBrowseButton" text="Browse..." onAction="#onBrowseStudents"/>
|
||||
</HBox>
|
||||
<Label fx:id="studentStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Courses Import Section -->
|
||||
<VBox spacing="8">
|
||||
<Label text="Course Data" styleClass="subsection-title"/>
|
||||
<HBox spacing="10" alignment="CENTER_LEFT">
|
||||
<TextField fx:id="courseFileField" promptText="No file selected" editable="false" HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="courseBrowseButton" text="Browse..." onAction="#onBrowseCourses"/>
|
||||
</HBox>
|
||||
<Label fx:id="courseStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Classrooms Import Section -->
|
||||
<VBox spacing="8">
|
||||
<Label text="Classroom Data" styleClass="subsection-title"/>
|
||||
<HBox spacing="10" alignment="CENTER_LEFT">
|
||||
<TextField fx:id="classroomFileField" promptText="No file selected" editable="false" HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="classroomBrowseButton" text="Browse..." onAction="#onBrowseClassrooms"/>
|
||||
</HBox>
|
||||
<Label fx:id="classroomStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Enrollments Import Section -->
|
||||
<VBox spacing="8">
|
||||
<Label text="Enrollment Data" styleClass="subsection-title"/>
|
||||
<HBox spacing="10" alignment="CENTER_LEFT">
|
||||
<TextField fx:id="enrollmentFileField" promptText="No file selected" editable="false" HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="enrollmentBrowseButton" text="Browse..." onAction="#onBrowseEnrollments"/>
|
||||
</HBox>
|
||||
<Label fx:id="enrollmentStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
<Label text="📁 Data Integration" styleClass="section-title"/>
|
||||
<Label text="Upload CSV files to initialize the exam scheduling process."
|
||||
styleClass="description-label" wrapText="true"/>
|
||||
</VBox>
|
||||
|
||||
<Separator/>
|
||||
|
||||
<!-- Import Messages Area -->
|
||||
<VBox spacing="5" VBox.vgrow="ALWAYS">
|
||||
<!-- Import Cards Grid -->
|
||||
<GridPane hgap="24" vgap="24">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints percentWidth="50" hgrow="ALWAYS"/>
|
||||
<ColumnConstraints percentWidth="50" hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
|
||||
<!-- Student Data Card -->
|
||||
<VBox spacing="16" styleClass="card" GridPane.columnIndex="0" GridPane.rowIndex="0">
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="👤" style="-fx-font-size: 32px;"/>
|
||||
<VBox>
|
||||
<Label text="Student Data" styleClass="subsection-title"/>
|
||||
<Label text="students.csv" styleClass="status-label"/>
|
||||
</VBox>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="studentBrowseButton" text="Browse..." onAction="#onBrowseStudents"
|
||||
styleClass="secondary-button, small-button"/>
|
||||
</HBox>
|
||||
<TextField fx:id="studentFileField" promptText="No file selected" editable="false"/>
|
||||
<Label fx:id="studentStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Course Data Card -->
|
||||
<VBox spacing="16" styleClass="card" GridPane.columnIndex="1" GridPane.rowIndex="0">
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="📚" style="-fx-font-size: 32px;"/>
|
||||
<VBox>
|
||||
<Label text="Course Data" styleClass="subsection-title"/>
|
||||
<Label text="courses.csv" styleClass="status-label"/>
|
||||
</VBox>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="courseBrowseButton" text="Browse..." onAction="#onBrowseCourses"
|
||||
styleClass="secondary-button, small-button"/>
|
||||
</HBox>
|
||||
<TextField fx:id="courseFileField" promptText="No file selected" editable="false"/>
|
||||
<Label fx:id="courseStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Classroom Data Card -->
|
||||
<VBox spacing="16" styleClass="card" GridPane.columnIndex="0" GridPane.rowIndex="1">
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="🏛️" style="-fx-font-size: 32px;"/>
|
||||
<VBox>
|
||||
<Label text="Classroom Data" styleClass="subsection-title"/>
|
||||
<Label text="classrooms.csv" styleClass="status-label"/>
|
||||
</VBox>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="classroomBrowseButton" text="Browse..." onAction="#onBrowseClassrooms"
|
||||
styleClass="secondary-button, small-button"/>
|
||||
</HBox>
|
||||
<TextField fx:id="classroomFileField" promptText="No file selected" editable="false"/>
|
||||
<Label fx:id="classroomStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Enrollment Data Card -->
|
||||
<VBox spacing="16" styleClass="card" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="📝" style="-fx-font-size: 32px;"/>
|
||||
<VBox>
|
||||
<Label text="Enrollment Data" styleClass="subsection-title"/>
|
||||
<Label text="enrollments.csv" styleClass="status-label"/>
|
||||
</VBox>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="enrollmentBrowseButton" text="Browse..." onAction="#onBrowseEnrollments"
|
||||
styleClass="secondary-button, small-button"/>
|
||||
</HBox>
|
||||
<TextField fx:id="enrollmentFileField" promptText="No file selected" editable="false"/>
|
||||
<Label fx:id="enrollmentStatusLabel" text="Not Loaded" styleClass="status-label"/>
|
||||
</VBox>
|
||||
</GridPane>
|
||||
|
||||
<Separator/>
|
||||
|
||||
<!-- Import Log -->
|
||||
<VBox spacing="12" VBox.vgrow="ALWAYS" styleClass="card">
|
||||
<Label text="Import Messages" styleClass="subsection-title"/>
|
||||
<TextArea fx:id="messagesArea" editable="false" wrapText="true" VBox.vgrow="ALWAYS"
|
||||
promptText="Validation messages and import results will appear here..."/>
|
||||
prefHeight="200"
|
||||
promptText="Validation messages and import results will appear here..."
|
||||
style="-fx-font-family: 'Consolas', 'Monaco', monospace; -fx-font-size: 12px;"/>
|
||||
</VBox>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<HBox spacing="10" alignment="CENTER_RIGHT">
|
||||
<Button fx:id="importAllButton" text="Import All" onAction="#onImportAll"
|
||||
styleClass="primary-button" disable="true"/>
|
||||
<HBox spacing="12" alignment="CENTER_RIGHT">
|
||||
<Button text="Clear All" onAction="#onClearAll"/>
|
||||
<Button fx:id="importAllButton" text="📥 Import All" onAction="#onImportAll"
|
||||
styleClass="primary-button" disable="true"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
|
||||
@@ -9,10 +9,17 @@
|
||||
fx:controller="org.example.se302.controller.MainController"
|
||||
prefHeight="800.0" prefWidth="1200.0">
|
||||
|
||||
<!-- Top: Title Bar -->
|
||||
<!-- Top: Title Bar with Theme Toggle -->
|
||||
<top>
|
||||
<VBox styleClass="header">
|
||||
<Label text="Exam Scheduling System" styleClass="title-label"/>
|
||||
<HBox alignment="CENTER_LEFT" spacing="20">
|
||||
<padding>
|
||||
<Insets top="10" right="20" bottom="10" left="20"/>
|
||||
</padding>
|
||||
<Label text="📅 Exam Scheduling System" styleClass="title-label" HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="themeToggleButton" text="🌙 Dark Mode" onAction="#onToggleTheme"
|
||||
style="-fx-background-color: rgba(255,255,255,0.2); -fx-text-fill: white; -fx-border-color: rgba(255,255,255,0.3); -fx-border-width: 1; -fx-border-radius: 8; -fx-background-radius: 8; -fx-padding: 8 16; -fx-font-weight: 700;"/>
|
||||
</HBox>
|
||||
<Separator/>
|
||||
</VBox>
|
||||
</top>
|
||||
@@ -21,27 +28,27 @@
|
||||
<center>
|
||||
<TabPane fx:id="mainTabPane" tabClosingPolicy="UNAVAILABLE">
|
||||
<!-- Import Data Tab -->
|
||||
<Tab fx:id="importTab" text="Import Data">
|
||||
<Tab fx:id="importTab" text="📁 Import Data">
|
||||
<fx:include source="import-view.fxml"/>
|
||||
</Tab>
|
||||
|
||||
<!-- Students Tab -->
|
||||
<Tab fx:id="studentsTab" text="Students">
|
||||
<Tab fx:id="studentsTab" text="👤 Students">
|
||||
<fx:include source="students-view.fxml"/>
|
||||
</Tab>
|
||||
|
||||
<!-- Courses Tab -->
|
||||
<Tab fx:id="coursesTab" text="Courses">
|
||||
<Tab fx:id="coursesTab" text="📚 Courses">
|
||||
<fx:include source="courses-view.fxml"/>
|
||||
</Tab>
|
||||
|
||||
<!-- Classrooms Tab -->
|
||||
<Tab fx:id="classroomsTab" text="Classrooms">
|
||||
<Tab fx:id="classroomsTab" text="🏛️ Classrooms">
|
||||
<fx:include source="classrooms-view.fxml"/>
|
||||
</Tab>
|
||||
|
||||
<!-- Schedule Views Tab -->
|
||||
<Tab fx:id="scheduleTab" text="Schedule Views">
|
||||
<Tab fx:id="scheduleTab" text="📅 Schedule Views">
|
||||
<fx:include source="schedule-views.fxml"/>
|
||||
</Tab>
|
||||
</TabPane>
|
||||
@@ -51,9 +58,11 @@
|
||||
<bottom>
|
||||
<HBox styleClass="status-bar" spacing="10">
|
||||
<padding>
|
||||
<Insets top="5" right="10" bottom="5" left="10"/>
|
||||
<Insets top="8" right="20" bottom="8" left="20"/>
|
||||
</padding>
|
||||
<Label fx:id="statusLabel" text="Ready - No data loaded"/>
|
||||
<Label text="🟢" style="-fx-font-size: 10px;"/>
|
||||
<Label fx:id="statusLabel" text="Ready - No data loaded" HBox.hgrow="ALWAYS"/>
|
||||
<Label text="ExamFlow v2.0" style="-fx-font-size: 11px; -fx-text-fill: -fx-slate-400; -fx-font-weight: 700;"/>
|
||||
</HBox>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
|
||||
@@ -12,125 +12,131 @@
|
||||
|
||||
<BorderPane styleClass="content-pane">
|
||||
<top>
|
||||
<VBox spacing="15">
|
||||
<VBox spacing="20">
|
||||
<padding>
|
||||
<Insets top="20" right="20" bottom="10" left="20"/>
|
||||
<Insets top="40" right="40" bottom="20" left="40"/>
|
||||
</padding>
|
||||
|
||||
<!-- Page Title -->
|
||||
<Label text="📅 Calendar View - Exam Schedule Generator" styleClass="section-title"/>
|
||||
<Label text="Configure and generate an optimized exam schedule using CSP algorithm."
|
||||
wrapText="true" styleClass="description-label"/>
|
||||
<VBox spacing="8">
|
||||
<Label text="📅 Auto Schedule Generator" styleClass="section-title"/>
|
||||
<Label text="Configure and generate an optimized exam schedule using CSP algorithm with intelligent heuristics."
|
||||
wrapText="true" styleClass="description-label"/>
|
||||
</VBox>
|
||||
|
||||
<Separator/>
|
||||
|
||||
<!-- Configuration Panel -->
|
||||
<VBox spacing="15" styleClass="config-panel">
|
||||
<Label text="Schedule Configuration" styleClass="subsection-title"/>
|
||||
<VBox spacing="20" styleClass="config-panel">
|
||||
<Label text="⚙️ Schedule Configuration" styleClass="subsection-title"/>
|
||||
|
||||
<!-- Row 1: Days and Slots -->
|
||||
<HBox spacing="20" alignment="CENTER_LEFT">
|
||||
<VBox spacing="5">
|
||||
<Label text="Number of Days:"/>
|
||||
<Spinner fx:id="numDaysSpinner" min="1" max="30" initialValue="5"
|
||||
editable="true" prefWidth="100"/>
|
||||
<!-- Row 1: Time Configuration -->
|
||||
<GridPane hgap="16" vgap="12">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints percentWidth="25"/>
|
||||
<ColumnConstraints percentWidth="25"/>
|
||||
<ColumnConstraints percentWidth="25"/>
|
||||
<ColumnConstraints percentWidth="25"/>
|
||||
</columnConstraints>
|
||||
|
||||
<VBox spacing="6" GridPane.columnIndex="0" GridPane.rowIndex="0">
|
||||
<Label text="Number of Days" styleClass="stat-label"/>
|
||||
<Spinner fx:id="numDaysSpinner" editable="true" prefWidth="120"/>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="5">
|
||||
<Label text="Slots per Day:"/>
|
||||
<Spinner fx:id="slotsPerDaySpinner" min="1" max="10" initialValue="4"
|
||||
editable="true" prefWidth="100"/>
|
||||
<VBox spacing="6" GridPane.columnIndex="1" GridPane.rowIndex="0">
|
||||
<Label text="Slots per Day" styleClass="stat-label"/>
|
||||
<Spinner fx:id="slotsPerDaySpinner" editable="true" prefWidth="120"/>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="5">
|
||||
<Label text="Start Date:"/>
|
||||
<DatePicker fx:id="startDatePicker" promptText="Select Start Date" prefWidth="150"/>
|
||||
<VBox spacing="6" GridPane.columnIndex="2" GridPane.rowIndex="0">
|
||||
<Label text="Start Date" styleClass="stat-label"/>
|
||||
<DatePicker fx:id="startDatePicker" promptText="Select Date" prefWidth="150"/>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="5">
|
||||
<Label text="Slot Duration (min):"/>
|
||||
<Spinner fx:id="slotDurationSpinner" min="30" max="240" initialValue="120"
|
||||
editable="true" prefWidth="100"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
<!-- Row 2: Strategy and Options -->
|
||||
<HBox spacing="20" alignment="CENTER_LEFT">
|
||||
<VBox spacing="5">
|
||||
<Label text="Optimization Strategy:"/>
|
||||
<ComboBox fx:id="strategyComboBox" prefWidth="200" promptText="Select Strategy"/>
|
||||
<VBox spacing="6" GridPane.columnIndex="3" GridPane.rowIndex="0">
|
||||
<Label text="Slot Duration (min)" styleClass="stat-label"/>
|
||||
<Spinner fx:id="slotDurationSpinner" editable="true" prefWidth="120"/>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="5">
|
||||
<Label text="Day Start Time:"/>
|
||||
<!-- Row 2: Strategy Configuration -->
|
||||
<VBox spacing="6" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2">
|
||||
<Label text="Optimization Strategy" styleClass="stat-label"/>
|
||||
<ComboBox fx:id="strategyComboBox" prefWidth="250" promptText="Select Strategy"/>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="6" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||
<Label text="Day Start Time" styleClass="stat-label"/>
|
||||
<ComboBox fx:id="startTimeComboBox" prefWidth="120" promptText="09:00"/>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="5" alignment="CENTER_LEFT">
|
||||
<padding>
|
||||
<Insets top="18"/>
|
||||
</padding>
|
||||
<CheckBox fx:id="allowBackToBackCheckBox" text="Allow back-to-back exams" selected="true"/>
|
||||
<VBox spacing="6" alignment="CENTER_LEFT" GridPane.columnIndex="3" GridPane.rowIndex="1">
|
||||
<Label text="Options" styleClass="stat-label"/>
|
||||
<CheckBox fx:id="allowBackToBackCheckBox" text="Allow back-to-back" selected="true"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
</GridPane>
|
||||
|
||||
<!-- Row 3: Summary -->
|
||||
<HBox spacing="10" alignment="CENTER_LEFT" styleClass="summary-row">
|
||||
<Label text="Configuration Summary:"/>
|
||||
<Label fx:id="summaryLabel" text="5 days × 4 slots = 20 total time slots"
|
||||
<!-- Summary -->
|
||||
<HBox spacing="12" alignment="CENTER_LEFT" styleClass="summary-row">
|
||||
<Label text="📊 Total Time Slots:" style="-fx-font-weight: 700;"/>
|
||||
<Label fx:id="summaryLabel" text="5 days × 4 slots = 20 slots"
|
||||
styleClass="summary-value"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
|
||||
<Separator/>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<HBox spacing="15" alignment="CENTER_LEFT">
|
||||
<!-- Action Bar -->
|
||||
<HBox spacing="16" alignment="CENTER_LEFT">
|
||||
<Button fx:id="generateButton" text="🚀 Generate Schedule"
|
||||
onAction="#onGenerateSchedule" styleClass="primary-button" prefWidth="180"/>
|
||||
onAction="#onGenerateSchedule" styleClass="primary-button"/>
|
||||
<Button fx:id="cancelButton" text="Cancel" onAction="#onCancelGeneration"
|
||||
disable="true" prefWidth="100"/>
|
||||
disable="true"/>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<Label fx:id="statusLabel" text="Ready" styleClass="status-label"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Progress Container with Indicator -->
|
||||
<VBox fx:id="progressContainer" spacing="8" visible="false" managed="false"
|
||||
style="-fx-background-color: #f8f9fa; -fx-padding: 15; -fx-background-radius: 5;">
|
||||
<HBox spacing="15" alignment="CENTER_LEFT">
|
||||
<ProgressIndicator fx:id="progressIndicator" prefWidth="30" prefHeight="30"/>
|
||||
<VBox spacing="3">
|
||||
<!-- Progress Container -->
|
||||
<VBox fx:id="progressContainer" spacing="12" visible="false" managed="false"
|
||||
styleClass="card">
|
||||
<HBox spacing="16" alignment="CENTER_LEFT">
|
||||
<ProgressIndicator fx:id="progressIndicator" prefWidth="32" prefHeight="32"/>
|
||||
<VBox spacing="4" HBox.hgrow="ALWAYS">
|
||||
<Label fx:id="progressLabel" text="Initializing..."
|
||||
style="-fx-font-weight: bold; -fx-font-size: 13;"/>
|
||||
<ProgressBar fx:id="progressBar" prefWidth="350" progress="0"/>
|
||||
style="-fx-font-weight: 700; -fx-font-size: 14px;"/>
|
||||
<ProgressBar fx:id="progressBar" prefWidth="400" progress="0" maxWidth="Infinity"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
<Label fx:id="progressDetailLabel" text="Please wait..."
|
||||
style="-fx-text-fill: #7f8c8d; -fx-font-size: 11;"/>
|
||||
style="-fx-text-fill: -fx-slate-600; -fx-font-size: 12px;"/>
|
||||
</VBox>
|
||||
</VBox>
|
||||
</top>
|
||||
|
||||
<center>
|
||||
<VBox spacing="15">
|
||||
<VBox spacing="20">
|
||||
<padding>
|
||||
<Insets top="20" right="20" bottom="20" left="20"/>
|
||||
<Insets top="20" right="40" bottom="40" left="40"/>
|
||||
</padding>
|
||||
|
||||
<!-- Schedule Grid Container -->
|
||||
<Label text="Generated Schedule" styleClass="subsection-title"/>
|
||||
<!-- Schedule Grid Header -->
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="📆 Generated Schedule" styleClass="subsection-title"/>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<Button text="📥 Export PDF" styleClass="small-button"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Schedule Grid -->
|
||||
<ScrollPane fx:id="scheduleScrollPane" fitToWidth="true" fitToHeight="false"
|
||||
prefHeight="500" minHeight="300"
|
||||
hbarPolicy="ALWAYS" vbarPolicy="ALWAYS"
|
||||
VBox.vgrow="ALWAYS"
|
||||
style="-fx-background-color: white; -fx-border-color: #bdc3c7; -fx-border-width: 1;">
|
||||
style="-fx-background: -fx-surface; -fx-border-color: -fx-slate-200; -fx-border-width: 1; -fx-border-radius: 24; -fx-background-radius: 24;">
|
||||
<GridPane fx:id="scheduleGrid" gridLinesVisible="true" styleClass="schedule-grid"
|
||||
minWidth="800" minHeight="400">
|
||||
<padding>
|
||||
<Insets top="10" right="10" bottom="10" left="10"/>
|
||||
</padding>
|
||||
<!-- Grid will be populated dynamically -->
|
||||
<Label text="Click 'Generate Schedule' to create an exam schedule"
|
||||
GridPane.columnIndex="0" GridPane.rowIndex="0"
|
||||
styleClass="placeholder-text" wrapText="true"/>
|
||||
@@ -138,23 +144,23 @@
|
||||
</ScrollPane>
|
||||
|
||||
<!-- Statistics Panel -->
|
||||
<HBox spacing="30" alignment="CENTER_LEFT" styleClass="stats-panel">
|
||||
<VBox spacing="3" alignment="CENTER">
|
||||
<HBox spacing="24" alignment="CENTER_LEFT" styleClass="stats-panel">
|
||||
<VBox spacing="4" alignment="CENTER">
|
||||
<Label fx:id="totalCoursesLabel" text="0" styleClass="stat-value"/>
|
||||
<Label text="Total Courses" styleClass="stat-label"/>
|
||||
</VBox>
|
||||
<Separator orientation="VERTICAL"/>
|
||||
<VBox spacing="3" alignment="CENTER">
|
||||
<VBox spacing="4" alignment="CENTER">
|
||||
<Label fx:id="scheduledCoursesLabel" text="0" styleClass="stat-value"/>
|
||||
<Label text="Scheduled" styleClass="stat-label"/>
|
||||
</VBox>
|
||||
<Separator orientation="VERTICAL"/>
|
||||
<VBox spacing="3" alignment="CENTER">
|
||||
<VBox spacing="4" alignment="CENTER">
|
||||
<Label fx:id="classroomsUsedLabel" text="0" styleClass="stat-value"/>
|
||||
<Label text="Classrooms Used" styleClass="stat-label"/>
|
||||
</VBox>
|
||||
<Separator orientation="VERTICAL"/>
|
||||
<VBox spacing="3" alignment="CENTER">
|
||||
<VBox spacing="4" alignment="CENTER">
|
||||
<Label fx:id="generationTimeLabel" text="-" styleClass="stat-value"/>
|
||||
<Label text="Generation Time" styleClass="stat-label"/>
|
||||
</VBox>
|
||||
|
||||
Reference in New Issue
Block a user