diff --git a/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/ActivityDTO.java b/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/ActivityDTO.java index 5f8b3f7..990d3ed 100644 --- a/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/ActivityDTO.java +++ b/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/ActivityDTO.java @@ -13,7 +13,8 @@ public record ActivityDTO( int maxParticipants, List participants, int currentParticipantCount, - ActivityTypeDTO type) { + ActivityTypeDTO type, + DifficultyLevelDTO difficultyLevel) { public record ScheduleDetailsDTO( List days, String startTime, diff --git a/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/DifficultyLevelDTO.java b/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/DifficultyLevelDTO.java new file mode 100644 index 0000000..d2abe01 --- /dev/null +++ b/src/main/java/com/mergingtonhigh/schoolmanagement/application/dtos/DifficultyLevelDTO.java @@ -0,0 +1,9 @@ +package com.mergingtonhigh.schoolmanagement.application.dtos; + +/** + * DTO for difficulty level data transfer. + */ +public record DifficultyLevelDTO( + String name, + String label) { +} \ No newline at end of file diff --git a/src/main/java/com/mergingtonhigh/schoolmanagement/domain/entities/Activity.java b/src/main/java/com/mergingtonhigh/schoolmanagement/domain/entities/Activity.java index 0bf7f94..b0de87c 100644 --- a/src/main/java/com/mergingtonhigh/schoolmanagement/domain/entities/Activity.java +++ b/src/main/java/com/mergingtonhigh/schoolmanagement/domain/entities/Activity.java @@ -7,6 +7,7 @@ import org.springframework.data.mongodb.core.mapping.Document; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.ActivityType; +import com.mergingtonhigh.schoolmanagement.domain.valueobjects.DifficultyLevel; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.Email; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.ScheduleDetails; @@ -24,6 +25,7 @@ public class Activity { private int maxParticipants; private List participants; private ActivityType type; + private DifficultyLevel difficultyLevel; public Activity() { this.participants = new ArrayList<>(); @@ -31,6 +33,11 @@ public Activity() { public Activity(String name, String description, String schedule, ScheduleDetails scheduleDetails, int maxParticipants, ActivityType type) { + this(name, description, schedule, scheduleDetails, maxParticipants, type, null); + } + + public Activity(String name, String description, String schedule, + ScheduleDetails scheduleDetails, int maxParticipants, ActivityType type, DifficultyLevel difficultyLevel) { this.name = validateName(name); this.description = validateDescription(description); this.schedule = schedule; @@ -38,6 +45,7 @@ public Activity(String name, String description, String schedule, this.maxParticipants = validateMaxParticipants(maxParticipants); this.participants = new ArrayList<>(); this.type = type != null ? type : ActivityType.determineFromContent(name, description); + this.difficultyLevel = difficultyLevel; } public boolean canAddParticipant() { @@ -146,4 +154,12 @@ public ActivityType getType() { public void setType(ActivityType type) { this.type = type != null ? type : ActivityType.determineFromContent(this.name, this.description); } + + public DifficultyLevel getDifficultyLevel() { + return difficultyLevel; + } + + public void setDifficultyLevel(DifficultyLevel difficultyLevel) { + this.difficultyLevel = difficultyLevel; + } } \ No newline at end of file diff --git a/src/main/java/com/mergingtonhigh/schoolmanagement/domain/valueobjects/DifficultyLevel.java b/src/main/java/com/mergingtonhigh/schoolmanagement/domain/valueobjects/DifficultyLevel.java new file mode 100644 index 0000000..8e12a64 --- /dev/null +++ b/src/main/java/com/mergingtonhigh/schoolmanagement/domain/valueobjects/DifficultyLevel.java @@ -0,0 +1,21 @@ +package com.mergingtonhigh.schoolmanagement.domain.valueobjects; + +/** + * Enum representing the different difficulty levels of activities. + * Each level has a label for display purposes. + */ +public enum DifficultyLevel { + BEGINNER("Iniciante"), + INTERMEDIATE("Intermediário"), + ADVANCED("Avançado"); + + private final String label; + + DifficultyLevel(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } +} \ No newline at end of file diff --git a/src/main/java/com/mergingtonhigh/schoolmanagement/infrastructure/migrations/V001_InitialDatabaseSetup.java b/src/main/java/com/mergingtonhigh/schoolmanagement/infrastructure/migrations/V001_InitialDatabaseSetup.java index 6b474ce..8006ad1 100644 --- a/src/main/java/com/mergingtonhigh/schoolmanagement/infrastructure/migrations/V001_InitialDatabaseSetup.java +++ b/src/main/java/com/mergingtonhigh/schoolmanagement/infrastructure/migrations/V001_InitialDatabaseSetup.java @@ -12,6 +12,7 @@ import com.mergingtonhigh.schoolmanagement.domain.entities.Activity; import com.mergingtonhigh.schoolmanagement.domain.entities.Teacher; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.ActivityType; +import com.mergingtonhigh.schoolmanagement.domain.valueobjects.DifficultyLevel; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.ScheduleDetails; import io.mongock.api.annotations.ChangeUnit; @@ -70,7 +71,7 @@ private void seedActivities() { chessClub.setParticipants(List.of("michael@mergington.edu", "daniel@mergington.edu")); mongoTemplate.save(chessClub); - // Aula de Programação + // Aula de Programação - Intermediário Activity programmingClass = new Activity( "Aula de Programação", "Aprenda fundamentos de programação e desenvolva projetos de software", @@ -78,7 +79,8 @@ private void seedActivities() { new ScheduleDetails(List.of("Tuesday", "Thursday"), LocalTime.of(7, 0), LocalTime.of(8, 0)), 20, - ActivityType.TECHNOLOGY); + ActivityType.TECHNOLOGY, + DifficultyLevel.INTERMEDIATE); programmingClass.setParticipants(List.of("emma@mergington.edu", "sophia@mergington.edu")); mongoTemplate.save(programmingClass); @@ -118,14 +120,15 @@ private void seedActivities() { basketballTeam.setParticipants(List.of("ava@mergington.edu", "mia@mergington.edu")); mongoTemplate.save(basketballTeam); - // Clube de Arte + // Clube de Arte - Iniciante Activity artClub = new Activity( "Clube de Arte", "Explore diversas técnicas artísticas e crie obras-primas", "Quintas-feiras, 15:15 - 17:00", new ScheduleDetails(List.of("Thursday"), LocalTime.of(15, 15), LocalTime.of(17, 0)), 15, - ActivityType.ARTS); + ActivityType.ARTS, + DifficultyLevel.BEGINNER); artClub.setParticipants(List.of("amelia@mergington.edu", "harper@mergington.edu")); mongoTemplate.save(artClub); @@ -163,25 +166,27 @@ private void seedActivities() { debateTeam.setParticipants(List.of("charlotte@mergington.edu", "amelia@mergington.edu")); mongoTemplate.save(debateTeam); - // Oficina de Robótica + // Oficina de Robótica - Avançado Activity roboticsWorkshop = new Activity( "Oficina de Robótica", "Construa e programe robôs em nossa oficina de última geração", "Sábados, 10:00 - 14:00", new ScheduleDetails(List.of("Saturday"), LocalTime.of(10, 0), LocalTime.of(14, 0)), 15, - ActivityType.TECHNOLOGY); + ActivityType.TECHNOLOGY, + DifficultyLevel.ADVANCED); roboticsWorkshop.setParticipants(List.of("ethan@mergington.edu", "oliver@mergington.edu")); mongoTemplate.save(roboticsWorkshop); - // Olimpíada de Ciências + // Olimpíada de Ciências - Avançado Activity scienceOlympiad = new Activity( "Olimpíada de Ciências", "Preparação para competições científicas regionais e estaduais aos fins de semana", "Sábados, 13:00 - 16:00", new ScheduleDetails(List.of("Saturday"), LocalTime.of(13, 0), LocalTime.of(16, 0)), 18, - ActivityType.ACADEMIC); + ActivityType.ACADEMIC, + DifficultyLevel.ADVANCED); scienceOlympiad.setParticipants(List.of("isabella@mergington.edu", "lucas@mergington.edu")); mongoTemplate.save(scienceOlympiad); diff --git a/src/main/java/com/mergingtonhigh/schoolmanagement/presentation/mappers/ActivityMapper.java b/src/main/java/com/mergingtonhigh/schoolmanagement/presentation/mappers/ActivityMapper.java index c15e442..496950f 100644 --- a/src/main/java/com/mergingtonhigh/schoolmanagement/presentation/mappers/ActivityMapper.java +++ b/src/main/java/com/mergingtonhigh/schoolmanagement/presentation/mappers/ActivityMapper.java @@ -4,6 +4,7 @@ import com.mergingtonhigh.schoolmanagement.application.dtos.ActivityDTO; import com.mergingtonhigh.schoolmanagement.application.dtos.ActivityTypeDTO; +import com.mergingtonhigh.schoolmanagement.application.dtos.DifficultyLevelDTO; import com.mergingtonhigh.schoolmanagement.domain.entities.Activity; /** @@ -34,6 +35,13 @@ public ActivityDTO toDTO(Activity activity) { activity.getType().getTextColor()); } + DifficultyLevelDTO difficultyLevelDTO = null; + if (activity.getDifficultyLevel() != null) { + difficultyLevelDTO = new DifficultyLevelDTO( + activity.getDifficultyLevel().name().toLowerCase(), + activity.getDifficultyLevel().getLabel()); + } + return new ActivityDTO( activity.getName(), activity.getDescription(), @@ -42,6 +50,7 @@ public ActivityDTO toDTO(Activity activity) { activity.getMaxParticipants(), activity.getParticipants(), activity.getCurrentParticipantCount(), - typeDTO); + typeDTO, + difficultyLevelDTO); } } \ No newline at end of file diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index 45a1085..d5b0490 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -14,6 +14,7 @@ document.addEventListener("DOMContentLoaded", () => { const categoryFilters = document.querySelectorAll(".category-filter"); const dayFilters = document.querySelectorAll(".day-filter"); const timeFilters = document.querySelectorAll(".time-filter"); + const difficultyFilters = document.querySelectorAll(".difficulty-filter"); // Authentication elements const loginButton = document.getElementById("login-button"); @@ -33,6 +34,7 @@ document.addEventListener("DOMContentLoaded", () => { let searchQuery = ""; let currentDay = ""; let currentTimeRange = ""; + let currentDifficulty = ""; // Authentication state let currentUser = null; @@ -57,6 +59,12 @@ document.addEventListener("DOMContentLoaded", () => { if (activeTimeFilter) { currentTimeRange = activeTimeFilter.dataset.time; } + + // Initialize difficulty filter + const activeDifficultyFilter = document.querySelector(".difficulty-filter.active"); + if (activeDifficultyFilter) { + currentDifficulty = activeDifficultyFilter.dataset.difficulty; + } } // Function to set day filter @@ -91,6 +99,22 @@ document.addEventListener("DOMContentLoaded", () => { fetchActivities(); } + // Function to set difficulty filter + function setDifficultyFilter(difficulty) { + currentDifficulty = difficulty; + + // Update active class + difficultyFilters.forEach((btn) => { + if (btn.dataset.difficulty === difficulty) { + btn.classList.add("active"); + } else { + btn.classList.remove("active"); + } + }); + + fetchActivities(); + } + // Check if user is already logged in (from localStorage) function checkAuthentication() { const savedUser = localStorage.getItem("currentUser"); @@ -386,6 +410,23 @@ document.addEventListener("DOMContentLoaded", () => { return; } + // Aplicar filtro de dificuldade + if (currentDifficulty !== "") { + const activityDifficulty = details.difficultyLevel ? details.difficultyLevel.name : null; + + if (currentDifficulty === "none") { + // "Todos os Níveis" significa apenas atividades sem dificuldade especificada + if (activityDifficulty !== null) { + return; + } + } else { + // Filtrar por nível específico (beginner, intermediate, advanced) + if (activityDifficulty !== currentDifficulty) { + return; + } + } + } + // Atividade passou por todos os filtros, adicionar à lista filtrada filteredActivities[name] = details; }); @@ -579,6 +620,19 @@ document.addEventListener("DOMContentLoaded", () => { }); }); + // Add event listeners for difficulty filter buttons + difficultyFilters.forEach((button) => { + button.addEventListener("click", () => { + // Update active class + difficultyFilters.forEach((btn) => btn.classList.remove("active")); + button.classList.add("active"); + + // Update current difficulty filter and display filtered activities + currentDifficulty = button.dataset.difficulty; + displayFilteredActivities(); + }); + }); + // Open registration modal function openRegistrationModal(activityName) { modalActivityName.textContent = activityName; @@ -797,6 +851,7 @@ document.addEventListener("DOMContentLoaded", () => { window.activityFilters = { setDayFilter, setTimeRangeFilter, + setDifficultyFilter, }; // Initialize app diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 1c5f6bc..72fceca 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -99,6 +99,28 @@

Filtrar Atividades

+ + +
+
Filtrar por dificuldade:
+
+ + + + + +
+
diff --git a/src/main/resources/static/styles.css b/src/main/resources/static/styles.css index 83361b1..1273733 100644 --- a/src/main/resources/static/styles.css +++ b/src/main/resources/static/styles.css @@ -539,7 +539,8 @@ footer { .category-filters, .day-filters, -.time-filters { +.time-filters, +.difficulty-filters { display: flex; flex-wrap: wrap; gap: 6px; @@ -547,7 +548,8 @@ footer { .category-filter, .day-filter, -.time-filter { +.time-filter, +.difficulty-filter { background-color: var(--background); border: 1px solid var(--border); color: var(--text-primary); @@ -560,7 +562,8 @@ footer { .category-filter.active, .day-filter.active, -.time-filter.active { +.time-filter.active, +.difficulty-filter.active { background-color: var(--primary); color: white; border-color: var(--primary-dark); @@ -568,7 +571,8 @@ footer { .category-filter:hover, .day-filter:hover, -.time-filter:hover { +.time-filter:hover, +.difficulty-filter:hover { background-color: var(--primary-light); color: white; } diff --git a/src/test/java/com/mergingtonhigh/schoolmanagement/domain/entities/ActivityTest.java b/src/test/java/com/mergingtonhigh/schoolmanagement/domain/entities/ActivityTest.java index 23f001e..c3b6232 100644 --- a/src/test/java/com/mergingtonhigh/schoolmanagement/domain/entities/ActivityTest.java +++ b/src/test/java/com/mergingtonhigh/schoolmanagement/domain/entities/ActivityTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.ActivityType; +import com.mergingtonhigh.schoolmanagement.domain.valueobjects.DifficultyLevel; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.Email; import com.mergingtonhigh.schoolmanagement.domain.valueobjects.ScheduleDetails; @@ -107,6 +108,65 @@ void shouldThrowExceptionWhenRemovingNonExistentParticipant() { assertThrows(IllegalArgumentException.class, () -> activity.removeParticipant(studentEmail)); } + @Test + void shouldCreateActivityWithDifficultyLevel() { + // Arrange + ScheduleDetails schedule = new ScheduleDetails( + List.of("Monday", "Wednesday"), + LocalTime.of(15, 30), + LocalTime.of(17, 0)); + + // Act + Activity activity = new Activity( + "Programação Avançada", + "Curso de programação para estudantes experientes", + "Seg/Qua 15:30-17:00", + schedule, + 8, + ActivityType.TECHNOLOGY, + DifficultyLevel.ADVANCED); + + // Assert + assertEquals("Programação Avançada", activity.getName()); + assertEquals(DifficultyLevel.ADVANCED, activity.getDifficultyLevel()); + assertEquals("Avançado", activity.getDifficultyLevel().getLabel()); + } + + @Test + void shouldCreateActivityWithoutDifficultyLevel() { + // Arrange + ScheduleDetails schedule = new ScheduleDetails( + List.of("Monday"), + LocalTime.of(15, 30), + LocalTime.of(17, 0)); + + // Act + Activity activity = new Activity( + "Atividade Geral", + "Atividade para todos os níveis", + "Seg 15:30-17:00", + schedule, + 12, + ActivityType.ACADEMIC); + + // Assert + assertEquals("Atividade Geral", activity.getName()); + assertEquals(null, activity.getDifficultyLevel()); + } + + @Test + void shouldSetDifficultyLevel() { + // Arrange + Activity activity = createTestActivity(); + + // Act + activity.setDifficultyLevel(DifficultyLevel.INTERMEDIATE); + + // Assert + assertEquals(DifficultyLevel.INTERMEDIATE, activity.getDifficultyLevel()); + assertEquals("Intermediário", activity.getDifficultyLevel().getLabel()); + } + private Activity createTestActivity() { ScheduleDetails schedule = new ScheduleDetails( List.of("Monday"),