Skip to content

Commit 2eab2dd

Browse files
authored
perf(dashboards): defer widget rendering from SSR to client-side (#245)
Wrap dashboard widget components in @defer blocks with skeleton placeholders to prevent SSR from triggering 25+ API calls. Above-fold widgets use `on idle`, below-fold use `on viewport`. LFXV2-1116 Signed-off-by: Asitha de Silva <asithade@gmail.com>
1 parent 91a8bd6 commit 2eab2dd

File tree

6 files changed

+264
-18
lines changed

6 files changed

+264
-18
lines changed

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,115 @@ <h1>{{ selectedFoundation()?.name }} Overview</h1>
1212
<!-- Dashboard Sections -->
1313
<div class="flex flex-col gap-6" data-testid="dashboard-sections-grid">
1414
<!-- Foundation Health - Full Width -->
15-
<lfx-foundation-health />
15+
@defer (on idle) {
16+
<lfx-foundation-health />
17+
} @placeholder {
18+
<section data-testid="foundation-health-placeholder">
19+
<div class="flex flex-wrap gap-3 items-end justify-between mb-4">
20+
<div class="flex flex-col gap-3">
21+
<p-skeleton width="12rem" height="1.5rem" />
22+
<p-skeleton width="16rem" height="1.5rem" />
23+
</div>
24+
<div class="flex items-center gap-2">
25+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
26+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
27+
</div>
28+
</div>
29+
<div class="flex gap-4 overflow-hidden">
30+
@for (i of [1, 2, 3, 4]; track i) {
31+
<div class="p-4 bg-white border border-gray-200 rounded-lg flex-shrink-0 w-[calc(100vw-3rem)] md:w-80">
32+
<div class="flex flex-col gap-2">
33+
<div class="flex items-center gap-2">
34+
<p-skeleton width="1rem" height="1rem" borderRadius="4px" />
35+
<p-skeleton width="6rem" height="0.875rem" />
36+
</div>
37+
<div class="mt-3 w-full h-16">
38+
<p-skeleton width="100%" height="100%" borderRadius="8px" />
39+
</div>
40+
<div class="flex flex-col gap-1 mt-auto">
41+
<p-skeleton width="3rem" height="1.75rem" />
42+
<p-skeleton width="70%" height="0.75rem" />
43+
</div>
44+
</div>
45+
</div>
46+
}
47+
</div>
48+
</section>
49+
}
1650

1751
<!-- Middle Row - Two Cards -->
1852
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1953
<!-- My Meetings -->
20-
<lfx-my-meetings class="h-full" />
54+
@defer (on idle) {
55+
<lfx-my-meetings class="h-full" />
56+
} @placeholder {
57+
<section class="flex flex-col flex-1" data-testid="dashboard-my-meetings-placeholder">
58+
<div class="flex items-center justify-between mb-4 h-8">
59+
<div class="flex items-center gap-2">
60+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
61+
<p-skeleton width="7rem" height="1rem" />
62+
</div>
63+
<p-skeleton width="4rem" height="1rem" />
64+
</div>
65+
<div class="flex flex-col gap-3">
66+
<p-skeleton width="100%" height="140px" />
67+
</div>
68+
</section>
69+
}
70+
2171
<!-- Pending Actions -->
22-
<lfx-pending-actions class="h-full" [pendingActions]="boardMemberActions()" (actionClick)="handleActionClick()" />
72+
@defer (on idle) {
73+
<lfx-pending-actions class="h-full" [pendingActions]="boardMemberActions()" (actionClick)="handleActionClick()" />
74+
} @placeholder {
75+
<section class="flex flex-col flex-1" data-testid="dashboard-pending-actions-placeholder">
76+
<div class="flex items-center justify-between mb-4 px-2 h-8">
77+
<div class="flex items-center gap-2">
78+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
79+
<p-skeleton width="9rem" height="1rem" />
80+
</div>
81+
</div>
82+
<div class="flex flex-col gap-3 px-2">
83+
<p-skeleton width="100%" height="140px" />
84+
</div>
85+
</section>
86+
}
2387
</div>
2488

25-
<!-- Organization Involvement - Full Width -->
26-
<lfx-organization-involvement />
89+
<!-- Organization Involvement - Full Width (below fold) -->
90+
@defer (on viewport) {
91+
<lfx-organization-involvement />
92+
} @placeholder {
93+
<section data-testid="organization-involvement-placeholder">
94+
<div class="flex flex-wrap gap-3 items-end justify-between mb-4">
95+
<div class="flex flex-col gap-3">
96+
<p-skeleton width="14rem" height="1.5rem" />
97+
<p-skeleton width="16rem" height="1.5rem" />
98+
</div>
99+
<div class="flex items-center gap-2">
100+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
101+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
102+
</div>
103+
</div>
104+
<div class="flex gap-4 overflow-hidden">
105+
@for (i of [1, 2, 3]; track i) {
106+
<div class="p-4 bg-white border border-gray-200 rounded-lg flex-shrink-0 w-[calc(100vw-3rem)] md:w-80">
107+
<div class="flex flex-col gap-2">
108+
<div class="flex items-center gap-2">
109+
<p-skeleton width="1rem" height="1rem" borderRadius="4px" />
110+
<p-skeleton width="6rem" height="0.875rem" />
111+
</div>
112+
<div class="mt-3 w-full h-16">
113+
<p-skeleton width="100%" height="100%" borderRadius="8px" />
114+
</div>
115+
<div class="flex flex-col gap-1 mt-auto">
116+
<p-skeleton width="3rem" height="1.75rem" />
117+
<p-skeleton width="70%" height="0.75rem" />
118+
</div>
119+
</div>
120+
</div>
121+
}
122+
</div>
123+
</section>
124+
}
27125
</div>
28126
</div>

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { PendingActionItem } from '@lfx-one/shared/interfaces';
77
import { HiddenActionsService } from '@services/hidden-actions.service';
88
import { ProjectContextService } from '@services/project-context.service';
99
import { ProjectService } from '@services/project.service';
10+
import { SkeletonModule } from 'primeng/skeleton';
1011
import { BehaviorSubject, catchError, of, switchMap } from 'rxjs';
1112

1213
import { FoundationHealthComponent } from '../components/foundation-health/foundation-health.component';
@@ -16,7 +17,7 @@ import { PendingActionsComponent } from '../components/pending-actions/pending-a
1617

1718
@Component({
1819
selector: 'lfx-board-member-dashboard',
19-
imports: [OrganizationInvolvementComponent, PendingActionsComponent, MyMeetingsComponent, FoundationHealthComponent],
20+
imports: [OrganizationInvolvementComponent, PendingActionsComponent, MyMeetingsComponent, FoundationHealthComponent, SkeletonModule],
2021
templateUrl: './board-member-dashboard.component.html',
2122
styleUrl: './board-member-dashboard.component.scss',
2223
})

apps/lfx-one/src/app/modules/dashboards/core-developer/core-developer-dashboard.component.html

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,90 @@ <h1>{{ selectedFoundation()?.name }} Overview</h1>
1212
<!-- Dashboard Sections -->
1313
<div class="flex flex-col gap-6" data-testid="dashboard-sections-grid">
1414
<!-- Recent Progress - Full Width -->
15-
<lfx-recent-progress />
15+
@defer (on idle) {
16+
<lfx-recent-progress />
17+
} @placeholder {
18+
<section data-testid="dashboard-recent-progress-placeholder">
19+
<div class="flex items-center justify-between mb-4">
20+
<p-skeleton width="10rem" height="1.5rem" />
21+
<div class="flex items-center gap-2">
22+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
23+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
24+
</div>
25+
</div>
26+
<div class="flex gap-4 overflow-hidden">
27+
@for (i of [1, 2, 3, 4]; track i) {
28+
<div class="p-4 bg-white border border-gray-200 rounded-lg flex-shrink-0 w-[calc(100vw-3rem)] md:w-80">
29+
<div class="flex flex-col gap-2">
30+
<div class="flex items-center gap-2">
31+
<p-skeleton width="1rem" height="1rem" borderRadius="4px" />
32+
<p-skeleton width="6rem" height="0.875rem" />
33+
</div>
34+
<div class="mt-3 w-full h-16">
35+
<p-skeleton width="100%" height="100%" borderRadius="8px" />
36+
</div>
37+
<div class="flex flex-col gap-1 mt-auto">
38+
<p-skeleton width="3rem" height="1.75rem" />
39+
<p-skeleton width="70%" height="0.75rem" />
40+
</div>
41+
</div>
42+
</div>
43+
}
44+
</div>
45+
</section>
46+
}
1647

1748
<!-- Middle Row - Two Cards -->
1849
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1950
<!-- My Meetings -->
20-
<lfx-my-meetings class="h-full" />
51+
@defer (on idle) {
52+
<lfx-my-meetings class="h-full" />
53+
} @placeholder {
54+
<section class="flex flex-col flex-1" data-testid="dashboard-my-meetings-placeholder">
55+
<div class="flex items-center justify-between mb-4 h-8">
56+
<div class="flex items-center gap-2">
57+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
58+
<p-skeleton width="7rem" height="1rem" />
59+
</div>
60+
<p-skeleton width="4rem" height="1rem" />
61+
</div>
62+
<div class="flex flex-col gap-3">
63+
<p-skeleton width="100%" height="140px" />
64+
</div>
65+
</section>
66+
}
2167

2268
<!-- Pending Actions -->
23-
<lfx-pending-actions class="h-full" [pendingActions]="coreDevActions()" (actionClick)="handleActionClick()" />
69+
@defer (on idle) {
70+
<lfx-pending-actions class="h-full" [pendingActions]="coreDevActions()" (actionClick)="handleActionClick()" />
71+
} @placeholder {
72+
<section class="flex flex-col flex-1" data-testid="dashboard-pending-actions-placeholder">
73+
<div class="flex items-center justify-between mb-4 px-2 h-8">
74+
<div class="flex items-center gap-2">
75+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
76+
<p-skeleton width="9rem" height="1rem" />
77+
</div>
78+
</div>
79+
<div class="flex flex-col gap-3 px-2">
80+
<p-skeleton width="100%" height="140px" />
81+
</div>
82+
</section>
83+
}
2484
</div>
2585

26-
<!-- My Projects - Full Width -->
27-
<lfx-my-projects />
86+
<!-- My Projects - Full Width (below fold) -->
87+
@defer (on viewport) {
88+
<lfx-my-projects />
89+
} @placeholder {
90+
<div data-testid="dashboard-my-projects-placeholder">
91+
<div class="flex items-center justify-between mb-4">
92+
<div class="flex items-center gap-2">
93+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
94+
<p-skeleton width="6rem" height="1rem" />
95+
</div>
96+
</div>
97+
<p-skeleton width="100%" height="200px" borderRadius="8px" />
98+
</div>
99+
}
28100
</div>
29101
</div>

apps/lfx-one/src/app/modules/dashboards/core-developer/core-developer-dashboard.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Component, computed, inject, signal } from '@angular/core';
55
import { CORE_DEVELOPER_ACTION_ITEMS } from '@lfx-one/shared/constants';
66
import { HiddenActionsService } from '@services/hidden-actions.service';
77
import { ProjectContextService } from '@services/project-context.service';
8+
import { SkeletonModule } from 'primeng/skeleton';
89
import { BehaviorSubject } from 'rxjs';
910

