Implement initial application UI including schedule generation and import views, a main controller, and update Maven compiler

This commit is contained in:
feyzagereme
2025-12-17 19:16:43 +03:00
parent cbf1d0b13c
commit ce556e92fd
6 changed files with 702 additions and 324 deletions

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>