JavaFX Scheduler

github specification database erd

Intro

I built this application for a college project. Using a MySQL database, this application allows the user to track appointments and data related to appointments. Additionally, it saves appointment times in UTC, and automatically converts the appointment time in the GUI to the system's time zone.

What I Learned

This is the first application I've made that uses an online SQL server. I learned how to execute queries, and implement results from queries into a GUI. I also learned about localization by translating the login form using resource bundles, and by converting between time zones.

Login Screen

The Login Screen allows the user to enter a username and password and log in to the main part of the application. This screen will also display the user's time zone(v1.1). The Login Form (as well as its error messages) has the functionality to translate into French if the user's computer has French set as the language. When the Login button is pressed, the application queries the Users table in the database to check for the correct username and password. Upon successful login, the main application will start.

Main View

The main screen gives the user a view of all appointments by month and week. The user can use the date selector in the top-right corner to select a timeframe to view. The buttons in the top-left corner allow the user to create, update, and delete appointments using a form.

New Appointment

Customers

The Customers table can be used to add and edit customers in the database. When a customer is removed, the application removes all the appointments connected to that customer first, before deleting the customer. This is nessecary to preserve data integrity. (See Database ERD)

Code Snippet - Remove Customer


public static void delete_customer(Customer to_delete) {
	int customer_id = to_delete.getId();
	// Delete all appointments for the customer
	String delete_query_1 = "DELETE FROM appointments WHERE Customer_ID = " + customer_id;
	// Delete the customer
	String delete_query_2 = "DELETE FROM customers WHERE Customer_ID = " + customer_id;
	try {
		Connection conn = database_connection.getConnection();
		database_query.setStatement(conn);
		Statement statement = database_query.getStatement();
		statement.execute(delete_query_1);
		System.out.println("debug: Removing all appointments for customer " + to_delete.getId());
		statement.execute(delete_query_2);
		System.out.println("debug: Removing customer " + to_delete.getId());
	}
	catch(SQLException e){
		e.printStackTrace();
		ui_popups.errorMessage("Unable to remove customer " + customer_id + ".  Please check your internet connection.");
	}
	finally{
		ui_popups.infoMessage("Customer successfully removed.");
	}
}
							

Reports / Schedules

The Reports screen filters data and generates different reports based on the time period that the user selects. The reports can be generated using appointments from all-time, monthly, or weekly time periods. The help button gives information about what reports are being generated, and how to view the desired reports. There is also a Schedules feature that allows the user to see a schedule for each Contact.

Code Snippet - Generate Reports


public void sortAppointmentTypeCounts(){
	// Get all appointments from the database within the selected timeframe.
	ObservableList appointments;
	if(time_view.equals("MONTH")) {
		appointments = database_operation.getAppointmentsByMonth(timeframe_selected);
		timeframe_label.setText(time_convert.formatMonthNicely(timeframe_selected));
	}
	else if (time_view.equals("WEEK")){
		appointments = database_operation.getAppointmentsByWeek(time_convert.getStartOfWeek(timeframe_selected), time_convert.getEndOfWeek(timeframe_selected));
		timeframe_label.setText(time_convert.formatWeekNicely(timeframe_selected));
	}
	else {
		appointments = database_operation.getAllAppointments();
		timeframe_label.setText("ALL APPOINTMENTS");
	}

	// Show the number of appointments in the UI.
	number_appointments.setText(String.valueOf(appointments.size()));

	// Sort appointments if there are appointments in the given timeframe.
	if (appointments.size() >= 1){
		// Create a List for the appointment type of each appointment.
		List appointment_types = new ArrayList<>();

		// Add each appointment type to the List.
		for(Appointment appointment : appointments) {
			appointment_types.add(appointment.getType());
		}

		// Convert the list into a String[] array.
		String[] itemsArray = new String[appointment_types.size()];
		itemsArray = appointment_types.toArray(itemsArray);

		// Lambda - Count # occurrences of each item and add them to a HashMap.
		Map types_sorted = Arrays.stream(itemsArray)
				.collect(Collectors.groupingBy(s -> s, Collectors.counting()));

		// Convert the map into an ObservableList that can be added to a table.
		ObservableList sorted_types_observable = FXCollections.observableArrayList();
		for (Map.Entry entry : types_sorted.entrySet()) {
			String key = entry.getKey();
			Long value = entry.getValue();
			sorted_types_observable.add(new AppointmentType(key, value.intValue()));
		}

		// Show the appointment types sorted in the table.
		appointments_table.setPlaceholder(new Label("No appointment types found"));
		appointment_type_column.setCellValueFactory(new PropertyValueFactory<>("Type"));
		appointment_number_column.setCellValueFactory(new PropertyValueFactory<>("Number"));
		appointments_table.getItems().setAll(sorted_types_observable);

		// Display the number of different appointment types in the UI.
		number_appointment_types.setText(String.valueOf(sorted_types_observable.size()));
	}

}