1011
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
@@ -14,7 +15,7 @@ import { RecentProgressComponent } from '../components/recent-progress/recent-pr
1415

1516
@Component({
1617
selector: 'lfx-core-developer-dashboard',
17-
imports: [RecentProgressComponent, PendingActionsComponent, MyMeetingsComponent, MyProjectsComponent],
18+
imports: [RecentProgressComponent, PendingActionsComponent, MyMeetingsComponent, MyProjectsComponent, SkeletonModule],
1819
templateUrl: './core-developer-dashboard.component.html',
1920
styleUrl: './core-developer-dashboard.component.scss',
2021
})

apps/lfx-one/src/app/modules/dashboards/maintainer/maintainer-dashboard.component.html

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,90 @@ <h1>{{ selectedProject()?.name }} Overview</h1>
1212
<!-- Dashboard Sections -->
1313
<div class="flex flex-col gap-6" data-testid="dashboard-sections-grid">
1414
<!-- Recent Progress - Full Width -->
15-
<lfx-recent-progress />
15+
@defer (on idle) {
16+
<lfx-recent-progress />
17+
} @placeholder {
18+
<section data-testid="dashboard-recent-progress-placeholder">
19+
<div class="flex items-center justify-between mb-4">
20+
<p-skeleton width="10rem" height="1.5rem" />
21+
<div class="flex items-center gap-2">
22+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
23+
<p-skeleton width="2rem" height="2rem" borderRadius="4px" />
24+
</div>
25+
</div>
26+
<div class="flex gap-4 overflow-hidden">
27+
@for (i of [1, 2, 3, 4]; track i) {
28+
<div class="p-4 bg-white border border-gray-200 rounded-lg flex-shrink-0 w-[calc(100vw-3rem)] md:w-80">
29+
<div class="flex flex-col gap-2">
30+
<div class="flex items-center gap-2">
31+
<p-skeleton width="1rem" height="1rem" borderRadius="4px" />
32+
<p-skeleton width="6rem" height="0.875rem" />
33+
</div>
34+
<div class="mt-3 w-full h-16">
35+
<p-skeleton width="100%" height="100%" borderRadius="8px" />
36+
</div>
37+
<div class="flex flex-col gap-1 mt-auto">
38+
<p-skeleton width="3rem" height="1.75rem" />
39+
<p-skeleton width="70%" height="0.75rem" />
40+
</div>
41+
</div>
42+
</div>
43+
}
44+
</div>
45+
</section>
46+
}
1647

