df5d1a9b9b
CI / build-and-test (pull_request) Successful in 2m46s
The reporting period was baked into the data service, so nothing could change what the dashboard shows without editing code. - DashboardFilter (period + optional region) parameterises every ChartDataService query; the no-arg overloads are the default filter. - DashboardContext is the bus between the new DashboardFilterBar and the widgets: charts subscribe in WidgetRegistry, KPI tiles in DashboardView. - Updates go through updateData, so widgets patch in place instead of being rebuilt; SparklineChart gained the same path. - KpiData carries the number, the bundle the unit and number pattern, so a KPI value can follow the filter and stay locale-formatted. - Filter state is deliberately not persisted, unlike the grid layout. Closes #24 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0124BiJikbhbiEsNfJxdWM69
64 lines
2.3 KiB
Java
64 lines
2.3 KiB
Java
package com.example.widgets;
|
|
|
|
import com.example.data.ChartDataService;
|
|
import com.example.data.DashboardFilter;
|
|
import com.vaadin.flow.shared.Registration;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.function.Consumer;
|
|
|
|
/**
|
|
* The dashboard's current {@link DashboardFilter} plus the widgets listening to
|
|
* it — the small event bus between the filter bar (which sets) and the widgets
|
|
* (which subscribe and re-request their data from
|
|
* {@link ChartDataService}). Neither side knows the other.
|
|
* <p>
|
|
* One context per dashboard instance, deliberately not a Spring bean: it is
|
|
* view state, so nothing leaks between users, tabs or visits. For the same
|
|
* reason the filter is <em>not persisted</em> — a reload starts from
|
|
* {@link DashboardFilter#defaults()}, unlike the grid layout, which
|
|
* {@code GridStackLayout} keeps in {@code localStorage}.
|
|
*/
|
|
public class DashboardContext {
|
|
|
|
private final List<Consumer<DashboardFilter>> listeners = new ArrayList<>();
|
|
private DashboardFilter filter = DashboardFilter.defaults();
|
|
|
|
public DashboardFilter getFilter() {
|
|
return filter;
|
|
}
|
|
|
|
/**
|
|
* Applies a filter and hands it to every listener. A filter equal to the
|
|
* current one changes nothing and notifies nobody, so re-picking the same
|
|
* value in the filter bar does not make the widgets re-fetch.
|
|
*/
|
|
public void setFilter(DashboardFilter filter) {
|
|
Objects.requireNonNull(filter, "filter");
|
|
if (this.filter.equals(filter)) {
|
|
return;
|
|
}
|
|
this.filter = filter;
|
|
// Over a copy: a listener may unsubscribe while being notified (a
|
|
// widget closing in reaction to the update).
|
|
List.copyOf(listeners).forEach(listener -> listener.accept(filter));
|
|
}
|
|
|
|
/**
|
|
* Subscribes to later filter changes. The listener is not called with the
|
|
* current filter — a widget is built from it already — so a subscriber
|
|
* feeds itself once and then only reacts.
|
|
*/
|
|
public Registration addFilterChangeListener(Consumer<DashboardFilter> listener) {
|
|
listeners.add(listener);
|
|
return () -> listeners.remove(listener);
|
|
}
|
|
|
|
/** How many widgets are currently listening; for tests and diagnostics. */
|
|
public int getListenerCount() {
|
|
return listeners.size();
|
|
}
|
|
}
|