feat: Added teachers
This commit is contained in:
Generated
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="com.mituuz.fuzzier.FuzzierSettings">
|
||||||
|
<option name="modules">
|
||||||
|
<map>
|
||||||
|
<entry key="jecnaclient" value="$PROJECT_DIR$" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
<option name="recentlySearchedFiles" value="rO0ABXNyABxqYXZheC5zd2luZy5EZWZhdWx0TGlzdE1vZGVsBgfGCGLvV2ICAAFMAAhkZWxlZ2F0ZXQAEkxqYXZhL3V0aWwvVmVjdG9yO3hyAB1qYXZheC5zd2luZy5BYnN0cmFjdExpc3RNb2RlbFrW+oYSs63tAgABTAAMbGlzdGVuZXJMaXN0dAAlTGphdmF4L3N3aW5nL2V2ZW50L0V2ZW50TGlzdGVuZXJMaXN0O3hwc3IAI2phdmF4LnN3aW5nLmV2ZW50LkV2ZW50TGlzdGVuZXJMaXN0kUjMLXPfDt4DAAB4cHB4c3IAEGphdmEudXRpbC5WZWN0b3LZl31bgDuvAQMAA0kAEWNhcGFjaXR5SW5jcmVtZW50SQAMZWxlbWVudENvdW50WwALZWxlbWVudERhdGF0ABNbTGphdmEvbGFuZy9PYmplY3Q7eHAAAAAAAAAAAXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAApzcgBIY29tLm1pdHV1ei5mdXp6aWVyLmVudGl0aWVzLkZ1enp5TWF0Y2hDb250YWluZXIkU2VyaWFsaXplZE1hdGNoQ29udGFpbmVy2S8sP5uny74CAARMAAhmaWxlUGF0aHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wACGZpbGVuYW1lcQB+AA1MAA5tb2R1bGVCYXNlUGF0aHEAfgANTAAFc2NvcmV0ADxMY29tL21pdHV1ei9mdXp6aWVyL2VudGl0aWVzL0Z1enp5TWF0Y2hDb250YWluZXIkRnV6enlTY29yZTt4cHQAQy9zcmMvbWFpbi9qYXZhL2N6L2p6aXRuaWsvY29udHJvbGxlcnMvVGVhY2hlclByb2ZpbGVDb250cm9sbGVyLmphdmF0AB1UZWFjaGVyUHJvZmlsZUNvbnRyb2xsZXIuamF2YXQAHS9ob21lL2t1YmEvQ29kaW5nL2plY25hY2xpZW50c3IAOmNvbS5taXR1dXouZnV6emllci5lbnRpdGllcy5GdXp6eU1hdGNoQ29udGFpbmVyJEZ1enp5U2NvcmUvP9P07ROG2QIABUkADWZpbGVuYW1lU2NvcmVJAA9tdWx0aU1hdGNoU2NvcmVJABBwYXJ0aWFsUGF0aFNjb3JlSQALc3RyZWFrU2NvcmVMABNoaWdobGlnaHRDaGFyYWN0ZXJzdAAPTGphdmEvdXRpbC9TZXQ7eHAAAAAVAAAAAAAAAAAAAAATc3IAEWphdmEudXRpbC5IYXNoU2V0ukSFlZa4tzQDAAB4cHcMAAAAID9AAAAAAAAVc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAABzcQB+ABgAAAABc3EAfgAYAAAAAnNxAH4AGAAAAANzcQB+ABgAAAAEc3EAfgAYAAAABXNxAH4AGAAAAAZzcQB+ABgAAAAHc3EAfgAYAAAACHNxAH4AGAAAAAlzcQB+ABgAAAAKc3EAfgAYAAAAC3NxAH4AGAAAAAxzcQB+ABgAAAANc3EAfgAYAAAADnNxAH4AGAAAAA9zcQB+ABgAAAAQc3EAfgAYAAAAEXNxAH4AGAAAABJzcQB+ABgAAAATc3EAfgAYAAAAFHhwcHBwcHBwcHB4" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package cz.jzitnik.controllers;
|
||||||
|
|
||||||
|
import cz.jzitnik.router.Route;
|
||||||
|
import cz.jzitnik.router.Router;
|
||||||
|
import cz.jzitnik.query.QueryOptions;
|
||||||
|
import cz.jzitnik.util.ImageFetcher;
|
||||||
|
import io.github.tomhula.jecnaapi.data.schoolStaff.Teacher;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Route(path = "/teacher_profile", fxml = "/teacher_profile.fxml")
|
||||||
|
public class TeacherProfileController extends DashboardBaseController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ProgressIndicator loadingIndicator;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ScrollPane scrollPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label headerNameLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label fullNameLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label tagLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private GridPane detailsGrid;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private GridPane timetableGrid;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView avatarView;
|
||||||
|
|
||||||
|
private String teacherTag;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNavigate(Map<String, Object> props) {
|
||||||
|
super.onNavigate(props);
|
||||||
|
|
||||||
|
teacherTag = (String) props.get("tag");
|
||||||
|
if (teacherTag == null || teacherTag.isEmpty()) {
|
||||||
|
onBack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTeacher();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTeacher() {
|
||||||
|
loadingIndicator.setVisible(true);
|
||||||
|
scrollPane.setVisible(false);
|
||||||
|
detailsGrid.getChildren().clear();
|
||||||
|
|
||||||
|
appState.getQueryClient().fetch("teacher:" + teacherTag, () -> {
|
||||||
|
try {
|
||||||
|
return appState.getClient().getTeacher(teacherTag).join();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, QueryOptions.defaultOptions()).thenAccept(result -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
loadingIndicator.setVisible(false);
|
||||||
|
|
||||||
|
if (result.isSuccess() && result.getData().isPresent()) {
|
||||||
|
renderTeacher(result.getData().get());
|
||||||
|
scrollPane.setVisible(true);
|
||||||
|
} else {
|
||||||
|
headerNameLabel.setText("Chyba");
|
||||||
|
detailsGrid.add(new Label("Failed to load teacher details."), 0, 0);
|
||||||
|
scrollPane.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderTeacher(Teacher teacher) {
|
||||||
|
headerNameLabel.setText(teacher.getFullName());
|
||||||
|
fullNameLabel.setText(teacher.getFullName());
|
||||||
|
tagLabel.setText(teacher.getTag());
|
||||||
|
|
||||||
|
if (teacher.getProfilePicturePath() != null && !teacher.getProfilePicturePath().isEmpty()) {
|
||||||
|
new Thread(() -> {
|
||||||
|
Image image = ImageFetcher.fetchImage(teacher.getProfilePicturePath());
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
avatarView.setImage(image);
|
||||||
|
});
|
||||||
|
}).start();
|
||||||
|
} else {
|
||||||
|
avatarView.setImage(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
row = addDetailRow("Kabinet", teacher.getCabinet(), row);
|
||||||
|
row = addDetailRow("Školní email", teacher.getSchoolMail(), row);
|
||||||
|
row = addDetailRow("Soukromý email", teacher.getPrivateMail(), row);
|
||||||
|
|
||||||
|
if (!teacher.getPhoneNumbers().isEmpty()) {
|
||||||
|
row = addDetailRow("Telefon", String.join(", ", teacher.getPhoneNumbers()), row);
|
||||||
|
}
|
||||||
|
|
||||||
|
row = addDetailRow("Pevná linka", teacher.getLandline(), row);
|
||||||
|
row = addDetailRow("Soukromý telefon", teacher.getPrivatePhoneNumber(), row);
|
||||||
|
row = addDetailRow("Třídní", teacher.getTutorOfClass(), row);
|
||||||
|
row = addDetailRow("Konzultační hodiny", teacher.getConsultationHours(), row);
|
||||||
|
|
||||||
|
if (timetableGrid != null) {
|
||||||
|
if (teacher.getTimetable() != null) {
|
||||||
|
cz.jzitnik.util.TimetableRenderer.renderTimetable(timetableGrid, teacher.getTimetable());
|
||||||
|
} else {
|
||||||
|
timetableGrid.getChildren().clear();
|
||||||
|
timetableGrid.getColumnConstraints().clear();
|
||||||
|
timetableGrid.getRowConstraints().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int addDetailRow(String labelText, String value, int row) {
|
||||||
|
if (value == null || value.isBlank()) {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
Label lbl = new Label(labelText + ":");
|
||||||
|
lbl.getStyleClass().add("text-muted");
|
||||||
|
|
||||||
|
Label val = new Label(value);
|
||||||
|
val.getStyleClass().addAll("text-light", "text-bold");
|
||||||
|
val.setWrapText(true);
|
||||||
|
|
||||||
|
detailsGrid.add(lbl, 0, row);
|
||||||
|
detailsGrid.add(val, 1, row);
|
||||||
|
|
||||||
|
return row + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
@Override
|
||||||
|
protected void onBack() {
|
||||||
|
Router.getInstance().navigate("/teachers");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package cz.jzitnik.controllers;
|
||||||
|
|
||||||
|
import cz.jzitnik.router.Route;
|
||||||
|
import cz.jzitnik.router.Router;
|
||||||
|
import cz.jzitnik.query.QueryOptions;
|
||||||
|
import io.github.tomhula.jecnaapi.data.schoolStaff.TeacherReference;
|
||||||
|
import io.github.tomhula.jecnaapi.data.schoolStaff.TeachersPage;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Route(path = "/teachers", fxml = "/teachers.fxml")
|
||||||
|
public class TeachersController extends DashboardBaseController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ProgressIndicator loadingIndicator;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ScrollPane scrollPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private FlowPane teachersFlow;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField searchField;
|
||||||
|
|
||||||
|
private List<TeacherReference> allTeachers = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNavigate(Map<String, Object> props) {
|
||||||
|
super.onNavigate(props);
|
||||||
|
|
||||||
|
searchField.textProperty().addListener((obs, oldVal, newVal) -> {
|
||||||
|
filterTeachers(newVal);
|
||||||
|
});
|
||||||
|
|
||||||
|
loadTeachers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTeachers() {
|
||||||
|
loadingIndicator.setVisible(true);
|
||||||
|
scrollPane.setVisible(false);
|
||||||
|
teachersFlow.getChildren().clear();
|
||||||
|
|
||||||
|
appState.getQueryClient().fetch("teachers:page", () -> {
|
||||||
|
try {
|
||||||
|
return appState.getClient().getTeachersPage().join();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, QueryOptions.defaultOptions()).thenAccept(result -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
loadingIndicator.setVisible(false);
|
||||||
|
|
||||||
|
if (result.isSuccess() && result.getData().isPresent()) {
|
||||||
|
TeachersPage page = result.getData().get();
|
||||||
|
allTeachers = new ArrayList<>(page.getTeachersReferences());
|
||||||
|
allTeachers.sort((t1, t2) -> t1.getFullName().compareToIgnoreCase(t2.getFullName()));
|
||||||
|
filterTeachers(searchField.getText());
|
||||||
|
scrollPane.setVisible(true);
|
||||||
|
} else {
|
||||||
|
Label errorLabel = new Label("Failed to load teachers.");
|
||||||
|
errorLabel.getStyleClass().addAll("text-danger", "title-3");
|
||||||
|
teachersFlow.getChildren().add(errorLabel);
|
||||||
|
scrollPane.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterTeachers(String query) {
|
||||||
|
teachersFlow.getChildren().clear();
|
||||||
|
|
||||||
|
String lowerQuery = query == null ? "" : query.toLowerCase();
|
||||||
|
|
||||||
|
List<TeacherReference> filtered = allTeachers.stream()
|
||||||
|
.filter(t -> t.getFullName().toLowerCase().contains(lowerQuery) || t.getTag().toLowerCase().contains(lowerQuery))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (filtered.isEmpty()) {
|
||||||
|
Label empty = new Label("Nenalezen žádný učitel.");
|
||||||
|
empty.getStyleClass().addAll("text-muted", "title-3");
|
||||||
|
teachersFlow.getChildren().add(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TeacherReference teacher : filtered) {
|
||||||
|
VBox card = new VBox(5);
|
||||||
|
card.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
card.getStyleClass().addAll("card", "teacher-card");
|
||||||
|
|
||||||
|
Label nameLbl = new Label(teacher.getFullName());
|
||||||
|
nameLbl.getStyleClass().addAll("title-4", "text-light");
|
||||||
|
|
||||||
|
Label tagLbl = new Label(teacher.getTag());
|
||||||
|
tagLbl.getStyleClass().addAll("teacher-tag");
|
||||||
|
|
||||||
|
card.getChildren().addAll(nameLbl, tagLbl);
|
||||||
|
|
||||||
|
card.setOnMouseClicked(e -> {
|
||||||
|
Router.getInstance().navigate("/teacher_profile", Map.of("tag", teacher.getTag()));
|
||||||
|
});
|
||||||
|
|
||||||
|
teachersFlow.getChildren().add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
protected void onBackToDashboard() {
|
||||||
|
Router.getInstance().navigate("/dashboard");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import cz.jzitnik.router.Route;
|
|||||||
import cz.jzitnik.router.Router;
|
import cz.jzitnik.router.Router;
|
||||||
import cz.jzitnik.query.QueryOptions;
|
import cz.jzitnik.query.QueryOptions;
|
||||||
import cz.jzitnik.query.QueryResult;
|
import cz.jzitnik.query.QueryResult;
|
||||||
|
import cz.jzitnik.util.TimetableRenderer;
|
||||||
import io.github.tomhula.jecnaapi.data.timetable.Lesson;
|
import io.github.tomhula.jecnaapi.data.timetable.Lesson;
|
||||||
import io.github.tomhula.jecnaapi.data.timetable.LessonPeriod;
|
import io.github.tomhula.jecnaapi.data.timetable.LessonPeriod;
|
||||||
import io.github.tomhula.jecnaapi.data.timetable.LessonSpot;
|
import io.github.tomhula.jecnaapi.data.timetable.LessonSpot;
|
||||||
@@ -80,183 +81,7 @@ public class TimetableController extends DashboardBaseController {
|
|||||||
timetableGrid.setAlignment(Pos.CENTER);
|
timetableGrid.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
Timetable timetable = currentPage.getTimetable();
|
Timetable timetable = currentPage.getTimetable();
|
||||||
List<LessonPeriod> periods = timetable.getLessonPeriods();
|
TimetableRenderer.renderTimetable(timetableGrid, timetable);
|
||||||
List<DayOfWeek> days = timetable.getDaysSorted();
|
|
||||||
|
|
||||||
ColumnConstraints dayCol = new ColumnConstraints();
|
|
||||||
dayCol.setMinWidth(120);
|
|
||||||
timetableGrid.getColumnConstraints().add(dayCol);
|
|
||||||
|
|
||||||
for (int i = 0; i < periods.size(); i++) {
|
|
||||||
LessonPeriod p = periods.get(i);
|
|
||||||
VBox header = new VBox(2);
|
|
||||||
header.setAlignment(Pos.CENTER);
|
|
||||||
header.getStyleClass().add("timetable-header");
|
|
||||||
|
|
||||||
Label indexLbl = new Label(String.valueOf(i));
|
|
||||||
indexLbl.getStyleClass().add("timetable-header-index");
|
|
||||||
|
|
||||||
Label timeLbl = new Label(p.toString());
|
|
||||||
timeLbl.getStyleClass().add("timetable-header-time");
|
|
||||||
|
|
||||||
header.getChildren().addAll(indexLbl, timeLbl);
|
|
||||||
|
|
||||||
ColumnConstraints col = new ColumnConstraints();
|
|
||||||
col.setMinWidth(110);
|
|
||||||
timetableGrid.getColumnConstraints().add(col);
|
|
||||||
|
|
||||||
timetableGrid.add(header, i + 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rowIndex = 1;
|
|
||||||
for (DayOfWeek day : days) {
|
|
||||||
VBox dayCell = new VBox();
|
|
||||||
dayCell.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
dayCell.getStyleClass().add("timetable-day-cell");
|
|
||||||
|
|
||||||
Label dayLabel = new Label(translateDay(day));
|
|
||||||
dayLabel.getStyleClass().addAll("timetable-day-label", "title-3");
|
|
||||||
dayCell.getChildren().add(dayLabel);
|
|
||||||
|
|
||||||
timetableGrid.add(dayCell, 0, rowIndex);
|
|
||||||
|
|
||||||
List<LessonSpot> spots = timetable.get(day);
|
|
||||||
if (spots != null) {
|
|
||||||
int colIndex = 1;
|
|
||||||
for (LessonSpot spot : spots) {
|
|
||||||
int span = spot.getPeriodSpan();
|
|
||||||
|
|
||||||
if (spot.isNotEmpty()) {
|
|
||||||
VBox cell = createLessonCell(spot);
|
|
||||||
timetableGrid.add(cell, colIndex, rowIndex, span, 1);
|
|
||||||
} else {
|
|
||||||
VBox emptyCell = new VBox();
|
|
||||||
emptyCell.getStyleClass().add("timetable-cell-empty");
|
|
||||||
timetableGrid.add(emptyCell, colIndex, rowIndex, span, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
colIndex += span;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rowIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createLessonCell(LessonSpot spot) {
|
|
||||||
VBox cell = new VBox(5);
|
|
||||||
cell.setAlignment(Pos.CENTER);
|
|
||||||
cell.getStyleClass().addAll("card", "timetable-cell");
|
|
||||||
|
|
||||||
if (spot.getSize() == 1) {
|
|
||||||
Lesson lesson = spot.getLesson(0);
|
|
||||||
addLessonInfoToCell(cell, lesson);
|
|
||||||
} else {
|
|
||||||
VBox groupContainer = new VBox(5);
|
|
||||||
groupContainer.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
for (int i = 0; i < spot.getSize(); i++) {
|
|
||||||
Lesson lesson = spot.getLesson(i);
|
|
||||||
VBox groupCell = new VBox(2);
|
|
||||||
groupCell.setAlignment(Pos.CENTER);
|
|
||||||
addLessonInfoToCell(groupCell, lesson);
|
|
||||||
|
|
||||||
if (lesson.getGroup() != null) {
|
|
||||||
Label groupLbl = new Label(lesson.getGroup());
|
|
||||||
groupLbl.getStyleClass().add("timetable-group-label");
|
|
||||||
groupCell.getChildren().add(0, groupLbl);
|
|
||||||
}
|
|
||||||
|
|
||||||
groupContainer.getChildren().add(groupCell);
|
|
||||||
|
|
||||||
if (i < spot.getSize() - 1) {
|
|
||||||
Separator sep = new Separator(javafx.geometry.Orientation.HORIZONTAL);
|
|
||||||
groupContainer.getChildren().add(sep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cell.getChildren().add(groupContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.setOnMouseClicked(e -> showLessonDetails(spot));
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addLessonInfoToCell(VBox cell, Lesson lesson) {
|
|
||||||
Label subjectLbl = new Label(lesson.getSubjectName().getShort() != null ? lesson.getSubjectName().getShort() : lesson.getSubjectName().getFull());
|
|
||||||
subjectLbl.getStyleClass().add("timetable-subject");
|
|
||||||
|
|
||||||
HBox bottomInfo = new HBox(5);
|
|
||||||
bottomInfo.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
if (lesson.getTeacherName() != null) {
|
|
||||||
Label teacherLbl = new Label(lesson.getTeacherName().getShort() != null ? lesson.getTeacherName().getShort() : lesson.getTeacherName().getFull());
|
|
||||||
teacherLbl.getStyleClass().add("timetable-teacher");
|
|
||||||
bottomInfo.getChildren().add(teacherLbl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lesson.getClassroom() != null) {
|
|
||||||
Label roomLbl = new Label(lesson.getClassroom());
|
|
||||||
roomLbl.getStyleClass().add("timetable-room");
|
|
||||||
bottomInfo.getChildren().add(roomLbl);
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.getChildren().addAll(subjectLbl, bottomInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showLessonDetails(LessonSpot spot) {
|
|
||||||
Dialog<Void> dialog = new Dialog<>();
|
|
||||||
dialog.setTitle("Detail hodiny");
|
|
||||||
dialog.setHeaderText(null);
|
|
||||||
dialog.getDialogPane().getButtonTypes().add(ButtonType.CLOSE);
|
|
||||||
|
|
||||||
VBox content = new VBox(15);
|
|
||||||
content.setPadding(new Insets(20));
|
|
||||||
|
|
||||||
for (Lesson lesson : spot) {
|
|
||||||
VBox lessonInfo = new VBox(5);
|
|
||||||
lessonInfo.getStyleClass().add("timetable-detail-box");
|
|
||||||
|
|
||||||
Label title = new Label(lesson.getSubjectName().getFull());
|
|
||||||
title.getStyleClass().add("title-3");
|
|
||||||
lessonInfo.getChildren().add(title);
|
|
||||||
|
|
||||||
GridPane grid = new GridPane();
|
|
||||||
grid.setHgap(10);
|
|
||||||
grid.setVgap(5);
|
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
if (lesson.getTeacherName() != null) {
|
|
||||||
grid.add(new Label("Učitel:"), 0, row);
|
|
||||||
grid.add(new Label(lesson.getTeacherName().getFull()), 1, row++);
|
|
||||||
}
|
|
||||||
if (lesson.getClassroom() != null) {
|
|
||||||
grid.add(new Label("Učebna:"), 0, row);
|
|
||||||
grid.add(new Label(lesson.getClassroom()), 1, row++);
|
|
||||||
}
|
|
||||||
if (lesson.getGroup() != null) {
|
|
||||||
grid.add(new Label("Skupina:"), 0, row);
|
|
||||||
grid.add(new Label(lesson.getGroup()), 1, row++);
|
|
||||||
}
|
|
||||||
|
|
||||||
lessonInfo.getChildren().add(grid);
|
|
||||||
content.getChildren().add(lessonInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.getDialogPane().setContent(content);
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String translateDay(DayOfWeek day) {
|
|
||||||
return switch (day) {
|
|
||||||
case MONDAY -> "Pondělí";
|
|
||||||
case TUESDAY -> "Úterý";
|
|
||||||
case WEDNESDAY -> "Středa";
|
|
||||||
case THURSDAY -> "Čtvrtek";
|
|
||||||
case FRIDAY -> "Pátek";
|
|
||||||
case SATURDAY -> "Sobota";
|
|
||||||
case SUNDAY -> "Neděle";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package cz.jzitnik.util;
|
||||||
|
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
|
public class ImageFetcher {
|
||||||
|
|
||||||
|
public static Image fetchImage(String imagePath) {
|
||||||
|
if (imagePath == null || imagePath.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (imagePath.startsWith("/")) {
|
||||||
|
imagePath = "https://www.spsejecna.cz" + imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url = new URL(imagePath);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||||
|
conn.setRequestProperty("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8");
|
||||||
|
conn.setRequestProperty("Accept-Language", "cs,en;q=0.9,en-US;q=0.8");
|
||||||
|
conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
|
||||||
|
conn.setRequestProperty("Referer", "https://www.spsejecna.cz/");
|
||||||
|
conn.setRequestProperty("Connection", "keep-alive");
|
||||||
|
|
||||||
|
conn.setConnectTimeout(5000);
|
||||||
|
conn.setReadTimeout(5000);
|
||||||
|
|
||||||
|
int responseCode = conn.getResponseCode();
|
||||||
|
if (responseCode >= 200 && responseCode < 300) {
|
||||||
|
InputStream in = conn.getInputStream();
|
||||||
|
String encoding = conn.getContentEncoding();
|
||||||
|
|
||||||
|
if ("gzip".equalsIgnoreCase(encoding)) {
|
||||||
|
in = new GZIPInputStream(in);
|
||||||
|
} else if ("deflate".equalsIgnoreCase(encoding)) {
|
||||||
|
in = new InflaterInputStream(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image img = new Image(in);
|
||||||
|
in.close();
|
||||||
|
return img;
|
||||||
|
} else {
|
||||||
|
System.err.println("Failed to fetch image: HTTP " + responseCode + " for " + imagePath);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
package cz.jzitnik.util;
|
||||||
|
|
||||||
|
import cz.jzitnik.router.Router;
|
||||||
|
import io.github.tomhula.jecnaapi.data.timetable.Lesson;
|
||||||
|
import io.github.tomhula.jecnaapi.data.timetable.LessonPeriod;
|
||||||
|
import io.github.tomhula.jecnaapi.data.timetable.LessonSpot;
|
||||||
|
import io.github.tomhula.jecnaapi.data.timetable.Timetable;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Dialog;
|
||||||
|
import javafx.scene.control.Hyperlink;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import kotlinx.datetime.DayOfWeek;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TimetableRenderer {
|
||||||
|
|
||||||
|
public static void renderTimetable(GridPane timetableGrid, Timetable timetable) {
|
||||||
|
timetableGrid.getChildren().clear();
|
||||||
|
timetableGrid.getColumnConstraints().clear();
|
||||||
|
timetableGrid.getRowConstraints().clear();
|
||||||
|
timetableGrid.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
List<LessonPeriod> periods = timetable.getLessonPeriods();
|
||||||
|
List<DayOfWeek> days = timetable.getDaysSorted();
|
||||||
|
|
||||||
|
ColumnConstraints dayCol = new ColumnConstraints();
|
||||||
|
dayCol.setMinWidth(120);
|
||||||
|
timetableGrid.getColumnConstraints().add(dayCol);
|
||||||
|
|
||||||
|
for (int i = 0; i < periods.size(); i++) {
|
||||||
|
LessonPeriod p = periods.get(i);
|
||||||
|
VBox header = new VBox(2);
|
||||||
|
header.setAlignment(Pos.CENTER);
|
||||||
|
header.getStyleClass().add("timetable-header");
|
||||||
|
|
||||||
|
Label indexLbl = new Label(String.valueOf(i));
|
||||||
|
indexLbl.getStyleClass().add("timetable-header-index");
|
||||||
|
|
||||||
|
Label timeLbl = new Label(p.toString());
|
||||||
|
timeLbl.getStyleClass().add("timetable-header-time");
|
||||||
|
|
||||||
|
header.getChildren().addAll(indexLbl, timeLbl);
|
||||||
|
|
||||||
|
ColumnConstraints col = new ColumnConstraints();
|
||||||
|
col.setMinWidth(110);
|
||||||
|
timetableGrid.getColumnConstraints().add(col);
|
||||||
|
|
||||||
|
timetableGrid.add(header, i + 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rowIndex = 1;
|
||||||
|
for (DayOfWeek day : days) {
|
||||||
|
VBox dayCell = new VBox();
|
||||||
|
dayCell.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
dayCell.getStyleClass().add("timetable-day-cell");
|
||||||
|
|
||||||
|
Label dayLabel = new Label(translateDay(day));
|
||||||
|
dayLabel.getStyleClass().addAll("timetable-day-label", "title-3");
|
||||||
|
dayCell.getChildren().add(dayLabel);
|
||||||
|
|
||||||
|
timetableGrid.add(dayCell, 0, rowIndex);
|
||||||
|
|
||||||
|
List<LessonSpot> spots = timetable.get(day);
|
||||||
|
if (spots != null) {
|
||||||
|
int colIndex = 1;
|
||||||
|
for (LessonSpot spot : spots) {
|
||||||
|
int span = spot.getPeriodSpan();
|
||||||
|
|
||||||
|
if (spot.isNotEmpty()) {
|
||||||
|
VBox cell = createLessonCell(spot);
|
||||||
|
timetableGrid.add(cell, colIndex, rowIndex, span, 1);
|
||||||
|
} else {
|
||||||
|
VBox emptyCell = new VBox();
|
||||||
|
emptyCell.getStyleClass().add("timetable-cell-empty");
|
||||||
|
timetableGrid.add(emptyCell, colIndex, rowIndex, span, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
colIndex += span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VBox createLessonCell(LessonSpot spot) {
|
||||||
|
VBox cell = new VBox(5);
|
||||||
|
cell.setAlignment(Pos.CENTER);
|
||||||
|
cell.getStyleClass().addAll("card", "timetable-cell");
|
||||||
|
|
||||||
|
if (spot.getSize() == 1) {
|
||||||
|
Lesson lesson = spot.getLesson(0);
|
||||||
|
addLessonInfoToCell(cell, lesson);
|
||||||
|
} else {
|
||||||
|
VBox groupContainer = new VBox(5);
|
||||||
|
groupContainer.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
for (int i = 0; i < spot.getSize(); i++) {
|
||||||
|
Lesson lesson = spot.getLesson(i);
|
||||||
|
VBox groupCell = new VBox(2);
|
||||||
|
groupCell.setAlignment(Pos.CENTER);
|
||||||
|
addLessonInfoToCell(groupCell, lesson);
|
||||||
|
|
||||||
|
if (lesson.getGroup() != null) {
|
||||||
|
Label groupLbl = new Label(lesson.getGroup());
|
||||||
|
groupLbl.getStyleClass().add("timetable-group-label");
|
||||||
|
groupCell.getChildren().add(0, groupLbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
groupContainer.getChildren().add(groupCell);
|
||||||
|
|
||||||
|
if (i < spot.getSize() - 1) {
|
||||||
|
Separator sep = new Separator(javafx.geometry.Orientation.HORIZONTAL);
|
||||||
|
groupContainer.getChildren().add(sep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell.getChildren().add(groupContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.setOnMouseClicked(e -> showLessonDetails(spot));
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addLessonInfoToCell(VBox cell, Lesson lesson) {
|
||||||
|
Label subjectLbl = new Label(lesson.getSubjectName().getShort() != null ? lesson.getSubjectName().getShort() : lesson.getSubjectName().getFull());
|
||||||
|
subjectLbl.getStyleClass().add("timetable-subject");
|
||||||
|
|
||||||
|
HBox bottomInfo = new HBox(5);
|
||||||
|
bottomInfo.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
if (lesson.getTeacherName() != null) {
|
||||||
|
Label teacherLbl = new Label(lesson.getTeacherName().getShort() != null ? lesson.getTeacherName().getShort() : lesson.getTeacherName().getFull());
|
||||||
|
teacherLbl.getStyleClass().add("timetable-teacher");
|
||||||
|
bottomInfo.getChildren().add(teacherLbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lesson.getClassroom() != null) {
|
||||||
|
Label roomLbl = new Label(lesson.getClassroom());
|
||||||
|
roomLbl.getStyleClass().add("timetable-room");
|
||||||
|
bottomInfo.getChildren().add(roomLbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.getChildren().addAll(subjectLbl, bottomInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void showLessonDetails(LessonSpot spot) {
|
||||||
|
Dialog<Void> dialog = new Dialog<>();
|
||||||
|
dialog.setTitle("Detail hodiny");
|
||||||
|
dialog.setHeaderText(null);
|
||||||
|
dialog.getDialogPane().getButtonTypes().add(ButtonType.CLOSE);
|
||||||
|
|
||||||
|
VBox content = new VBox(15);
|
||||||
|
content.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
for (Lesson lesson : spot) {
|
||||||
|
VBox lessonInfo = new VBox(5);
|
||||||
|
lessonInfo.getStyleClass().add("timetable-detail-box");
|
||||||
|
|
||||||
|
Label title = new Label(lesson.getSubjectName().getFull());
|
||||||
|
title.getStyleClass().add("title-3");
|
||||||
|
lessonInfo.getChildren().add(title);
|
||||||
|
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
grid.setHgap(10);
|
||||||
|
grid.setVgap(5);
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
if (lesson.getTeacherName() != null) {
|
||||||
|
Hyperlink teacherLbl = new Hyperlink(lesson.getTeacherName().getFull());
|
||||||
|
teacherLbl.getStyleClass().addAll("timetable-teacher", "teacher-link");
|
||||||
|
teacherLbl.setOnAction(e -> {
|
||||||
|
dialog.close();
|
||||||
|
String tag = lesson.getTeacherName().getShort() != null ? lesson.getTeacherName().getShort() : lesson.getTeacherName().getFull();
|
||||||
|
Router.getInstance().navigate("/teacher_profile", Map.of("tag", tag));
|
||||||
|
});
|
||||||
|
grid.add(new Label("Učitel:"), 0, row);
|
||||||
|
grid.add(teacherLbl, 1, row++);
|
||||||
|
}
|
||||||
|
if (lesson.getClassroom() != null) {
|
||||||
|
grid.add(new Label("Učebna:"), 0, row);
|
||||||
|
grid.add(new Label(lesson.getClassroom()), 1, row++);
|
||||||
|
}
|
||||||
|
if (lesson.getGroup() != null) {
|
||||||
|
grid.add(new Label("Skupina:"), 0, row);
|
||||||
|
grid.add(new Label(lesson.getGroup()), 1, row++);
|
||||||
|
}
|
||||||
|
|
||||||
|
lessonInfo.getChildren().add(grid);
|
||||||
|
content.getChildren().add(lessonInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.getDialogPane().setContent(content);
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String translateDay(DayOfWeek day) {
|
||||||
|
return switch (day) {
|
||||||
|
case MONDAY -> "Pondělí";
|
||||||
|
case TUESDAY -> "Úterý";
|
||||||
|
case WEDNESDAY -> "Středa";
|
||||||
|
case THURSDAY -> "Čtvrtek";
|
||||||
|
case FRIDAY -> "Pátek";
|
||||||
|
case SATURDAY -> "Sobota";
|
||||||
|
case SUNDAY -> "Neděle";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,18 +31,18 @@
|
|||||||
<ProgressIndicator fx:id="loadingIndicator" maxWidth="50" maxHeight="50" />
|
<ProgressIndicator fx:id="loadingIndicator" maxWidth="50" maxHeight="50" />
|
||||||
|
|
||||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;" visible="false">
|
<ScrollPane fx:id="scrollPane" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;" visible="false">
|
||||||
<padding>
|
<StackPane alignment="TOP_CENTER">
|
||||||
<Insets top="20" right="30" bottom="30" left="30"/>
|
<padding>
|
||||||
</padding>
|
<Insets top="20" right="30" bottom="30" left="30"/>
|
||||||
<VBox spacing="25" alignment="TOP_CENTER" maxWidth="900" StackPane.alignment="TOP_CENTER">
|
</padding>
|
||||||
|
<VBox spacing="25" alignment="TOP_CENTER" maxWidth="900">
|
||||||
<!-- Summary Box -->
|
|
||||||
<HBox fx:id="summaryBox" spacing="20" alignment="CENTER" />
|
<HBox fx:id="summaryBox" spacing="20" alignment="CENTER" />
|
||||||
|
|
||||||
<!-- Detail Grid -->
|
<FlowPane fx:id="detailFlow" hgap="15" vgap="15" alignment="CENTER" />
|
||||||
<FlowPane fx:id="detailFlow" hgap="15" vgap="15" alignment="CENTER" />
|
|
||||||
|
</VBox>
|
||||||
</VBox>
|
</StackPane>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</center>
|
</center>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<!-- Row 0 -->
|
<!-- Row 0 -->
|
||||||
<Button text="Známky" GridPane.rowIndex="0" GridPane.columnIndex="0" onAction="#onNavigateToGrades" styleClass="card"/>
|
<Button text="Známky" GridPane.rowIndex="0" GridPane.columnIndex="0" onAction="#onNavigateToGrades" styleClass="card"/>
|
||||||
<Button text="Rozvrh" GridPane.rowIndex="0" GridPane.columnIndex="1" onAction="#onNavigateToTimetable" styleClass="card"/>
|
<Button text="Rozvrh" GridPane.rowIndex="0" GridPane.columnIndex="1" onAction="#onNavigateToTimetable" styleClass="card"/>
|
||||||
<Button text="Učitelé" GridPane.rowIndex="0" GridPane.columnIndex="2" onAction="#onDoNothing" styleClass="card"/>
|
<Button text="Učitelé" GridPane.rowIndex="0" GridPane.columnIndex="2" onAction="#onNavigateToTeachers" styleClass="card"/>
|
||||||
|
|
||||||
<!-- Row 1 -->
|
<!-- Row 1 -->
|
||||||
<Button text="Učebny" GridPane.rowIndex="1" GridPane.columnIndex="0" onAction="#onDoNothing" styleClass="card"/>
|
<Button text="Učebny" GridPane.rowIndex="1" GridPane.columnIndex="0" onAction="#onDoNothing" styleClass="card"/>
|
||||||
|
|||||||
@@ -29,10 +29,12 @@
|
|||||||
<ProgressIndicator fx:id="loadingIndicator" maxWidth="50" maxHeight="50" />
|
<ProgressIndicator fx:id="loadingIndicator" maxWidth="50" maxHeight="50" />
|
||||||
|
|
||||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;" visible="false">
|
<ScrollPane fx:id="scrollPane" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;" visible="false">
|
||||||
<padding>
|
<StackPane alignment="TOP_CENTER">
|
||||||
<Insets top="20" right="30" bottom="30" left="30"/>
|
<padding>
|
||||||
</padding>
|
<Insets top="20" right="30" bottom="30" left="30"/>
|
||||||
<VBox fx:id="contentBox" spacing="15" alignment="TOP_CENTER" maxWidth="900" />
|
</padding>
|
||||||
|
<VBox fx:id="contentBox" spacing="15" alignment="TOP_CENTER" maxWidth="900" />
|
||||||
|
</StackPane>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</center>
|
</center>
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
.search-field {
|
||||||
|
-fx-min-width: 250px;
|
||||||
|
-fx-background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
-fx-border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
-fx-border-radius: 4px;
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-field:focused {
|
||||||
|
-fx-border-color: #58a6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-card {
|
||||||
|
-fx-min-width: 220px;
|
||||||
|
-fx-background-color: rgba(30, 40, 56, 0.7);
|
||||||
|
-fx-border-color: rgba(255, 255, 255, 0.05);
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-padding: 15px 20px;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-card:hover {
|
||||||
|
-fx-background-color: rgba(45, 55, 71, 0.9);
|
||||||
|
-fx-border-color: rgba(255, 255, 255, 0.15);
|
||||||
|
-fx-effect: dropshadow(two-pass-box, rgba(0,0,0,0.3), 8, 0.0, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-tag {
|
||||||
|
-fx-text-fill: #58a6ff;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-padding: 2px 6px;
|
||||||
|
-fx-background-color: rgba(88, 166, 255, 0.1);
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-tag.large {
|
||||||
|
-fx-font-size: 16px;
|
||||||
|
-fx-padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-profile-header {
|
||||||
|
-fx-background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
-fx-padding: 20px;
|
||||||
|
-fx-background-radius: 12px;
|
||||||
|
-fx-border-color: rgba(255, 255, 255, 0.05);
|
||||||
|
-fx-border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-details-grid {
|
||||||
|
-fx-background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
-fx-padding: 25px;
|
||||||
|
-fx-background-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-bold {
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
@@ -95,3 +95,12 @@
|
|||||||
-fx-border-color: rgba(255, 255, 255, 0.1);
|
-fx-border-color: rgba(255, 255, 255, 0.1);
|
||||||
-fx-border-radius: 8px;
|
-fx-border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.teacher-link {
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
-fx-padding: 0;
|
||||||
|
-fx-text-fill: #58a6ff;
|
||||||
|
}
|
||||||
|
.teacher-link:hover {
|
||||||
|
-fx-underline: true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ProgressIndicator?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.shape.Rectangle?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
|
||||||
|
<BorderPane xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="cz.jzitnik.controllers.TeacherProfileController"
|
||||||
|
style="-fx-background-color: linear-gradient(#0f1724, #071023);"
|
||||||
|
stylesheets="@styles/teachers.css">
|
||||||
|
|
||||||
|
<top>
|
||||||
|
<HBox spacing="15" alignment="CENTER_LEFT" style="-fx-background-color: rgba(255, 255, 255, 0.05); -fx-padding: 15 25;">
|
||||||
|
<Button text="← Zpět" onAction="#onBack" styleClass="flat, small" />
|
||||||
|
<Label fx:id="headerNameLabel" text="Profil" styleClass="title-2, text-light" />
|
||||||
|
</HBox>
|
||||||
|
</top>
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<StackPane>
|
||||||
|
<ProgressIndicator fx:id="loadingIndicator" maxWidth="50" maxHeight="50" />
|
||||||
|
|
||||||
|
<ScrollPane fx:id="scrollPane" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;" visible="false">
|
||||||
|
<StackPane alignment="TOP_CENTER">
|
||||||
|
<padding>
|
||||||
|
<Insets top="30" right="30" bottom="30" left="30"/>
|
||||||
|
</padding>
|
||||||
|
|
||||||
|
<VBox spacing="25" alignment="TOP_CENTER" maxWidth="1000">
|
||||||
|
|
||||||
|
<HBox spacing="20" alignment="CENTER_LEFT" styleClass="teacher-profile-header" maxWidth="800">
|
||||||
|
<StackPane>
|
||||||
|
<ImageView fx:id="avatarView" fitWidth="150" fitHeight="193" preserveRatio="false">
|
||||||
|
<clip>
|
||||||
|
<Rectangle width="150" height="193" arcWidth="20" arcHeight="20" />
|
||||||
|
</clip>
|
||||||
|
</ImageView>
|
||||||
|
</StackPane>
|
||||||
|
|
||||||
|
<VBox spacing="5" alignment="CENTER_LEFT">
|
||||||
|
<Label fx:id="fullNameLabel" styleClass="title-1, text-light" />
|
||||||
|
<Label fx:id="tagLabel" styleClass="teacher-tag, large" />
|
||||||
|
</VBox>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<GridPane fx:id="detailsGrid" hgap="20" vgap="15" styleClass="teacher-details-grid" maxWidth="800">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints minWidth="150" halignment="RIGHT" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" />
|
||||||
|
</columnConstraints>
|
||||||
|
</GridPane>
|
||||||
|
|
||||||
|
<VBox spacing="10" alignment="TOP_CENTER" styleClass="teacher-timetable-container">
|
||||||
|
<Label text="Rozvrh učitele" styleClass="title-2, text-light" />
|
||||||
|
<ScrollPane vbarPolicy="NEVER" fitToHeight="true" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;">
|
||||||
|
<GridPane fx:id="timetableGrid" hgap="10" vgap="10" alignment="CENTER" />
|
||||||
|
</ScrollPane>
|
||||||
|
</VBox>
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
|
</ScrollPane>
|
||||||
|
</StackPane>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
</BorderPane>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ProgressIndicator?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.FlowPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<BorderPane xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="cz.jzitnik.controllers.TeachersController"
|
||||||
|
style="-fx-background-color: linear-gradient(#0f1724, #071023);"
|
||||||
|
stylesheets="@styles/teachers.css">
|
||||||
|
|
||||||
|
<top>
|
||||||
|
<HBox spacing="15" alignment="CENTER_LEFT" style="-fx-background-color: rgba(255, 255, 255, 0.05); -fx-padding: 15 25;">
|
||||||
|
<Button text="← Zpět" onAction="#onBackToDashboard" styleClass="flat, small" />
|
||||||
|
<Label text="Učitelé" styleClass="title-2, text-light" />
|
||||||
|
<Region HBox.hgrow="ALWAYS" />
|
||||||
|
<TextField fx:id="searchField" promptText="Hledat učitele..." styleClass="search-field" />
|
||||||
|
</HBox>
|
||||||
|
</top>
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<StackPane>
|
||||||
|
<ProgressIndicator fx:id="loadingIndicator" maxWidth="50" maxHeight="50" />
|
||||||
|
|
||||||
|
<ScrollPane fx:id="scrollPane" fitToWidth="true" style="-fx-background: transparent; -fx-background-color: transparent;" visible="false">
|
||||||
|
<padding>
|
||||||
|
<Insets top="20" right="30" bottom="30" left="30"/>
|
||||||
|
</padding>
|
||||||
|
<VBox alignment="TOP_CENTER">
|
||||||
|
<FlowPane fx:id="teachersFlow" hgap="15" vgap="15" alignment="CENTER" maxWidth="900" />
|
||||||
|
</VBox>
|
||||||
|
</ScrollPane>
|
||||||
|
</StackPane>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
</BorderPane>
|
||||||
Reference in New Issue
Block a user