feat(frontend): section recommandations sur la vue détail d'un site
La vue détail pose app-recommendation-list restreint au site consulté (les alertes sont demandées avec site_id) et renvoie vers la vue complète préfiltrée sur ce site. Le spec fournit les deux services du composant enfant et vérifie le filtre transmis.
This commit is contained in:
@@ -81,5 +81,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
<section class="recommendations-section">
|
||||
<h2>Recommandations</h2>
|
||||
<app-recommendation-list [siteId]="siteId() ?? null" [sites]="siteAsList()" />
|
||||
<a
|
||||
routerLink="/recommendations"
|
||||
[queryParams]="{ site: siteId() }"
|
||||
class="ev-link recommendations-section__link"
|
||||
>Voir dans la vue recommandations</a
|
||||
>
|
||||
</section>
|
||||
|
||||
<a routerLink="/sites" class="ev-link">Retour aux sites</a>
|
||||
</div>
|
||||
|
||||
@@ -117,3 +117,12 @@ h2 {
|
||||
.chart-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.recommendations-section {
|
||||
margin: 2.5rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.recommendations-section__link {
|
||||
display: inline-block;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { BehaviorSubject, of, throwError } from 'rxjs';
|
||||
import { SiteDetail } from './site-detail';
|
||||
import { SitesService } from '../../../core/services/sites.service';
|
||||
import { ReadingsService } from '../../../core/services/readings.service';
|
||||
import { AlertsService } from '../../../core/services/alerts.service';
|
||||
import { RecommendationsService } from '../../../core/services/recommendations.service';
|
||||
|
||||
const SITE = {
|
||||
site_id: 'SITE001',
|
||||
@@ -69,6 +71,7 @@ function setup(
|
||||
readingsMock: Partial<ReadingsService>,
|
||||
) {
|
||||
const paramMap = new BehaviorSubject(convertToParamMap({ siteId }));
|
||||
const getAlerts = vi.fn().mockReturnValue(of([]));
|
||||
TestBed.configureTestingModule({
|
||||
imports: [SiteDetail],
|
||||
providers: [
|
||||
@@ -76,9 +79,14 @@ function setup(
|
||||
{ provide: ActivatedRoute, useValue: { paramMap } },
|
||||
{ provide: SitesService, useValue: sitesMock },
|
||||
{ provide: ReadingsService, useValue: readingsMock },
|
||||
{ provide: AlertsService, useValue: { getAlerts } },
|
||||
{
|
||||
provide: RecommendationsService,
|
||||
useValue: { getRecommendations: vi.fn().mockReturnValue(of([])) },
|
||||
},
|
||||
],
|
||||
});
|
||||
return { fixture: TestBed.createComponent(SiteDetail), paramMap };
|
||||
return { fixture: TestBed.createComponent(SiteDetail), paramMap, getAlerts };
|
||||
}
|
||||
|
||||
describe('SiteDetail', () => {
|
||||
@@ -261,6 +269,27 @@ describe('SiteDetail', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('demande les recommandations du site consulté à travers ses alertes', () => {
|
||||
const { fixture, getAlerts } = setup(
|
||||
'SITE001',
|
||||
{
|
||||
getSite: vi.fn().mockReturnValue(of(SITE)),
|
||||
getCurrent: vi.fn().mockReturnValue(of(CURRENT_COMPLET)),
|
||||
},
|
||||
{ getHistory: vi.fn().mockReturnValue(of([])) },
|
||||
);
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getAlerts).toHaveBeenCalledWith({ site_id: 'SITE001' });
|
||||
expect(fixture.nativeElement.querySelector('app-recommendation-list')).not.toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain('Recommandations');
|
||||
expect(
|
||||
fixture.nativeElement.querySelector('a[href="/recommendations?site=SITE001"]'),
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it("annonce l'absence de mesure sans interroger l'historique quand timestamp est null", () => {
|
||||
const getHistory = vi.fn().mockReturnValue(of([]));
|
||||
const { fixture } = setup(
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Badge, BadgeTone } from '../../../shared/components/ui/badge/badge';
|
||||
import { Brand } from '../../../shared/components/ui/brand/brand';
|
||||
import { ConsumptionGauge } from '../../../shared/components/consumption-gauge/consumption-gauge';
|
||||
import { ReadingHistoryChart } from '../../../shared/components/reading-history-chart/reading-history-chart';
|
||||
import { RecommendationList } from '../../../shared/components/recommendation-list/recommendation-list';
|
||||
|
||||
const UNAVAILABLE_MESSAGE = 'Détail du site indisponible, réessayez plus tard.';
|
||||
const NO_MEASUREMENT_MESSAGE = 'Aucune mesure remontée pour ce site.';
|
||||
@@ -96,7 +97,16 @@ export interface MetricView {
|
||||
@Component({
|
||||
selector: 'app-site-detail',
|
||||
standalone: true,
|
||||
imports: [RouterLink, Card, Alert, Badge, Brand, ConsumptionGauge, ReadingHistoryChart],
|
||||
imports: [
|
||||
RouterLink,
|
||||
Card,
|
||||
Alert,
|
||||
Badge,
|
||||
Brand,
|
||||
ConsumptionGauge,
|
||||
ReadingHistoryChart,
|
||||
RecommendationList,
|
||||
],
|
||||
templateUrl: './site-detail.html',
|
||||
styleUrl: './site-detail.scss',
|
||||
})
|
||||
@@ -117,6 +127,11 @@ export class SiteDetail {
|
||||
|
||||
hasMeasurement = computed(() => this.current()?.timestamp != null);
|
||||
|
||||
siteAsList = computed<Site[]>(() => {
|
||||
const site = this.site();
|
||||
return site ? [site] : [];
|
||||
});
|
||||
|
||||
consumptionKw = computed(() => this.current()?.consumption_kw ?? null);
|
||||
|
||||
consumptionLabel = computed(() => {
|
||||
|
||||
Reference in New Issue
Block a user