diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java index 4e38eba..d020d38 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java @@ -23,6 +23,11 @@ public interface MembershipRepository extends JpaRepository { boolean existsByEnergyCommunityId(UUID energyCommunityId); + long countByStatus(MembershipStatus status); + + @Query("SELECT COUNT(m) FROM Membership m JOIN m.meteringPoint mp WHERE mp.userId = :userId AND m.status = :status") + long countByUserIdAndStatus(@Param("userId") UUID userId, @Param("status") MembershipStatus status); + @Query("SELECT m.energyCommunity.id, COUNT(m) FROM Membership m WHERE m.energyCommunity.id IN :communityIds AND m.status != 'INACTIVE' GROUP BY m.energyCommunity.id") List countActiveMembersForCommunities(@Param("communityIds") List communityIds); diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java index 496e754..9fe941b 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java @@ -11,6 +11,8 @@ public interface MeteringPointRepository extends JpaRepository findAllByUserId(UUID userId); + long countByUserId(UUID userId); + boolean existsByAtNumber(String atNumber); Optional findByAtNumber(String atNumber); diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/dashboard/service/DashboardService.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/dashboard/service/DashboardService.java index 093b641..64eaf0c 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/dashboard/service/DashboardService.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/dashboard/service/DashboardService.java @@ -25,23 +25,17 @@ public class DashboardService { public DashboardStatsDto.AdminStats getAdminStats() { long communities = energyCommunityRepository.count(); - long pendingUsers = userRepository.findAllByStatusAndEmailVerified(RegistrationStatus.PENDING, true).size(); - - // Zählt alle aktiven Mitgliedschaften - long activeMemberships = membershipRepository.findAll().stream() - .filter(m -> m.getStatus() == MembershipStatus.ACTIVE) - .count(); + long pendingUsers = userRepository.countByStatusAndEmailVerified(RegistrationStatus.PENDING, true); + long activeMemberships = membershipRepository.countByStatus(MembershipStatus.ACTIVE); return new DashboardStatsDto.AdminStats(communities, pendingUsers, activeMemberships); } public DashboardStatsDto.UserStats getUserStats(UUID userId) { - long meteringPoints = meteringPointRepository.findAllByUserId(userId).size(); + long meteringPoints = meteringPointRepository.countByUserId(userId); + long activeMemberships = membershipRepository.countByUserIdAndStatus(userId, MembershipStatus.ACTIVE); + long pendingMemberships = membershipRepository.countByUserIdAndStatus(userId, MembershipStatus.PENDING); - // Zählt die Mitgliedschaften der Zählpunkte des Users - long activeMembers = 0; // Hier idealerweise eine angepasste Query im Repository nutzen - long pendingMembers = 0; - - return new DashboardStatsDto.UserStats(meteringPoints, activeMembers, pendingMembers); + return new DashboardStatsDto.UserStats(meteringPoints, activeMemberships, pendingMemberships); } } \ No newline at end of file diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java index f37ac10..0861240 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java @@ -14,5 +14,7 @@ public interface UserRepository extends JpaRepository { List findAllByStatusAndEmailVerified(RegistrationStatus status, Boolean verified); + long countByStatusAndEmailVerified(RegistrationStatus status, Boolean verified); + Optional findByVerificationToken(String token); } diff --git a/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/service/DashboardServiceTest.java b/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/service/DashboardServiceTest.java new file mode 100644 index 0000000..628c6c9 --- /dev/null +++ b/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/service/DashboardServiceTest.java @@ -0,0 +1,65 @@ +package at.mueller.eeg.backend.dashboard.service; + +import at.mueller.eeg.backend.community.domain.MembershipStatus; +import at.mueller.eeg.backend.community.repository.EnergyCommunityRepository; +import at.mueller.eeg.backend.community.repository.MembershipRepository; +import at.mueller.eeg.backend.community.repository.MeteringPointRepository; +import at.mueller.eeg.backend.dashboard.api.dto.DashboardStatsDto; +import at.mueller.eeg.backend.iam.domain.RegistrationStatus; +import at.mueller.eeg.backend.iam.repository.UserRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class DashboardServiceTest { + + @Mock + private EnergyCommunityRepository energyCommunityRepository; + + @Mock + private UserRepository userRepository; + + @Mock + private MembershipRepository membershipRepository; + + @Mock + private MeteringPointRepository meteringPointRepository; + + @InjectMocks + private DashboardService dashboardService; + + @Test + void getAdminStats_returnsCorrectCounts() { + when(energyCommunityRepository.count()).thenReturn(5L); + when(userRepository.countByStatusAndEmailVerified(RegistrationStatus.PENDING, true)).thenReturn(3L); + when(membershipRepository.countByStatus(MembershipStatus.ACTIVE)).thenReturn(10L); + + DashboardStatsDto.AdminStats stats = dashboardService.getAdminStats(); + + assertEquals(5, stats.totalCommunities()); + assertEquals(3, stats.pendingUsers()); + assertEquals(10, stats.totalActiveMemberships()); + } + + @Test + void getUserStats_returnsCorrectCounts() { + UUID userId = UUID.randomUUID(); + when(meteringPointRepository.countByUserId(userId)).thenReturn(2L); + when(membershipRepository.countByUserIdAndStatus(userId, MembershipStatus.ACTIVE)).thenReturn(1L); + when(membershipRepository.countByUserIdAndStatus(userId, MembershipStatus.PENDING)).thenReturn(1L); + + DashboardStatsDto.UserStats stats = dashboardService.getUserStats(userId); + + assertEquals(2, stats.totalMeteringPoints()); + assertEquals(1, stats.activeMemberships()); + assertEquals(1, stats.pendingMemberships()); + } +} diff --git a/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.html b/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.html index 34e9e74..fbe2fa6 100644 --- a/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.html +++ b/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.html @@ -1,5 +1,12 @@ @if (isLoading()) { -
Lade Daten...
+
+
+ Lade Daten... +
+} @else if (error()) { +
+

{{ error() }}

+
} @else if (role === 'ADMIN' && adminStats(); as stats) {
diff --git a/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.ts b/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.ts index 9fd5c52..c6b50fe 100644 --- a/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.ts +++ b/eeg_frontend/src/app/pages/dashboard-overview/dashboard-overview.ts @@ -15,17 +15,24 @@ export class DashboardOverview implements OnInit { adminStats = signal(null); userStats = signal(null); isLoading = signal(true); + error = signal(null); ngOnInit() { if (this.role === 'ADMIN') { this.dashboardService.getAdminStats().subscribe({ next: stats => { this.adminStats.set(stats); this.isLoading.set(false); }, - error: () => this.isLoading.set(false) + error: () => { + this.isLoading.set(false); + this.error.set('Fehler beim Laden der Statistiken.'); + } }); } else { this.dashboardService.getUserStats().subscribe({ next: stats => { this.userStats.set(stats); this.isLoading.set(false); }, - error: () => this.isLoading.set(false) + error: () => { + this.isLoading.set(false); + this.error.set('Fehler beim Laden der Statistiken.'); + } }); } }