Commit a1a0724
authored
feat(health-metrics): add per-card and full-page empty states (#642)
* feat(health-metrics): add per-card and full-page empty states
Adds standardized empty-state UI for the foundation health-metrics page:
- New `lfx-health-metrics-card-empty-state` component with icon, title,
description, optional CTA, and a bottom-stuck info banner that supports
optional inline link (used by Board Meeting card)
- New `lfx-health-metrics-full-page-empty-state` component for foundations
with no data at all
- Each of the 9 metric cards now exposes a `hasData` signal and a
`hasDataChange` output so the parent can detect "all empty" state
- Parent page renders the full-page empty state only when foundation-level
totals (totalValue/totalProjects/totalMembers) are all zero AND every
card reports no data — preserves the cards view if any year-filter
option has data so users can switch ranges
Replaces silent zero-renders and bare "No X data" text with consistent
guidance copy across NPS, Participating Orgs, Membership Churn,
Outstanding Balance, Events, Training & Certification, Code Contribution,
Flywheel Conversion, and Board Meeting Participation cards.
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
* fix(health-metrics): address PR #642 review feedback
Address review comments from copilot-pull-request-reviewer:
- health-metrics-card-empty-state, health-metrics-full-page-empty-state:
add explicit standalone: true to align with the rest of the
health-metrics card components (per copilot[bot]).
- training-certification-card.component.ts: include revenue fields in
hasData so the card does not show the empty state when only Revenue
mode has values (per copilot[bot]).
- membership-churn-tier-card.component.ts: include membersLost and
churnRatePct in hasData so churn with zero valueLost still renders
the card (per copilot[bot]).
- health-metrics.component.ts: reset cardDataStates on foundation
change so stale per-card hasData signals from a previous foundation
cannot incorrectly trigger the full-page empty state (per copilot[bot]).
Resolves 4 review threads. Two additional threads about the
foundation-totals proxy and hiding the year filter in the full-page
empty state are responded to inline (intentional design — foundation
totals are filter-independent).
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
* fix(health-metrics): address PR #642 review feedback round 2
Address review comments from copilot-pull-request-reviewer:
- health-metrics-card-empty-state.component.html: tighten the banner
wrapper condition so it only renders when there is content to show
(an action string, or both actionLinkLabel and actionLinkHref).
Previously, setting actionLinkLabel without actionLinkHref produced
an empty banner with only an icon (per copilot[bot]).
- health-metrics.component.ts: gate allCardsEmpty on every card having
reported its hasData state. Cards now have to be present in
cardDataStates (not just === false) before emptiness is evaluated,
which removes the flicker where the page rendered the cards first
and then snapped to the full-page empty state once children emitted
hasDataChange (per copilot[bot]).
Also: switch the per-card empty-state banner from blue tones to gray
tones (bg-gray-50 / border-gray-200 / text-gray-500/700/900) per
design feedback.
Resolves 2 review threads.
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
* fix(health-metrics): address PR #642 review feedback round 3
Address review comments from copilot-pull-request-reviewer:
- health-metrics-card-empty-state.component.ts: remove RouterLink from
imports. lfx-button already exposes its own routerLink input and
handles navigation internally — importing RouterLink at this level
attaches the directive to the host <lfx-button> element and causes
duplicated routing behavior (per copilot[bot]).
- health-metrics-card-empty-state.component.ts: widen ctaRoute type to
string | string[] | undefined to match lfx-button's routerLink input
contract and the rest of the app, which routinely passes string
paths (per copilot[bot]).
Resolves 2 review threads.
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
* fix(review): address PR #642 review feedback (round 4)
Address review comments from MRashad26, coderabbitai[bot]:
- 9 health-metrics card components: replace effect() with toObservable()
+ RxJS pipe per project rules (per MRashad26 thread BWtea)
- health-metrics.component.ts: collapse multi-line comments to single
lines for readability (per MRashad26 threads BWteg, BWtey)
- Re-run yarn format to satisfy CI format check (per coderabbitai[bot]
threads BWjQy, BWjQ5)
Resolves 5 review threads.
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
* fix(review): address PR #642 review feedback (round 5)
Address review comment from MRashad26:
- 9 health-metrics card components: move public output() declaration
to sit immediately after public input() declarations, per
component-organization.md (public fields → signals → computed)
Resolves 1 review thread.
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
* fix(review): address PR #642 review feedback
Address review comments from MRashad26:
- health-metrics-card-empty-state.component.ts: simplified input declarations
to plain input<T>() with consistent ordering (per MRashad26)
- board-meeting-card.component.ts: changed addPastMeetingUrl to return
string | null directly so template binding can pass it through without
the `|| undefined` workaround (per MRashad26)
- board-meeting-card.component.html: removed `|| undefined` from
[actionLinkHref] binding (per MRashad26)
- packages/shared/src/interfaces/dashboard-metric.interface.ts: added
HealthMetricCardName type-safe union for the parent page's per-card
state tracking (per MRashad26)
- health-metrics.component.ts: typed cardDataStates as
Partial<Record<HealthMetricCardName, boolean>>, cardNames as
readonly HealthMetricCardName[], and updateCardDataState param as
HealthMetricCardName (per MRashad26)
- shared/utils/health-metrics-data.util.ts: extracted emitHasDataOnLoad
util to centralize the loading->hasData->emit pattern across cards
(per MRashad26)
- 9 card components (board-meeting, code-contribution, events, nps,
membership-churn-tier, outstanding-balance, participating-orgs,
training-certification, flywheel-conversion): replaced inline
toObservable/filter/map/takeUntilDestroyed block in constructor with
emitHasDataOnLoad util call; cleaned up unused rxjs/rxjs-interop
imports (per MRashad26)
Resolves 4 review threads.
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>
---------
Signed-off-by: Nuno Eufrasio <nmeufrasio@gmail.com>1 parent 87e319a commit a1a0724
26 files changed
Lines changed: 596 additions & 228 deletions
File tree
- apps/lfx-one/src/app
- modules/dashboards/health-metrics
- board-meeting-card
- code-contribution-card
- events-card
- flywheel-conversion-card
- health-metrics-card-empty-state
- health-metrics-full-page-empty-state
- membership-churn-tier-card
- nps-card
- outstanding-balance-card
- participating-orgs-card
- training-certification-card
Lines changed: 10 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
44 | 49 | | |
45 | 50 | | |
46 | 51 | | |
| |||
Lines changed: 8 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| |||
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
32 | | - | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
| 45 | + | |
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
| |||
54 | 56 | | |
55 | 57 | | |
56 | 58 | | |
57 | | - | |
| 59 | + | |
58 | 60 | | |
59 | | - | |
60 | | - | |
| 61 | + | |
61 | 62 | | |
62 | 63 | | |
63 | 64 | | |
| |||
146 | 147 | | |
147 | 148 | | |
148 | 149 | | |
| 150 | + | |
149 | 151 | | |
150 | 152 | | |
151 | 153 | | |
| |||
Lines changed: 17 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
48 | 50 | | |
49 | 51 | | |
50 | 52 | | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
55 | 60 | | |
56 | 61 | | |
57 | 62 | | |
| |||
Lines changed: 7 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | | - | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | | - | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| 33 | + | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
36 | 38 | | |
| 39 | + | |
37 | 40 | | |
38 | 41 | | |
39 | 42 | | |
| |||
126 | 129 | | |
127 | 130 | | |
128 | 131 | | |
| 132 | + | |
129 | 133 | | |
130 | 134 | | |
131 | 135 | | |
| |||
Lines changed: 18 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
41 | 43 | | |
42 | 44 | | |
43 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
44 | 54 | | |
45 | 55 | | |
46 | 56 | | |
| |||
Lines changed: 8 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | | - | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | | - | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| 32 | + | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
| 37 | + | |
| 38 | + | |
35 | 39 | | |
36 | 40 | | |
37 | 41 | | |
| |||
99 | 103 | | |
100 | 104 | | |
101 | 105 | | |
| 106 | + | |
102 | 107 | | |
103 | 108 | | |
104 | 109 | | |
| |||
Lines changed: 15 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
28 | 30 | | |
29 | 31 | | |
30 | 32 | | |
| |||
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
51 | 56 | | |
52 | 57 | | |
53 | 58 | | |
| |||
Lines changed: 11 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
| 22 | + | |
21 | 23 | | |
22 | 24 | | |
23 | 25 | | |
| |||
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
36 | | - | |
| 38 | + | |
37 | 39 | | |
38 | 40 | | |
39 | 41 | | |
| |||
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
| 47 | + | |
45 | 48 | | |
46 | 49 | | |
47 | | - | |
| 50 | + | |
48 | 51 | | |
49 | 52 | | |
| 53 | + | |
50 | 54 | | |
51 | 55 | | |
52 | 56 | | |
| |||
150 | 154 | | |
151 | 155 | | |
152 | 156 | | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
153 | 161 | | |
154 | 162 | | |
155 | 163 | | |
| |||
Lines changed: 34 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
0 commit comments