1748
<!-- Middle Row - Two Cards -->
1849
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1950
<!-- My Meetings -->
20-
<lfx-my-meetings class="h-full" />
51+
@defer (on idle) {
52+
<lfx-my-meetings class="h-full" />
53+
} @placeholder {
54+
<section class="flex flex-col flex-1" data-testid="dashboard-my-meetings-placeholder">
55+
<div class="flex items-center justify-between mb-4 h-8">
56+
<div class="flex items-center gap-2">
57+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
58+
<p-skeleton width="7rem" height="1rem" />
59+
</div>
60+
<p-skeleton width="4rem" height="1rem" />
61+
</div>
62+
<div class="flex flex-col gap-3">
63+
<p-skeleton width="100%" height="140px" />
64+
</div>
65+
</section>
66+
}
67+
2168
<!-- Pending Actions -->
22-
<lfx-pending-actions class="h-full" [pendingActions]="maintainerActions()" (actionClick)="handleActionClick()" />
69+
@defer (on idle) {
70+
<lfx-pending-actions class="h-full" [pendingActions]="maintainerActions()" (actionClick)="handleActionClick()" />
71+
} @placeholder {
72+
<section class="flex flex-col flex-1" data-testid="dashboard-pending-actions-placeholder">
73+
<div class="flex items-center justify-between mb-4 px-2 h-8">
74+
<div class="flex items-center gap-2">
75+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
76+
<p-skeleton width="9rem" height="1rem" />
77+
</div>
78+
</div>
79+
<div class="flex flex-col gap-3 px-2">
80+
<p-skeleton width="100%" height="140px" />
81+
</div>
82+
</section>
83+
}
2384
</div>
2485

