diff --git a/lib/commons-7.2.5.jar b/lib/commons-7.2.5.jar new file mode 100644 index 0000000..ac34286 Binary files /dev/null and b/lib/commons-7.2.5.jar differ diff --git a/lib/io-7.2.5.jar b/lib/io-7.2.5.jar new file mode 100644 index 0000000..0e434c7 Binary files /dev/null and b/lib/io-7.2.5.jar differ diff --git a/lib/kernel-7.2.5.jar b/lib/kernel-7.2.5.jar new file mode 100644 index 0000000..b5062cf Binary files /dev/null and b/lib/kernel-7.2.5.jar differ diff --git a/lib/layout-7.2.5.jar b/lib/layout-7.2.5.jar new file mode 100644 index 0000000..fab9797 Binary files /dev/null and b/lib/layout-7.2.5.jar differ diff --git a/lib/sqlite-jdbc-3.51.2.0.jar b/lib/sqlite-jdbc-3.51.2.0.jar new file mode 100644 index 0000000..822a5c1 Binary files /dev/null and b/lib/sqlite-jdbc-3.51.2.0.jar differ diff --git a/managementdata.ser b/managementdata.ser new file mode 100644 index 0000000..855bdda Binary files /dev/null and b/managementdata.ser differ diff --git a/src/main/java/org/codedifferently/Appoitment.java b/src/main/java/org/codedifferently/Appoitment.java new file mode 100644 index 0000000..7a09000 --- /dev/null +++ b/src/main/java/org/codedifferently/Appoitment.java @@ -0,0 +1,32 @@ +package org.codedifferently; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + + +class Appointment implements Serializable { + private String customerID; + private LocalDateTime time; + private boolean isCompleted; + + public Appointment(String customerID, LocalDateTime time) { + this.customerID = customerID; + this.time = time; + this.isCompleted = false; + } + + public String getCustomerID() { return customerID; } + public LocalDateTime getTime() { return time; } + public boolean isCompleted() { return isCompleted; } + + public void complete() { this.isCompleted = true; } + + @Override + public String toString() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + return "Appointment | Customer ID: " + customerID + + " | Time: " + time.format(formatter) + + " | Completed: " + isCompleted; + } +} diff --git a/src/main/java/org/codedifferently/ConsultantWeeklyGUI.java b/src/main/java/org/codedifferently/ConsultantWeeklyGUI.java new file mode 100644 index 0000000..e442d2e --- /dev/null +++ b/src/main/java/org/codedifferently/ConsultantWeeklyGUI.java @@ -0,0 +1,312 @@ +package org.codedifferently; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + + +public class ConsultantWeeklyGUI { + + private ManagementSystem management; + private JFrame frame; + private JTextArea outputArea; + private JPanel calendarPanel; + private JScrollPane calendarScrollPane; + private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); +// Main GUI interface + public ConsultantWeeklyGUI() { + + management = ManagementSystem.loadData(); + + frame = new JFrame("Lan & Amani Consultant Weekly Calendar"); + frame.setSize(1200, 700); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLayout(new BorderLayout()); + + outputArea = new JTextArea(); + outputArea.setEditable(false); + frame.add(new JScrollPane(outputArea), BorderLayout.EAST); + + +// Buttons + JPanel topPanel = new JPanel(new FlowLayout()); + JButton addCustomerBtn = new JButton("Add Customer"); + JButton searchCustomerBtn = new JButton("Search Customer"); + JButton checkInBtn = new JButton("Check-In"); + JButton saveBtn = new JButton("Save Data"); + JButton dailyReportBtn = new JButton("Daily Report"); + JButton viewScheduleBtn = new JButton("View Schedule"); + JButton viewAllCustomersBtn = new JButton("View All Customers"); + topPanel.add(viewAllCustomersBtn); + + topPanel.add(addCustomerBtn); + topPanel.add(searchCustomerBtn); + topPanel.add(checkInBtn); + topPanel.add(saveBtn); + topPanel.add(dailyReportBtn); + topPanel.add(viewScheduleBtn); + frame.add(topPanel, BorderLayout.NORTH); + + calendarPanel = new JPanel(); + calendarPanel.setLayout(new GridLayout(19, 8, 2, 2)); + calendarScrollPane = new JScrollPane(calendarPanel); + frame.add(calendarScrollPane, BorderLayout.CENTER); + + + // ---------------- Actions ---------------- + viewAllCustomersBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + outputArea.append("--- All Customers ---\n"); + ArrayList customers = management.getCustomers(); + if (customers.isEmpty()) { + outputArea.append("No customer found.\n"); + } else { + for (Customer p : customers) { + outputArea.append(p.toString() + "\n"); + } + } + } + }); + addCustomerBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String name = JOptionPane.showInputDialog(frame, "Customer Name:"); + + if (name != null && !name.trim().isEmpty()) { + management.addCustomer(name.trim()); + outputArea.append("Added customer: " + name + "\n"); + updateCalendar(); + } else { + JOptionPane.showMessageDialog(frame, "Invalid customer name."); + } + } + + }); + + searchCustomerBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String id = JOptionPane.showInputDialog(frame, "Customer ID or Name:"); + Customer c = management.searchCustomer(id); + if (c != null) outputArea.append(c.toString() + "\n"); + else outputArea.append("customer not found.\n"); + } + }); + + checkInBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String id = JOptionPane.showInputDialog(frame, "customer ID:"); + management.checkInCustomer(id); + outputArea.append("Checked in: " + id + "\n"); + updateCalendar(); + } + }); + + saveBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + management.saveData(); + JOptionPane.showMessageDialog(frame, "Data saved successfully."); + } + }); + + dailyReportBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + outputArea.append("--- Daily Report ---\n"); + LocalDate today = LocalDate.now(); + int total = 0, completed = 0; + for (Appointment a : management.getAppointments()) { + if (a.getTime().toLocalDate().equals(today)) { + total++; + if (a.isCompleted()) completed++; + } + } + outputArea.append("Appointments Today: " + total + "\n"); + outputArea.append("Completed Today: " + completed + "\n"); + } + }); + + viewScheduleBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + outputArea.append("--- All Appointments ---\n"); + for (Appointment a : management.getAppointments()) outputArea.append(a.toString() + "\n"); + } + }); + + updateCalendar(); + frame.setVisible(true); + } + + // ---------------- Calendar ---------------- + private void updateCalendar() { + calendarPanel.removeAll(); + + LocalDate today = LocalDate.now(); + LocalTime nowTime = LocalTime.now(); + final LocalDateTime nowSlot = LocalDateTime.of(today, + nowTime.withMinute((nowTime.getMinute() / 30) * 30).withSecond(0).withNano(0)); +//use ArrayList to show calender panel + java.util.List weekDates = new ArrayList<>(); + for (int i = 0; i < 7; i++) weekDates.add(today.plusDays(i)); + + calendarPanel.add(new JLabel("Time")); + for (LocalDate d : weekDates) calendarPanel.add(new JLabel(d.getDayOfWeek() + " " + d.toString())); + + LocalTime start = LocalTime.of(9, 0); + LocalTime end = LocalTime.of(17, 0); + + JButton currentSlotButton = null; + + while (!start.isAfter(end.minusMinutes(30))) { + final LocalTime displayTime = start; + calendarPanel.add(new JLabel(displayTime.toString())); + + for (LocalDate date : weekDates) { + final LocalDateTime slotTime = LocalDateTime.of(date, start); + final java.util.List customerNames = new ArrayList<>(management.getCustomerNamesForSlot(slotTime)); + final int waitlistCount = management.getWaitlistMap().containsKey(slotTime) + ? management.getWaitlistMap().get(slotTime).size() : 0; + + String label; + if (customerNames.isEmpty()) label = "Available"; + else if (waitlistCount > 0) label = customerNames.get(0) + " (" + waitlistCount + " waitlist)"; + else label = customerNames.get(0); + + final String buttonLabel = label; + JButton slotBtn = new JButton(buttonLabel); + + if (customerNames.isEmpty()) slotBtn.setBackground(Color.GREEN); + else if (waitlistCount > 0) slotBtn.setBackground(Color.ORANGE); + else slotBtn.setBackground(Color.RED); + + slotBtn.setOpaque(true); + slotBtn.setBorderPainted(true); + + if (slotTime.equals(nowSlot)) { + slotBtn.setBorder(BorderFactory.createLineBorder(Color.YELLOW, 3)); + currentSlotButton = slotBtn; + } else { + slotBtn.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); + } + + // LEFT-CLICK + slotBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleSlotClick(slotTime); + } + }); + + // RIGHT-CLICK popup menu + JPopupMenu menu = new JPopupMenu(); + JMenuItem scheduleItem = new JMenuItem("Schedule / Join Waitlist"); + JMenuItem completeItem = new JMenuItem("Complete Appointment"); + JMenuItem cancelItem = new JMenuItem("Cancel Appointment"); + + scheduleItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleSlotClick(slotTime); + } + }); + completeItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleComplete(slotTime); + } + }); + cancelItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleCancel(slotTime); + } + }); + + menu.add(scheduleItem); + menu.add(completeItem); + menu.add(cancelItem); + slotBtn.setComponentPopupMenu(menu); + + calendarPanel.add(slotBtn); + } + + start = start.plusMinutes(30); + } + + calendarPanel.revalidate(); + calendarPanel.repaint(); + + if (currentSlotButton != null) { + final JButton scrollBtn = currentSlotButton; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + Rectangle rect = scrollBtn.getBounds(); + calendarScrollPane.getViewport().scrollRectToVisible(rect); + } + }); + } + } + + // ---------------- Slot Handlers ---------------- + private void handleSlotClick(LocalDateTime slotTime) { + String customerID = JOptionPane.showInputDialog(frame, "Enter Customer ID:"); + if (customerID == null || customerID.isEmpty()) return; + + if (management.isSlotAvailable(slotTime)) { + management.scheduleAppointment(customerID, slotTime); + outputArea.append("Scheduled " + customerID + " at " + slotTime.format(dtf) + "\n"); + } else { + int option = JOptionPane.showConfirmDialog(frame, + "Slot taken. Join waitlist?", "Waitlist", JOptionPane.YES_NO_OPTION); + if (option == JOptionPane.YES_OPTION) { + management.scheduleAppointment(customerID, slotTime); + outputArea.append("Added to waitlist: " + customerID + " at " + slotTime.format(dtf) + "\n"); + } + } + updateCalendar(); + } + + private void handleComplete(LocalDateTime slotTime) { + String customerID = JOptionPane.showInputDialog(frame, "Enter Customer ID to complete:"); + if (customerID == null || customerID.isEmpty()) return; + management.completeAppointment(customerID, slotTime); + outputArea.append("Completed: " + customerID + " at " + slotTime.format(dtf) + "\n"); + updateCalendar(); + } + + private void handleCancel(LocalDateTime slotTime) { + String customerID = JOptionPane.showInputDialog(frame, "Enter Customer ID to cancel:"); + if (customerID == null || customerID.isEmpty()) return; + + // Cancel the appointment, promote from waitlist automatically + management.cancelAppointment(customerID, slotTime); + outputArea.append("Cancelled appointment: " + customerID + " at " + slotTime.format(dtf) + "\n"); + + // Immediately refresh calendar so both schedule and waitlist update + updateCalendar(); + + // Optional: print current slot status + List customerNames = management.getCustomerNamesForSlot(slotTime); + int waitlistCount = management.getWaitlistMap().containsKey(slotTime) + ? management.getWaitlistMap().get(slotTime).size() : 0; + + if (!customerNames.isEmpty()) + outputArea.append("Now scheduled: " + customerNames.get(0) + " (" + waitlistCount + " on waitlist)\n"); + else if (waitlistCount > 0) + outputArea.append("Slot is now empty, waitlist: " + waitlistCount + "\n"); + else + outputArea.append("Slot is now available.\n"); + + + updateCalendar(); + } + + // ---------------- MAIN ---------------- + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + new ConsultantWeeklyGUI(); + } + }); + } +} diff --git a/src/main/java/org/codedifferently/Customer.java b/src/main/java/org/codedifferently/Customer.java new file mode 100644 index 0000000..76e774c --- /dev/null +++ b/src/main/java/org/codedifferently/Customer.java @@ -0,0 +1,32 @@ +package org.codedifferently; + +import java.io.Serializable; +import java.util.UUID; + +public class Customer implements Serializable { + private String name; + private String ID; + private boolean isCheckedIn; + + public Customer(String name) { + this.name = name; + this.ID = generateID(name); + this.isCheckedIn = false; + } + + private String generateID(String name) { + return name.substring(0,1).toUpperCase() + UUID.randomUUID().toString().substring(0,5); + } + + public String getName() { return name; } + public String getID() { return ID; } + public boolean isCheckedIn() { return isCheckedIn; } + + public void checkIn() { this.isCheckedIn = true; } + public void checkOut() { this.isCheckedIn = false; } + + @Override + public String toString() { + return "Customer ID: " + ID + ", Name: " + name + ", Checked-in: " + isCheckedIn; + } +} diff --git a/src/main/java/org/codedifferently/ManagementSystem.java b/src/main/java/org/codedifferently/ManagementSystem.java new file mode 100644 index 0000000..2f18f84 --- /dev/null +++ b/src/main/java/org/codedifferently/ManagementSystem.java @@ -0,0 +1,122 @@ +package org.codedifferently; + +import java.io.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +// store customers & appointments in ArrayList because the list can expand and store different data types +// Use Map for local date & time ( key - value) +public class ManagementSystem implements Serializable { + private ArrayList customers; + private ArrayList appointments; + private HashMap> waitlistMap; + + public ManagementSystem() { + customers = new ArrayList<>(); + appointments = new ArrayList<>(); + waitlistMap = new HashMap<>(); + } + + // ---------------- Customer Methods ---------------- + public void addCustomer(String name) { + Customer customer = new Customer(name); + customers.add(customer); + } + + public Customer searchCustomer(String idOrName) { + for (Customer c : customers) { + if (c.getID().equalsIgnoreCase(idOrName) || c.getName().equalsIgnoreCase(idOrName)) + return c; + } + return null; + } + + + public void checkInCustomer(String idOrName) { + Customer c = searchCustomer(idOrName); + if (c != null) c.checkIn(); + } + + public ArrayList getCustomers() { return customers; } + + // ---------------- Appointment Methods ---------------- + public boolean isSlotAvailable(LocalDateTime slotTime) { + for (Appointment a : appointments) { + if (a.getTime().equals(slotTime)) return false; + } + return true; + } + + public void scheduleAppointment(String customerID, LocalDateTime slotTime) { + if (isSlotAvailable(slotTime)) { + appointments.add(new Appointment(customerID, slotTime)); + } else { + waitlistMap.putIfAbsent(slotTime, new ArrayList<>()); + waitlistMap.get(slotTime).add(customerID); + } + } + + public void completeAppointment(String customerID, LocalDateTime slotTime) { + for (Appointment a : appointments) { + if (a.getCustomerID().equalsIgnoreCase(customerID) && a.getTime().equals(slotTime)) { + a.complete(); + promoteWaitlist(slotTime); + return; + } + } + } + + public void cancelAppointment(String customerID, LocalDateTime slotTime) { + Appointment toRemove = null; + for (Appointment a : appointments) { + if (a.getCustomerID().equalsIgnoreCase(customerID) && a.getTime().equals(slotTime)) { + toRemove = a; + break; + } + } + if (toRemove != null) { + appointments.remove(toRemove); + promoteWaitlist(slotTime); + } else if (waitlistMap.containsKey(slotTime)) { + waitlistMap.get(slotTime).remove(customerID); + } + } + + private void promoteWaitlist(LocalDateTime slotTime) { + if (waitlistMap.containsKey(slotTime) && !waitlistMap.get(slotTime).isEmpty()) { + String nextCustomer = waitlistMap.get(slotTime).remove(0); + appointments.add(new Appointment(nextCustomer, slotTime)); + } + } + + public ArrayList getAppointments() { return appointments; } + public HashMap> getWaitlistMap() { return waitlistMap; } + + public List getCustomerNamesForSlot(LocalDateTime slotTime) { + List names = new ArrayList<>(); + for (Appointment a : appointments) { + if (a.getTime().equals(slotTime)) { + Customer c = searchCustomer(a.getCustomerID()); + names.add(c != null ? c.getName() : a.getCustomerID()); + } + } + return names; + } + + // ---------------- Persistence to store data in a file---------------- + public void saveData() { + try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("managementdata.ser"))) { + + oos.writeObject(this); + } catch (Exception e) { e.printStackTrace(); } + } + + public static ManagementSystem loadData() { + + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("managementdata.ser"))) { + return (ManagementSystem) ois.readObject(); + } catch (Exception e) { return new ManagementSystem(); } + } +} diff --git a/src/main/java/org/codedifferently/README.md b/src/main/java/org/codedifferently/README.md new file mode 100644 index 0000000..ed757a7 --- /dev/null +++ b/src/main/java/org/codedifferently/README.md @@ -0,0 +1,371 @@ +# Consultant Weekly Management System + +## Overview + +The **Consultant Weekly Management System** is a Java-based desktop application designed to help manage customer appointments for a consultant business. + +This system provides a **Graphical User Interface (GUI)** built using **Java Swing**, allowing users to manage customers, schedule appointments, track waitlists, and view weekly calendars. + +The program helps consultants organize their weekly schedule, track customer check-ins, and manage appointment availability efficiently. + +--- + +# Technologies Used + +- Java +- Java Swing (GUI) +- Object-Oriented Programming (OOP) +- Serialization for data persistence +- Collections (ArrayList, HashMap) +- Date and Time API (`LocalDateTime`, `LocalDate`, `LocalTime`) + +--- + +# Project Structure + +The project contains the following classes: + +| Class | Description | +|------|-------------| +| `ConsultantWeeklyGUI.java` | Main GUI interface and application entry point | +| `ManagementSystem.java` | Core logic for managing customers, appointments, and waitlists | +| `Customer.java` | Represents a customer in the system | +| `Appointment.java` | Represents a scheduled appointment | + +--- + +# Application Features + +## Customer Management + +The system allows users to: + +- Add new customers +- Search for customers by ID or name +- View all registered customers +- Check-in customers + +Each customer is assigned a **unique ID** automatically when created. + +Example Customer Output: + +``` +Customer ID: A12345, Name: Alice, Checked-in: true +``` + +--- + +# Appointment Scheduling + +The application includes a **weekly calendar interface** where appointments can be scheduled. + +Features include: + +- Schedule appointments in **30-minute time slots** +- Prevent double-booking +- Join a waitlist if a slot is already booked +- Cancel appointments +- Complete appointments + +Appointments are stored in an **ArrayList**, while waitlists are stored using a **HashMap**. + +--- + +# Weekly Calendar System + +The calendar displays a **7-day schedule** starting from the current date. + +### Working Hours +``` +9:00 AM – 5:00 PM +``` + +### Slot Duration +``` +30 minutes +``` + +### Slot Color Indicators + +| Color | Meaning | +|------|--------| +| Green | Available | +| Red | Booked | +| Orange | Booked with waitlist | +| Yellow Border | Current time slot | + +Users can interact with each slot using: + +- **Left-click** → Schedule appointment +- **Right-click menu** → + - Schedule / Join Waitlist + - Complete Appointment + - Cancel Appointment + +--- + +# Waitlist System + +If a time slot is already booked, customers may join the **waitlist**. + +When an appointment is: + +- **Completed** +- **Canceled** + +The system automatically **promotes the next customer from the waitlist** into the available appointment slot. + +--- + +# Daily Report Feature + +The system can generate a **daily report** showing: + +- Total appointments scheduled today +- Total appointments completed today + +This helps track daily consultant activity. + +Example Output: + +``` +--- Daily Report --- +Appointments Today: 6 +Completed Today: 4 +``` + +--- + +# Data Persistence + +The system uses **Java Serialization** to save application data. + +Saved data includes: + +- Customers +- Appointments +- Waitlists + +Data is stored in: + +``` +managementdata.ser +``` + +When the program starts, it attempts to **load existing data automatically**. + +--- + +# User Interface + +The GUI includes the following controls: + +### Top Menu Buttons + +- Add Customer +- Search Customer +- Check-In Customer +- Save Data +- Daily Report +- View Schedule +- View All Customers + +### Calendar View + +A scrollable weekly calendar where each time slot is represented as a button. + +### Output Panel + +A text area on the right side displays: + +- System messages +- Customer information +- Reports +- Appointment status updates + +--- + +# How to Run the Application + +## Compile the Program + +``` +javac org/codedifferently/*.java +``` + +## Run the Program + +``` +java org.codedifferently.ConsultantWeeklyGUI +``` + +--- + +# Sprint Documentation + +Because the development team recently earned their **Certified Scrum Master certification**, development was organized into three sprint phases. + +--- + +# Sprint 1 — The Plan + +## Problem Statement + +Consultants need a simple system to organize weekly appointments, track customers, and manage scheduling conflicts. + +Without a digital system, managing schedules manually can lead to: + +- Double-booked appointments +- Lost customer records +- Poor organization of daily schedules + +The goal was to create a **Java-based appointment management system** that solves these problems. + +--- + +## Planned Features + +The following features were planned: + +### Customer Features +- Add new customers +- Generate unique IDs +- Search customers +- Check-in customers + +### Appointment Features +- Schedule appointments +- Prevent double-booking +- Cancel appointments +- Track appointment completion + +### Scheduling System +- Weekly calendar +- 30-minute time slots +- Working hours (9 AM – 5 PM) + +### Reporting +- Daily appointment summary +- Completed appointment tracking + +--- + +## Planned Classes + +- `Customer` +- `Appointment` +- `ManagementSystem` +- `ConsultantWeeklyGUI` + +Each class was designed to follow **object-oriented programming principles**. + +--- + +# Sprint 2 — The Build + +## Implemented Features + +The following features were successfully implemented: + +- Java Swing GUI interface +- Weekly calendar scheduling system +- Appointment management +- Waitlist functionality +- Customer management system +- Daily report generation +- Data persistence using serialization + +--- + +## Challenges Encountered + +### GUI Layout Management + +Designing the weekly calendar grid required careful layout planning. + +Solution: +``` +GridLayout was used to organize the calendar slots. +``` + +--- + +### Preventing Double Booking + +Ensuring that no two appointments could occupy the same slot required additional validation. + +Solution: + +The system checks existing appointments before allowing scheduling. + +--- + +### Waitlist Promotion + +Handling waitlist promotion required tracking multiple customers for the same slot. + +Solution: + +A `HashMap>` was used to store waitlists for each slot. + +--- + +# Sprint 3 — The Reflection + +## What Works Well + +The application successfully provides: + +- A functional GUI calendar +- Appointment scheduling +- Waitlist management +- Customer tracking +- Data persistence + +The object-oriented structure also makes the program easy to extend. + +--- + +## Possible Improvements + +With more time, the system could be improved by adding: + +- Database storage instead of serialization +- Email or SMS reminders +- Multiple consultants +- Advanced search filters +- Improved GUI styling + +--- + +## Java Concepts Used + +This project demonstrates the use of: + +- Classes and Objects +- Inheritance and encapsulation +- ArrayLists +- HashMaps +- Event handling +- GUI programming with Swing +- Serialization +- Date and time APIs + +--- + +## Lessons Learned + +This project provided experience in: + +- Designing a multi-class Java application +- Building graphical user interfaces +- Implementing real-world scheduling logic +- Managing program state and data persistence +- Applying Agile development through sprint planning + +--- + +# Authors + +Lan +Amani \ No newline at end of file