package com.example.schedulerapp; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.Event; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.stage.Stage; import java.io.IOException; import java.text.DecimalFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.*; public class Controller { @FXML BorderPane myBorderPane; @FXML VBox leftSideButtons; @FXML public BorderPane rootBorderPane; @FXML TextArea employees; @FXML private TextField addFirstName; @FXML private TextField addLastName; @FXML private TextField removeID; @FXML Button addShiftSubmitButton; @FXML DatePicker addShiftDatePicker; @FXML TextField addShiftStartTime; @FXML TextField addShiftEndTime; @FXML ComboBox addShiftEmployeeBox; @FXML HBox addShiftEmployeeHBox; @FXML ComboBox removeEmployeeBox; @FXML ComboBox addShiftStartTimeBox; @FXML ComboBox addShiftEndTimeBox; @FXML Label currentDateText; @FXML TextField loginField; @FXML Label loggedIn; @FXML Label viewing; @FXML Label loginWarning; @FXML TextField editEmployeeFirstName; @FXML TextField editEmployeeLastName; @FXML TextField editEmployeeEmail; @FXML TextField editEmployeeWage; @FXML TextField editEmployeePhoneNumber; @FXML TextField editEmployeeAddress; @FXML ComboBox editEmployeeBox; @FXML CheckBox editEmployeeManager; @FXML DatePicker timeOffDatePicker; @FXML ChoiceBox<Employee> selectedEmployee; @FXML Label currentDateTextAvail; @FXML ComboBox<String> availabilityDayBox; @FXML ComboBox<String> editAvailabilityStartBox; @FXML ComboBox<String> editAvailabilityEndBox; @FXML Label availabilityResult; @FXML Label addShiftError; @FXML ComboBox editEmployeePositions; @FXML ComboBox<String> addShiftPositionBox; @FXML TextArea timeOffReasonBox; @FXML DatePicker timeOffDateStart; @FXML DatePicker timeOffDateEnd; @FXML Label timeOffLabel; boolean isDaily; Stage popupStage; Scene popupScene; Stage popupAvailStage; Scene popupAvail; Stage popupTimeOffStage; Scene popupTimeOff; Model model; DailyView dailyView; WeeklyView weeklyView; EmployeeView employeeView; AvailabilityView availabilityView; //PickupView pickupView; BorderPane root; Parent header; Parent sideSchedulePanel; Parent bottomPanel; Parent paystubView; Parent editEmployeeView; Parent addEmployeeView; Parent staffPanel; Parent availabilityPanel; Parent removeEmployeeView; DatePicker jumpDatePicker; // login methods, initializes views too public void attemptLogin(Event e) throws Exception { if (!model.hasEmployee(Integer.parseInt(loginField.getText()))) { loginWarning.setText("Invalid id"); } else { login(e); } } public void login(Event e) throws Exception { //set this employee and manager privileges as appropriate int thisEmployee = Integer.parseInt(loginField.getText()); model.setThisEmployee(thisEmployee); model.setSelectedEmployee(thisEmployee); model.setIsManager(model.getEmployee(thisEmployee).isManager()); //load everything loadEverything(); //set up for initial daily schedule view root.setTop(header); viewSchedule(); Stage stage = (Stage)((Node)e.getSource()).getScene().getWindow(); Scene scene = new Scene(root, 800, 600); stage.setTitle("Scheduler App"); stage.setScene(scene); stage.show(); //set variable text updateCurrentDate(e); loggedIn.setText("Logged In: " + model.getEmployee(model.getThisEmployee()).toString()); //set manager specific text if (model.getIsManager()){ selectedEmployee.setItems(FXCollections.observableArrayList(model.getAllEmployees())); selectedEmployee.getSelectionModel().selectedItemProperty().addListener((observableValue, o, t1) -> model.setSelectedEmployee(selectedEmployee.getSelectionModel().getSelectedItem().getEmployeeID())); selectedEmployee.getSelectionModel().select(model.getEmployee(model.getSelectedEmployee())); } } // add components here to be loaded once public void loadEverything() throws IOException { root = new BorderPane(); dailyView = new DailyView(); dailyView.setModel(model); model.addSubscriber(dailyView); weeklyView = new WeeklyView(); weeklyView.setModel(model); model.addSubscriber(weeklyView); employeeView = new EmployeeView(); employeeView.setModel(model); model.addSubscriber(employeeView); availabilityView = new AvailabilityView(); availabilityView.setModel(model); //pickupView = new PickupView(); //pickupView.setModel(model); model.addSubscriber(availabilityView); //fxml views loadHeader(); loadScheduleSide(); loadScheduleBottom(); loadPaystubView(); loadEditEmployee(); loadStaffPanel(); loadAvailabilityBottomPanel(); loadAddEmployee(); loadRemoveEmployee(); } /* load functions for individual views / panels all need to be called in loadEverything emp vs manager distinction made here, later can just load */ public void loadHeader() throws IOException { if (model.getIsManager()){ //load manager header FXMLLoader headerLoader = new FXMLLoader(this.getClass().getResource("headerManager.fxml")); headerLoader.setController(this); header = headerLoader.load(); } else { //load employee header FXMLLoader headerLoader = new FXMLLoader(this.getClass().getResource("employeeHeader.fxml")); //FXMLLoader headerLoader = new FXMLLoader(this.getClass().getResource("headerManager.fxml")); //for now headerLoader.setController(this); header = headerLoader.load(); } } public void loadScheduleSide() throws IOException { //same for both FXMLLoader sideLoader = new FXMLLoader(this.getClass().getResource("schedSidePanel.fxml")); sideLoader.setController(this); sideSchedulePanel = sideLoader.load(); } public void loadScheduleBottom() throws IOException { if (model.getIsManager()){ FXMLLoader bottomLoader = new FXMLLoader(this.getClass().getResource("schedBottomPanelManager.fxml")); bottomLoader.setController(this); bottomPanel = bottomLoader.load(); } else { FXMLLoader bottomLoader = new FXMLLoader(this.getClass().getResource("employeeSchedBottomPanel.fxml")); bottomLoader.setController(this); bottomPanel = bottomLoader.load(); } } public void loadAvailabilityBottomPanel() throws IOException { FXMLLoader sideLoader = new FXMLLoader(this.getClass().getResource("availabilityBottomPanel.fxml")); sideLoader.setController(this); availabilityPanel = sideLoader.load(); } public void loadPaystubView() throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(ScheduleApp.class.getResource("paystubView.fxml")); fxmlLoader.setController(this); paystubView = fxmlLoader.load(); } public void loadEditEmployee() throws IOException{ FXMLLoader gridLoader = new FXMLLoader(this.getClass().getResource("editEmployee.fxml")); gridLoader.setController(this); editEmployeeView = gridLoader.load(); } public void loadStaffPanel() throws IOException { FXMLLoader sideLoader = new FXMLLoader(this.getClass().getResource("staffManagerSidePanel.fxml")); sideLoader.setController(this); staffPanel = sideLoader.load(); } public void loadAddEmployee() throws IOException { FXMLLoader gridLoader = new FXMLLoader(this.getClass().getResource("addEmployee.fxml")); gridLoader.setController(this); addEmployeeView = gridLoader.load(); } public void loadRemoveEmployee() throws IOException { FXMLLoader gridLoader = new FXMLLoader(this.getClass().getResource("removeEmployee.fxml")); gridLoader.setController(this); removeEmployeeView = gridLoader.load(); } /* switch to the appropriate view same code for emp vs manager, distinction made in load functions */ // schedule tab (default daily view) public void viewSchedule(){ root.setLeft(sideSchedulePanel); root.setBottom(bottomPanel); root.setCenter(dailyView); isDaily = true; } // schedule -> weekly public void viewScheduleWeekly(){ root.setCenter(weeklyView); root.setBottom(bottomPanel); isDaily = false; } // schedule -> daily public void viewScheduleDaily(){ root.setCenter(dailyView); root.setBottom(bottomPanel); isDaily = true; } // schedule -> availability public void viewAvailability() { root.setCenter(availabilityView); root.setBottom(availabilityPanel); isDaily = false; } // schedule -> pickups todo public void viewPickups(){ //root.setCenter(pickupView); root.setCenter(null); root.setBottom(bottomPanel); } //schedule popups public void editAvailability() throws IOException { FXMLLoader popupLoader = new FXMLLoader(this.getClass().getResource("availabilityPopup.fxml")); popupLoader.setController(this); popupAvail = new Scene(popupLoader.load(),500,500); popupAvailStage = new Stage(); popupAvailStage.setScene(popupAvail); popupAvailStage.show(); } public void requestTimeOff() throws IOException { FXMLLoader popupLoader = new FXMLLoader(this.getClass().getResource("timeOffPopup.fxml")); popupLoader.setController(this); popupTimeOff = new Scene(popupLoader.load(),500,500); popupTimeOffStage = new Stage(); popupTimeOffStage.setScene(popupTimeOff); popupTimeOffStage.show(); } //add shift popup (manager only) public void addShiftClicked() throws IOException { FXMLLoader popupLoader = new FXMLLoader(this.getClass().getResource("addShiftPopup.fxml")); popupLoader.setController(this); popupScene = new Scene(popupLoader.load(),500,500); popupStage = new Stage(); popupStage.setScene(popupScene); popupStage.show(); } // Paystub tab (employee only) public void viewPaystub() throws IOException { VBox paystubVBox = new VBox(); root.setLeft(null); root.setBottom(null); String[][] payrollInfo = getPayrollInfo(model.getSelectedEmployee()); double grossPay = 0; double totalHours = 0; for(int i = 0; i < payrollInfo.length; i++){ HBox tempHBox = new HBox(); tempHBox.setSpacing(50); tempHBox.setBorder(new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); Label position = new Label("Position: " + payrollInfo[i][0]); Label hours = new Label("Hours Worked: " + payrollInfo[i][1]); Label wage = new Label("Wage: " + payrollInfo[i][2]); Label totalPay = new Label("Total Pay: " + payrollInfo[i][3]); tempHBox.getChildren().addAll(position,hours,wage,totalPay); paystubVBox.getChildren().add(tempHBox); grossPay += Double.parseDouble(payrollInfo[i][3]); totalHours += Double.parseDouble(payrollInfo[i][1]); } HBox totalHBox = new HBox(); totalHBox.setSpacing(50); totalHBox.getChildren().add(new Label("Total Hours Worked: " + totalHours)); totalHBox.getChildren().add(new Label("Gross Pay: " + grossPay)); paystubVBox.getChildren().add(totalHBox); root.setCenter(paystubVBox); } private String[][] getPayrollInfo(int selectedEmployee) { System.out.println(model.getDate()); LocalDateTime currentDate = LocalDateTime.of(model.getDate(), LocalTime.MIN); ArrayList<Shift> shiftsThisWeek = model.getEmployeeShiftsByWeek(selectedEmployee,currentDate,currentDate.plusDays(14)); HashMap<String , Double> hoursWorked = new HashMap<String, Double>(); DecimalFormat df = new DecimalFormat("0.00"); for(int i = 0; i < shiftsThisWeek.size(); i++){ Shift tempShift = shiftsThisWeek.get(i); LocalDateTime startTime = tempShift.getStart(); LocalDateTime endTime = tempShift.getEnd(); String position = tempShift.getPosition(); int startHour = startTime.getHour(); int startMin = startTime.getMinute(); int endHour = endTime.getHour(); int endMin = endTime.getMinute();; double newHours = endHour - startHour; double minRemainder; if(!hoursWorked.containsKey(position)){ hoursWorked.put(position,newHours); } else{ double oldHours = hoursWorked.get(position); if(endMin - startMin == 0){ minRemainder = 0; } else{ minRemainder = 0.5; } hoursWorked.replace(position,oldHours + newHours + minRemainder); } } String [][] result = new String[hoursWorked.size()][4]; int index = 0; for (Map.Entry<String, Double> entry : hoursWorked.entrySet()) { String position = entry.getKey(); double hours = entry.getValue(); Float wage = model.getWage(position); result[index][0] = position; result[index][1] = String.valueOf(hours); result[index][2] = String.valueOf(wage); result[index][3] = String.valueOf(df.format(wage * hours)); index++; } return result; } private void showPaystub() { Employee currentEmployee = model.getEmployee(model.getThisEmployee()); } // Payroll tab (manager only) public void viewPayroll() throws IOException { viewPaystub(); //showPayroll(model.getSelectedEmployee()); } // Staff tab (default view staff, manager only) public void viewStaff(){ root.setLeft(staffPanel); root.setCenter(employeeView); root.setBottom(null); } // Staff -> edit employee public void viewEditEmployee(){ root.setCenter(editEmployeeView); } // Staff -> add employee public void viewAddEmployee(){ root.setCenter(addEmployeeView); } // Staff -> remove employee public void viewRemoveEmployee(){ root.setCenter(removeEmployeeView); } //Staff -> view employees public void viewViewEmployees(){ root.setCenter(employeeView); model.notifySubscribers(); } // Requests tab (manager only) public void viewRequests() { root.setCenter(null); System.out.println("todo: display requests"); root.setLeft(null); root.setBottom(null); } //methods for submitting data public void submitTimeOff(MouseEvent event){ timeOffLabel.setText(model.addTimeOff(model.getSelectedEmployee(), timeOffDateStart.getValue().toString(), timeOffDateEnd.getValue().toString(), timeOffReasonBox.getText())); } public void addShiftSubmitClicked(MouseEvent event){ String employee = (String) addShiftEmployeeBox.getValue(); int index = addShiftEmployeeBox.getSelectionModel().getSelectedIndex(); String startTime = (String) addShiftStartTimeBox.getValue(); String endTime = (String) addShiftEndTimeBox.getValue(); LocalDate date = addShiftDatePicker.getValue(); String position = addShiftPositionBox.getValue(); addShiftError.setText(model.addShift(model.getIDbyIndex(index),date.toString(),Integer.parseInt(startTime), Integer.parseInt(endTime), position)); } public void submitNewAvailability(MouseEvent e){ availabilityResult.setText(model.editAvailability(model.getSelectedEmployee(), availabilityDayBox.getSelectionModel().getSelectedIndex(), Integer.parseInt(editAvailabilityStartBox.getValue()), Integer.parseInt(editAvailabilityEndBox.getValue()))); } public void addEmployeeClicked(MouseEvent mouseEvent) { String firstName = addFirstName.getText(); String lastName = addLastName.getText(); model.addEmployee(firstName, lastName); System.out.println("Employee " + firstName + " " + lastName + " added to Staff. Welcome " + firstName + "!"); model.notifySubscribers(); } public void editEmployeeClicked(MouseEvent mouseEvent) { int id = model.getIDbyIndex(editEmployeeBox.getSelectionModel().getSelectedIndex()); String newFirstName = editEmployeeFirstName.getText(); String newLastName = editEmployeeLastName.getText(); String newEmail = editEmployeeEmail.getText(); Float newWage = Float.parseFloat(editEmployeeWage.getText()); String newPhoneNumber = editEmployeePhoneNumber.getText(); boolean newIsManager = editEmployeeManager.isSelected(); model.editEmployee(id,newFirstName,newLastName,newIsManager,newEmail,newPhoneNumber,newWage); System.out.println("edit employee submit clicked"); } public void removeEmployeeClicked(MouseEvent mouseEvent) { int index = removeEmployeeBox.getSelectionModel().getSelectedIndex(); String id = String.valueOf(model.getIDbyIndex(index)); model.removeEmployee(id); removeEmployeeBox.getSelectionModel().clearSelection(); } public void prevButtonClicked (MouseEvent event) throws Exception { if (isDaily) model.datePrev(); else model.weekPrev(); updateCurrentDate(event); } public void jumpButtonClicked (MouseEvent event) throws Exception { System.out.println("jump button clicked"); jumpDatePicker = new DatePicker(); Scene tempScene = new Scene(jumpDatePicker); Stage tempStage = new Stage(); tempStage.setScene(tempScene); tempStage.show(); jumpDatePicker.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent actionEvent) { model.dateJump(jumpDatePicker.getValue()); try { updateCurrentDate(actionEvent); } catch (Exception e) { e.printStackTrace(); } } }); //model.dateJump(); updateCurrentDate(event); } public void jumpDatePicked(){ model.dateJump(jumpDatePicker.getValue()); } public void nextButtonClicked (MouseEvent event) throws Exception { if (isDaily) model.dateNext(); else model.weekNext(); updateCurrentDate(event); } public void cancelClicked(MouseEvent e){ ((Node)e.getSource()).getScene().getWindow().hide(); } public void logoutClicked(MouseEvent mouseEvent) throws IOException { FXMLLoader loginLoader = new FXMLLoader(this.getClass().getResource("login.fxml")); loginLoader.setController(this); Parent root = loginLoader.load(); Stage stage = (Stage)((Node)mouseEvent.getSource()).getScene().getWindow(); Scene scene = new Scene(root, 800, 600); stage.setTitle("Scheduler App"); stage.setScene(scene); stage.show(); } // helper methods for comboboxes public void populateEmployeeBox(MouseEvent event) { ArrayList<String> aList = model.returnFormattedEmployeeNames(); ObservableList<String> list = FXCollections.observableArrayList(); list.addAll(aList); ComboBox temp = (ComboBox) event.getSource(); if(event.getSource() == editEmployeeBox){ System.out.println("test"); } temp.setItems(list); } public void populatePositionBox(MouseEvent event) { ComboBox test = (ComboBox) event.getSource(); test.setItems(FXCollections.observableArrayList(model.returnPositions())); } public void removeEmployeeBoxClicked(MouseEvent event){ System.out.println("remove employee box clicked"); populateEmployeeBox(event); } public void populateDaysOfWeek(MouseEvent event){ List<String> times = Arrays.asList("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"); ComboBox box = (ComboBox) event.getSource(); ObservableList<String> list = FXCollections.observableArrayList(times); box.setItems(list); } public void addShiftTimeBoxClicked(MouseEvent event){ ArrayList<String> times = new ArrayList<>(); for (int i = 8; i < 24; i++){ String j = i + "00"; String k = i + "30"; if (i < 10) { j = "0" + j; k = "0" + k; } times.add(j); times.add(k); } ComboBox box = (ComboBox) event.getSource(); ObservableList<String> list = FXCollections.observableArrayList(times); box.setItems(list); } public void availabilityTimeBoxClicked(MouseEvent event){ ArrayList<String> times = new ArrayList<>(); for (int i = 0; i < 24; i++){ String j = i + "00"; String k = i + "30"; if (i < 10) { j = "0" + j; k = "0" + k; } times.add(j); times.add(k); } ComboBox box = (ComboBox) event.getSource(); ObservableList<String> list = FXCollections.observableArrayList(times); box.setItems(list); } // other helper methods public void updateCurrentDate(Event event) throws Exception { currentDateText.setText("Current Date: " + model.date.toString()); currentDateTextAvail.setText("Current Date: " + model.date.toString()); } public void setModel(Model model) { this.model = model; } public void fillEditEmployee(){ int boxIndex = editEmployeeBox.getSelectionModel().getSelectedIndex(); if(boxIndex >= 0){ int index = model.getIDbyIndex(boxIndex); Employee currentEmployee = model.getEmployee(index); editEmployeeFirstName.setText(currentEmployee.getFirstName()); editEmployeeLastName.setText(currentEmployee.getLastName()); editEmployeeManager.setSelected(currentEmployee.isManager()); editEmployeeEmail.setText(currentEmployee.getEmail()); editEmployeePhoneNumber.setText(currentEmployee.getPhoneNumber()); System.out.println(currentEmployee.getPositions()); } } public void addRemovePosition(){ VBox tempBox = new VBox(); tempBox.setPadding(new Insets(20)); tempBox.setSpacing(20); ComboBox tempCombo = new ComboBox(); Button addPosition = new Button("Add Position"); Button removePosition = new Button("Remove Position"); Label result = new Label(""); tempBox.getChildren().addAll(tempCombo,addPosition,removePosition, result); tempCombo.setOnMouseClicked(this::populatePositionBox); addPosition.setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { VBox tempVBox = new VBox(); tempVBox.setSpacing(20); tempVBox.setPadding(new Insets(30)); TextField tempPositionText = new TextField("Enter Position Here"); TextField tempWageText = new TextField("Enter Wage Here"); Button addPositionSubmit = new Button("Submit"); tempVBox.getChildren().addAll(tempPositionText,tempWageText,addPositionSubmit); Scene tempScene = new Scene(tempVBox); Stage tempStage = new Stage(); tempStage.setScene(tempScene); tempStage.show(); addPositionSubmit.setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { System.out.println("add position submit clicked"); System.out.println(model.addPosition(tempPositionText.getText(),Float.parseFloat(tempWageText.getText()))); tempStage.close(); } }); } }); removePosition.setOnMouseClicked(e -> { if (tempCombo.getValue() == null) result.setText("No position selected."); else result.setText(model.removePosition(tempCombo.getSelectionModel().getSelectedItem().toString())); }); root.setCenter(tempBox); } }