25-
<!-- My Projects - Full Width -->
26-
<lfx-my-projects />
86+
<!-- My Projects - Full Width (below fold) -->
87+
@defer (on viewport) {
88+
<lfx-my-projects />
89+
} @placeholder {
90+
<div data-testid="dashboard-my-projects-placeholder">
91+
<div class="flex items-center justify-between mb-4">
92+
<div class="flex items-center gap-2">
93+
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
94+
<p-skeleton width="6rem" height="1rem" />
95+
</div>
96+
</div>
97+
<p-skeleton width="100%" height="200px" borderRadius="8px" />
98+
</div>
99+
}
27100
</div>
28101
</div>

apps/lfx-one/src/app/modules/dashboards/maintainer/maintainer-dashboard.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { Component, computed, inject, signal } from '@angular/core';
55
import { HiddenActionsService } from '@services/hidden-actions.service';
66
import { ProjectContextService } from '@services/project-context.service';
7+
import { SkeletonModule } from 'primeng/skeleton';
78
import { BehaviorSubject } from 'rxjs';
89

910
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
@@ -13,7 +14,7 @@ import { RecentProgressComponent } from '../components/recent-progress/recent-pr
1314

1415
@Component({
1516
selector: 'lfx-maintainer-dashboard',
16-
imports: [RecentProgressComponent, PendingActionsComponent, MyMeetingsComponent, MyProjectsComponent],
17+
imports: [RecentProgressComponent, PendingActionsComponent, MyMeetingsComponent, MyProjectsComponent, SkeletonModule],
1718
templateUrl: './maintainer-dashboard.component.html',
1819
styleUrl: './maintainer-dashboard.component.scss',
1920
})

0 commit comments

Comments
 (0)