From 65874c4b30ecbf118fdecc0a3eb7f6b8a103b0d8 Mon Sep 17 00:00:00 2001 From: sasha Date: Tue, 2 Dec 2025 04:30:23 -0500 Subject: [PATCH 1/2] Chore list logic --- src/main/java/app/AppBuilder.java | 4 +++- .../dashboard/DashboardPresenter.java | 1 + .../dashboard/DashboardState.java | 12 ++++++++++++ .../dashboard/DashboardInteractor.java | 19 +++++++++++++++++-- .../dashboard/DashboardOutputData.java | 12 +++++++++++- 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java index 3df177e..27dbccc 100644 --- a/src/main/java/app/AppBuilder.java +++ b/src/main/java/app/AppBuilder.java @@ -272,12 +272,14 @@ public AppBuilder addDashboardUseCase() { DynamoDbClientSingleton.getInstance()); final CommitDataAccessInterface commitDataAccess = new CommitDataAccessObject( DynamoDbClientSingleton.getInstance()); + final ChoreDataAccessInterface choreDataAccess = new ChoreDataAccessObject( + DynamoDbClientSingleton.getInstance()); final DashboardOutputBoundary dashboardOutputBoundary = new DashboardPresenter( dashboardViewModel, viewManagerModel, choreCreationViewModel); final DashboardInputBoundary dashboardInteractor = new DashboardInteractor(dashboardOutputBoundary, userService, - roomDataAccess, commitDataAccess); + roomDataAccess, commitDataAccess, choreDataAccess); final DashboardController dashboardController = new DashboardController( dashboardInteractor); diff --git a/src/main/java/interface_adapter/dashboard/DashboardPresenter.java b/src/main/java/interface_adapter/dashboard/DashboardPresenter.java index 03fcee5..6db5606 100644 --- a/src/main/java/interface_adapter/dashboard/DashboardPresenter.java +++ b/src/main/java/interface_adapter/dashboard/DashboardPresenter.java @@ -27,6 +27,7 @@ public void prepareSuccessView(DashboardOutputData dashboardOutputData) { dashboardState.setRoomCode(dashboardOutputData.getRoomCode()); dashboardState.setRoomName(dashboardOutputData.getRoomName()); dashboardState.setRoomDescription(dashboardOutputData.getRoomDescription()); + dashboardState.setChores(dashboardOutputData.getChores()); dashboardViewModel.setState(dashboardState); dashboardViewModel.firePropertyChange(); } diff --git a/src/main/java/interface_adapter/dashboard/DashboardState.java b/src/main/java/interface_adapter/dashboard/DashboardState.java index b8a1196..5b880de 100644 --- a/src/main/java/interface_adapter/dashboard/DashboardState.java +++ b/src/main/java/interface_adapter/dashboard/DashboardState.java @@ -1,10 +1,13 @@ package interface_adapter.dashboard; import java.time.LocalDate; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import entity.Chore; + public class DashboardState { private String currentUsername; private String roomName; @@ -12,6 +15,7 @@ public class DashboardState { private String roomCode; private Map activityData = new HashMap<>(); private Map> commitsMessages = new HashMap<>(); + private List chores = new ArrayList<>(); private String errorMessage; public Map getActivityData() { @@ -38,6 +42,10 @@ public Map> getCommitsMessages() { return commitsMessages; } + public List getChores() { + return chores; + } + public String getErrorMessage() { return errorMessage; } @@ -66,6 +74,10 @@ public void setCommitsMessages(Map> commitsMessages) { this.commitsMessages = commitsMessages; } + public void setChores(List chores) { + this.chores = chores; + } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } diff --git a/src/main/java/use_case/dashboard/DashboardInteractor.java b/src/main/java/use_case/dashboard/DashboardInteractor.java index 1ad910e..e9c4182 100644 --- a/src/main/java/use_case/dashboard/DashboardInteractor.java +++ b/src/main/java/use_case/dashboard/DashboardInteractor.java @@ -2,12 +2,15 @@ import java.time.LocalDate; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; +import entity.Chore; import entity.Commit; import entity.Room; import entity.User; +import use_case.chore.ChoreDataAccessInterface; import use_case.commit.CommitDataAccessInterface; import use_case.logged_in.UserService; import use_case.room.RoomDataAccessInterface; @@ -17,18 +20,21 @@ public class DashboardInteractor implements DashboardInputBoundary { private final UserService userService; private final RoomDataAccessInterface roomDataAccessObject; private final CommitDataAccessInterface commitDataAccessObject; + private final ChoreDataAccessInterface choreDataAccessObject; public DashboardInteractor(DashboardOutputBoundary dashboardPresenter, UserService userService, RoomDataAccessInterface roomDataAccessObject, - CommitDataAccessInterface commitDataAccessObject) { + CommitDataAccessInterface commitDataAccessObject, ChoreDataAccessInterface choreDataAccessObject) { this.dashboardPresenter = dashboardPresenter; this.userService = userService; this.roomDataAccessObject = roomDataAccessObject; this.commitDataAccessObject = commitDataAccessObject; + this.choreDataAccessObject = choreDataAccessObject; } @Override + @SuppressWarnings("checkstyle:IllegalCatch") public void execute(DashboardInputData dashboardInputData) { final User user = userService.getUser(); if (user == null) { @@ -61,13 +67,22 @@ public void execute(DashboardInputData dashboardInputData) { messages.computeIfAbsent(date, key -> new ArrayList<>()).add(commit.getMessage()); } + List chores; + try { + chores = choreDataAccessObject.getChoresForRoom(roomId); + chores.sort(Comparator.comparing(Chore::getDueDate)); + } catch (Exception empty) { + chores = new ArrayList<>(); + } + final DashboardOutputData dashboardOutputData = new DashboardOutputData(true, user.getUsername(), room.getName(), room.getDescription(), room.getInviteCode(), counts, - messages); + messages, + chores); dashboardPresenter.prepareSuccessView(dashboardOutputData); } diff --git a/src/main/java/use_case/dashboard/DashboardOutputData.java b/src/main/java/use_case/dashboard/DashboardOutputData.java index 86853c3..dd71271 100644 --- a/src/main/java/use_case/dashboard/DashboardOutputData.java +++ b/src/main/java/use_case/dashboard/DashboardOutputData.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Map; +import entity.Chore; + public class DashboardOutputData { private final boolean success; private final String currentUsername; @@ -12,14 +14,17 @@ public class DashboardOutputData { private final String roomCode; private final Map activityData; private final Map> commitsMessages; + private final List chores; + @SuppressWarnings("checkstyle:ParameterNumber") public DashboardOutputData(boolean success, String currentUsername, String roomName, String roomDescription, String roomCode, Map activityData, - Map> commitsMessages) { + Map> commitsMessages, + List chores) { this.success = success; this.currentUsername = currentUsername; this.roomName = roomName; @@ -27,6 +32,7 @@ public DashboardOutputData(boolean success, this.roomCode = roomCode; this.activityData = activityData; this.commitsMessages = commitsMessages; + this.chores = chores; } public boolean isSuccess() { @@ -56,4 +62,8 @@ public Map getActivityData() { public Map> getCommitsMessages() { return commitsMessages; } + + public List getChores() { + return chores; + } } From 315b24b0b1a2a6d74080975e39a52a7085fdbe2c Mon Sep 17 00:00:00 2001 From: sasha Date: Tue, 2 Dec 2025 06:41:07 -0500 Subject: [PATCH 2/2] Chore list implementation in DashboardView, colors and constants in ViewColors and ViewConstants to not have magic numbers --- src/main/java/view/DashboardView.java | 179 ++++++++++++++++++++++---- src/main/java/view/ViewColors.java | 14 ++ src/main/java/view/ViewConstants.java | 17 +++ 3 files changed, 188 insertions(+), 22 deletions(-) diff --git a/src/main/java/view/DashboardView.java b/src/main/java/view/DashboardView.java index 518d7d3..3d46801 100644 --- a/src/main/java/view/DashboardView.java +++ b/src/main/java/view/DashboardView.java @@ -5,9 +5,12 @@ import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.time.format.DateTimeFormatter; +import java.util.List; import javax.swing.*; +import entity.Chore; import interface_adapter.dashboard.DashboardController; import interface_adapter.dashboard.DashboardState; import interface_adapter.dashboard.DashboardViewModel; @@ -16,13 +19,14 @@ public class DashboardView extends JPanel implements PropertyChangeListener { private final ActivityTilesPanel activityTilesPanel; + private final JPanel choresListPanel; private final DashboardViewModel dashboardViewModel; private DashboardController dashboardController; private final JLabel roomNameLabel; private final JLabel inviteCodeLabel; private final JLabel descriptionLabel; - @SuppressWarnings("checkstyle:ExecutableStatementCount") + @SuppressWarnings({ "checkstyle:ExecutableStatementCountCheck", "JavaNCSS" }) public DashboardView(DashboardViewModel dashboardViewModel) { this.dashboardViewModel = dashboardViewModel; @@ -34,28 +38,31 @@ public DashboardView(DashboardViewModel dashboardViewModel) { contentPanel.setBorder(BorderFactory.createEmptyBorder(pad, pad, pad, pad)); final GridBagConstraints constraints = new GridBagConstraints(); - constraints.fill = GridBagConstraints.VERTICAL; - final int spacing = ViewConstants.SPACING_5; - constraints.insets = new Insets(1, spacing, 1, spacing); + constraints.fill = GridBagConstraints.BOTH; + constraints.insets = new Insets(1, 1, 1, 1); constraints.gridx = 0; constraints.gridy = 0; constraints.gridwidth = 1; constraints.weightx = ViewConstants.DASHBOARD_WEIGHTX; constraints.weighty = ViewConstants.DASHBOARD_WEIGHTY; + constraints.anchor = GridBagConstraints.WEST; roomNameLabel = new JLabel(); roomNameLabel.setFont(ViewConstants.TITLE_FONT); roomNameLabel.setForeground(ViewColors.DARK_BLUE); contentPanel.add(roomNameLabel, constraints); + constraints.gridx = 1; constraints.anchor = GridBagConstraints.EAST; inviteCodeLabel = new JLabel(); inviteCodeLabel.setFont(ViewConstants.LABEL_FONT); inviteCodeLabel.setForeground(ViewColors.DARK_BLUE); contentPanel.add(inviteCodeLabel, constraints); + constraints.gridx = 0; constraints.gridy++; + constraints.gridwidth = 2; constraints.anchor = GridBagConstraints.CENTER; descriptionLabel = new JLabel(); descriptionLabel.setFont(ViewConstants.LABEL_FONT); @@ -63,16 +70,30 @@ public DashboardView(DashboardViewModel dashboardViewModel) { contentPanel.add(descriptionLabel, constraints); constraints.gridy++; + constraints.gridwidth = ViewConstants.DASHBOARD_GRID_WIDTH; + constraints.weighty = ViewConstants.DASHBOARD_SECTION_WEIGHT; final DashboardState dashboardState = dashboardViewModel.getState(); activityTilesPanel = new ActivityTilesPanel(dashboardState.getActivityData(), dashboardState.getCommitsMessages()); - final JPanel tilesSection = createSection(activityTilesPanel); + final JPanel tilesSection = createSection("Chore Activity", activityTilesPanel, true); contentPanel.add(tilesSection, constraints); + constraints.gridy++; + constraints.gridwidth = ViewConstants.DASHBOARD_GRID_WIDTH; + constraints.weighty = ViewConstants.DASHBOARD_SECTION_WEIGHT; + + this.choresListPanel = new JPanel(); + choresListPanel.setLayout(new BoxLayout(choresListPanel, BoxLayout.Y_AXIS)); + choresListPanel.setBackground(Color.WHITE); + final JScrollPane choresScrollPane = new JScrollPane(choresListPanel); + choresScrollPane.setBorder(null); + final JPanel choresSection = createSection("Upcoming Chores", choresScrollPane, false); + contentPanel.add(choresSection, constraints); + add(contentPanel, BorderLayout.CENTER); } - private JPanel createSection(Component content) { + private JPanel createSection(String title, Component content, boolean hasButton) { final JPanel section = new JPanel(new BorderLayout()); section.setBackground(ViewColors.SAND_BACKGROUND); section.setBorder(BorderFactory.createCompoundBorder( @@ -85,32 +106,144 @@ private JPanel createSection(Component content) { headerPanel.setBackground(ViewColors.SAND_BACKGROUND); headerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, ViewConstants.DASHBOARD_BORDER, 0)); - final JLabel titleLabel = new JLabel("Chore Activity"); + final JLabel titleLabel = new JLabel(title); titleLabel.setFont(ViewConstants.TITLE_FONT); titleLabel.setForeground(ViewColors.DARK_BLUE); - final JButton createChoreButton = new ButtonBuilder() - .setText("Create Chore") - .setFont(ViewConstants.LABEL_FONT) - .setBackground(ViewColors.ORANGE) - .setForeground(Color.WHITE) - .build(); - - createChoreButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dashboardController.switchToChoreCreationView(); - } - }); - headerPanel.add(titleLabel, BorderLayout.WEST); - headerPanel.add(createChoreButton, BorderLayout.EAST); + + if (hasButton) { + final JButton createChoreButton = new ButtonBuilder() + .setText("Create Chore") + .setFont(ViewConstants.LABEL_FONT) + .setBackground(ViewColors.ORANGE) + .setForeground(Color.WHITE) + .build(); + + createChoreButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dashboardController.switchToChoreCreationView(); + } + }); + + headerPanel.add(createChoreButton, BorderLayout.EAST); + } + section.add(headerPanel, BorderLayout.NORTH); section.add(content, BorderLayout.CENTER); return section; } + private void updateChoresList(List chores) { + choresListPanel.removeAll(); + + if (chores == null || chores.isEmpty()) { + final JLabel emptyLabel = new JLabel("No chores yet. Click Create Chore to add one!"); + emptyLabel.setFont(ViewConstants.LABEL_FONT); + emptyLabel.setForeground(ViewColors.CHORE_DESCRIPTION_TEXT); + emptyLabel.setBorder(BorderFactory.createEmptyBorder( + ViewConstants.CHORE_EMPTY_MESSAGE_PADDING, + ViewConstants.CHORE_EMPTY_MESSAGE_PADDING, + ViewConstants.CHORE_EMPTY_MESSAGE_PADDING, + ViewConstants.CHORE_EMPTY_MESSAGE_PADDING)); + choresListPanel.add(emptyLabel); + } else { + for (Chore chore : chores) { + choresListPanel.add(createChoreCard(chore)); + choresListPanel.add(Box.createRigidArea(new Dimension(0, ViewConstants.CHORE_CARD_SPACING))); + } + } + + choresListPanel.revalidate(); + choresListPanel.repaint(); + } + + @SuppressWarnings({ "checkstyle:ExecutableStatementCountCheck", "JavaNCSS" }) + private JPanel createChoreCard(Chore chore) { + final JPanel card = new JPanel(new BorderLayout()); + card.setBackground(Color.WHITE); + card.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder( + ViewColors.CHORE_CARD_BORDER, + ViewConstants.CHORE_CARD_BORDER_THICKNESS), + BorderFactory.createEmptyBorder( + ViewConstants.CHORE_CARD_PADDING_VERTICAL, + ViewConstants.CHORE_CARD_PADDING_HORIZONTAL, + ViewConstants.CHORE_CARD_PADDING_VERTICAL, + ViewConstants.CHORE_CARD_PADDING_HORIZONTAL) + )); + card.setMaximumSize(new Dimension(Integer.MAX_VALUE, ViewConstants.CHORE_CARD_MAX_HEIGHT)); + + final JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); + leftPanel.setBackground(Color.WHITE); + + final JLabel titleLabel = new JLabel(chore.getTitle()); + titleLabel.setFont(ViewConstants.LABEL_FONT.deriveFont(Font.BOLD)); + titleLabel.setForeground(ViewColors.DARK_BLUE); + + final String description = chore.getDescription(); + final JLabel descLabel = new JLabel(description); + descLabel.setFont(ViewConstants.LABEL_FONT.deriveFont((float) ViewConstants.CHORE_DESCRIPTION_FONT_SIZE)); + descLabel.setForeground(ViewColors.CHORE_DESCRIPTION_TEXT); + + leftPanel.add(titleLabel); + leftPanel.add(Box.createRigidArea(new Dimension(0, ViewConstants.SPACING_5))); + leftPanel.add(descLabel); + + final JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.setBackground(Color.WHITE); + rightPanel.setAlignmentY(Component.CENTER_ALIGNMENT); + + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd, yyyy HH:mm"); + final JLabel dueDateLabel = new JLabel("Due: " + chore.getDueDate().format(formatter)); + dueDateLabel.setFont(ViewConstants.LABEL_FONT.deriveFont((float) ViewConstants.CHORE_DESCRIPTION_FONT_SIZE)); + + final JLabel statusLabel = new JLabel(chore.getStatus().name()); + statusLabel.setFont(ViewConstants.LABEL_FONT.deriveFont( + Font.BOLD, (float) ViewConstants.CHORE_STATUS_FONT_SIZE)); + statusLabel.setOpaque(true); + statusLabel.setBorder(BorderFactory.createEmptyBorder( + ViewConstants.CHORE_STATUS_PADDING, + ViewConstants.CHORE_STATUS_PADDING_HORIZONTAL, + ViewConstants.CHORE_STATUS_PADDING, + ViewConstants.CHORE_STATUS_PADDING_HORIZONTAL)); + + switch (chore.getStatus()) { + case INACTIVE: + statusLabel.setBackground(ViewColors.STATUS_INACTIVE_BG); + statusLabel.setForeground(ViewColors.STATUS_INACTIVE_FG); + break; + case PENDING: + statusLabel.setBackground(ViewColors.STATUS_PENDING_BG); + statusLabel.setForeground(ViewColors.STATUS_PENDING_FG); + break; + case REVIEW_PENDING: + statusLabel.setBackground(ViewColors.STATUS_REVIEW_PENDING_BG); + statusLabel.setForeground(ViewColors.STATUS_REVIEW_PENDING_FG); + break; + case COMPLETED: + statusLabel.setBackground(ViewColors.STATUS_COMPLETED_BG); + statusLabel.setForeground(ViewColors.STATUS_COMPLETED_FG); + break; + default: + statusLabel.setBackground(ViewColors.STATUS_INACTIVE_BG); + statusLabel.setForeground(ViewColors.STATUS_INACTIVE_FG); + } + + rightPanel.add(dueDateLabel); + rightPanel.add(Box.createRigidArea(new Dimension(0, ViewConstants.SPACING_5))); + rightPanel.add(statusLabel); + + card.add(leftPanel, BorderLayout.CENTER); + card.add(rightPanel, BorderLayout.EAST); + + return card; + } + @Override public void propertyChange(PropertyChangeEvent evt) { final DashboardState state = dashboardViewModel.getState(); @@ -125,6 +258,8 @@ public void propertyChange(PropertyChangeEvent evt) { descriptionLabel.setText("Description: " + state.getRoomDescription()); inviteCodeLabel.setText("Invite Code: " + state.getRoomCode()); + updateChoresList(state.getChores()); + revalidate(); repaint(); } diff --git a/src/main/java/view/ViewColors.java b/src/main/java/view/ViewColors.java index 59bba4f..6523237 100644 --- a/src/main/java/view/ViewColors.java +++ b/src/main/java/view/ViewColors.java @@ -13,4 +13,18 @@ public class ViewColors { public static final Color TILE_LEVEL_2 = new Color(64, 196, 99); public static final Color TILE_LEVEL_3 = new Color(48, 161, 78); public static final Color TILE_LEVEL_4 = new Color(33, 110, 57); + + // ---- Chore Card Colors ---- + public static final Color CHORE_CARD_BORDER = new Color(220, 220, 220); + public static final Color CHORE_DESCRIPTION_TEXT = Color.GRAY; + + // ---- Chore Status Colors ---- + public static final Color STATUS_INACTIVE_BG = Color.LIGHT_GRAY; + public static final Color STATUS_INACTIVE_FG = Color.DARK_GRAY; + public static final Color STATUS_PENDING_BG = new Color(255, 243, 205); + public static final Color STATUS_PENDING_FG = new Color(180, 120, 0); + public static final Color STATUS_REVIEW_PENDING_BG = new Color(220, 237, 255); + public static final Color STATUS_REVIEW_PENDING_FG = new Color(0, 100, 200); + public static final Color STATUS_COMPLETED_BG = new Color(220, 255, 220); + public static final Color STATUS_COMPLETED_FG = new Color(0, 150, 0); } diff --git a/src/main/java/view/ViewConstants.java b/src/main/java/view/ViewConstants.java index f82c46c..07455c3 100644 --- a/src/main/java/view/ViewConstants.java +++ b/src/main/java/view/ViewConstants.java @@ -97,6 +97,23 @@ public final class ViewConstants { public static final int DAYS_PER_WEEK = 7; public static final int DISMISS_DELAY = 10000; + // ---- Chore Card Constants ---- + public static final int CHORE_CARD_BORDER_COLOR = 220; + public static final int CHORE_CARD_BORDER_THICKNESS = 1; + public static final int CHORE_CARD_PADDING_VERTICAL = 10; + public static final int CHORE_CARD_PADDING_HORIZONTAL = 15; + public static final int CHORE_CARD_MAX_HEIGHT = 80; + public static final int CHORE_CARD_SPACING = 10; + public static final int CHORE_DESCRIPTION_FONT_SIZE = 12; + public static final int CHORE_STATUS_FONT_SIZE = 11; + public static final int CHORE_STATUS_PADDING = 2; + public static final int CHORE_STATUS_PADDING_HORIZONTAL = 8; + public static final int CHORE_EMPTY_MESSAGE_PADDING = 20; + + // ---- Dashboard Section Weights ---- + public static final double DASHBOARD_SECTION_WEIGHT = 0.5; + public static final int DASHBOARD_GRID_WIDTH = 2; + // ---- Insets ---- public static final Insets TEXT_FIELD_INSETS = new Insets(5, 5, 5, 5);