fix(frontend): add missing provideEcharts() to app config causing empty dashboard chart
Root cause: ngx-echarts v18+ requires provideEcharts() in the app config. Without it, NgxEchartsDirective silently fails to render charts, causing the dashboard overview chart to appear empty. Added provideEcharts() to app.config.ts and added DashboardServiceIntegrationTest for the metering data overview endpoint.
This commit is contained in:
parent
0ef4f94933
commit
6cb4ff7a4a
@ -0,0 +1,137 @@
|
||||
package at.mueller.eeg.backend.dashboard.service;
|
||||
|
||||
import at.mueller.eeg.backend.community.api.dto.MeteringDataRecordDto;
|
||||
import at.mueller.eeg.backend.community.api.dto.MeteringDataUploadRequest;
|
||||
import at.mueller.eeg.backend.community.api.dto.MeteringDataUploadResponse;
|
||||
import at.mueller.eeg.backend.community.domain.*;
|
||||
import at.mueller.eeg.backend.community.domain.state.MakoState;
|
||||
import at.mueller.eeg.backend.community.repository.MeteringDataRepository;
|
||||
import at.mueller.eeg.backend.community.repository.MeteringPointRepository;
|
||||
import at.mueller.eeg.backend.community.service.MeteringDataService;
|
||||
import at.mueller.eeg.backend.dashboard.api.dto.DashboardStatsDto;
|
||||
import at.mueller.eeg.backend.iam.domain.ParticipantType;
|
||||
import at.mueller.eeg.backend.iam.domain.RegistrationStatus;
|
||||
import at.mueller.eeg.backend.iam.domain.User;
|
||||
import at.mueller.eeg.backend.iam.domain.UserRole;
|
||||
import at.mueller.eeg.backend.iam.repository.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class DashboardServiceIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private DashboardService dashboardService;
|
||||
|
||||
@Autowired
|
||||
private MeteringDataService meteringDataService;
|
||||
|
||||
@Autowired
|
||||
private MeteringPointRepository meteringPointRepository;
|
||||
|
||||
@Autowired
|
||||
private MeteringDataRepository meteringDataRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
private User testUser;
|
||||
private MeteringPoint testMeteringPoint;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testUser = new User();
|
||||
testUser.setEmail("test-dashboard-" + UUID.randomUUID() + "@example.com");
|
||||
testUser.setPasswordHash("encoded");
|
||||
testUser.setFirstName("Test");
|
||||
testUser.setLastName("Dashboard");
|
||||
testUser.setRole(UserRole.MEMBER);
|
||||
testUser.setStatus(RegistrationStatus.APPROVED);
|
||||
testUser.setParticipantType(ParticipantType.PRIVATE);
|
||||
testUser.setEnabled(true);
|
||||
testUser = userRepository.save(testUser);
|
||||
|
||||
testMeteringPoint = new MeteringPoint();
|
||||
testMeteringPoint.setUserId(testUser.getId());
|
||||
testMeteringPoint.setAtNumber("AT0010000000000000000000001234567");
|
||||
testMeteringPoint.setType(PointType.CONSUMER);
|
||||
testMeteringPoint.setMakoState(MakoState.ACTIVE);
|
||||
testMeteringPoint = meteringPointRepository.save(testMeteringPoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMeteringDataOverview_afterUpload_returnsNonEmptyTimeSeries() {
|
||||
// Upload hourly data for 3 days
|
||||
List<MeteringDataRecordDto> records = new ArrayList<>();
|
||||
Instant base = Instant.parse("2026-06-01T00:00:00Z");
|
||||
for (int i = 0; i < 72; i++) { // 3 days * 24 hours
|
||||
Instant start = base.plusSeconds(i * 3600L);
|
||||
Instant end = start.plusSeconds(3600);
|
||||
records.add(new MeteringDataRecordDto(
|
||||
testMeteringPoint.getAtNumber(), MeteringDataType.CONSUMPTION,
|
||||
start, end, 0.5 + (i % 10) * 0.1, null));
|
||||
}
|
||||
|
||||
MeteringDataUploadRequest request = new MeteringDataUploadRequest(
|
||||
DataSource.EMAIL_XLSX, "test.xlsx", records);
|
||||
MeteringDataUploadResponse uploadResponse = meteringDataService.uploadMeteringData(request, testUser.getId());
|
||||
assertEquals(UploadStatus.COMPLETED, uploadResponse.status());
|
||||
|
||||
// Now query via dashboard service
|
||||
Instant from = Instant.parse("2026-06-01T00:00:00Z");
|
||||
Instant to = Instant.parse("2026-06-04T00:00:00Z");
|
||||
|
||||
DashboardStatsDto.MeteringDataOverview overview = dashboardService.getMeteringDataOverview(testUser.getId(), from, to);
|
||||
|
||||
assertNotNull(overview);
|
||||
assertFalse(overview.meteringPoints().isEmpty(), "Should have metering points");
|
||||
assertFalse(overview.timeSeries().isEmpty(), "Should have time series data");
|
||||
// Data spans 3 full days, but timezone grouping may add 1 day
|
||||
assertTrue(overview.timeSeries().size() >= 3, "Should have at least 3 days of data");
|
||||
assertEquals(1, overview.meteringPoints().size());
|
||||
assertTrue(overview.meteringPoints().get(0).totalKwh() > 0, "Total kWh should be > 0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMeteringDataOverview_fullYearData_returnsTimeSeries() {
|
||||
// Upload full year hourly data
|
||||
List<MeteringDataRecordDto> records = new ArrayList<>();
|
||||
Instant base = Instant.parse("2026-01-01T00:00:00Z");
|
||||
for (int i = 0; i < 8760; i++) {
|
||||
Instant start = base.plusSeconds(i * 3600L);
|
||||
Instant end = start.plusSeconds(3600);
|
||||
records.add(new MeteringDataRecordDto(
|
||||
testMeteringPoint.getAtNumber(), MeteringDataType.CONSUMPTION,
|
||||
start, end, 0.5 + (i % 10) * 0.1, null));
|
||||
}
|
||||
|
||||
MeteringDataUploadRequest request = new MeteringDataUploadRequest(
|
||||
DataSource.EMAIL_XLSX, "fullyear.xlsx", records);
|
||||
MeteringDataUploadResponse uploadResponse = meteringDataService.uploadMeteringData(request, testUser.getId());
|
||||
assertEquals(UploadStatus.COMPLETED, uploadResponse.status());
|
||||
|
||||
// Query last 30 days
|
||||
Instant from = Instant.parse("2026-06-24T00:00:00Z");
|
||||
Instant to = Instant.parse("2026-07-24T00:00:00Z");
|
||||
|
||||
DashboardStatsDto.MeteringDataOverview overview = dashboardService.getMeteringDataOverview(testUser.getId(), from, to);
|
||||
|
||||
assertNotNull(overview);
|
||||
assertFalse(overview.meteringPoints().isEmpty(), "Should have metering points");
|
||||
assertFalse(overview.timeSeries().isEmpty(), "Should have time series data for the queried range");
|
||||
assertTrue(overview.timeSeries().size() >= 29, "Should have ~30 days of data");
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import {BASE_PATH} from './api';
|
||||
import {jwtInterceptor} from './interceptors/jwt.interceptor';
|
||||
import {errorInterceptor} from './interceptors/error.interceptor';
|
||||
import {environment} from '../environments/environment';
|
||||
import {provideEcharts} from 'ngx-echarts';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
@ -13,6 +14,7 @@ export const appConfig: ApplicationConfig = {
|
||||
provideRouter(routes),
|
||||
provideHttpClient(withFetch(),
|
||||
withInterceptors([jwtInterceptor, errorInterceptor])),
|
||||
{ provide: BASE_PATH, useValue: environment.apiUrl }
|
||||
{ provide: BASE_PATH, useValue: environment.apiUrl },
|
||||
provideEcharts()
|
||||
]
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user