Skip to content

Commit 64c1e37

Browse files
committed
first batch of polishing touches
1 parent 4e065d8 commit 64c1e37

21 files changed

+317
-101
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<!-- Foundation Project -->
66
@if (selectedFoundation()) {
77
<div class="mb-6 flex items-center gap-4" data-testid="foundation-project">
8-
<h1 class="text-2xl font-serif font-semibold text-gray-900">{{ selectedFoundation()?.name }} Overview</h1>
8+
<h1>{{ selectedFoundation()?.name }} Overview</h1>
99
</div>
1010
}
1111

apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<section data-testid="foundation-health-section">
55
<div class="flex items-center justify-between mb-4">
66
<div class="flex flex-col md:flex-row md:items-center gap-3">
7-
<h2 class="font-['Roboto_Slab'] font-semibold text-[16px]">{{ title() }}</h2>
7+
<h2>{{ title() }}</h2>
88

99
<!-- Filter Pills -->
1010
<lfx-filter-pills
@@ -36,15 +36,25 @@ <h2 class="font-['Roboto_Slab'] font-semibold text-[16px]">{{ title() }}</h2>
3636
</div>
3737

3838
<!-- Carousel Container -->
39-
<div class="overflow-hidden">
40-
<div #carouselScroll class="flex gap-4 overflow-x-auto pb-2 hide-scrollbar scroll-smooth" data-testid="foundation-health-carousel">
39+
<div class="relative overflow-hidden">
40+
<!-- Left Fade Gradient -->
41+
@if (canScrollLeft()) {
42+
<div class="hidden md:block absolute top-0 bottom-0 left-0 w-24 pointer-events-none z-10" style="background: linear-gradient(to right, #f8f8f9 0%, transparent 100%);"></div>
43+
}
44+
45+
<!-- Right Fade Gradient -->
46+
@if (canScrollRight()) {
47+
<div class="hidden md:block absolute top-0 bottom-0 right-0 w-24 pointer-events-none z-10" style="background: linear-gradient(to left, #f8f8f9 0%, transparent 100%);"></div>
48+
}
49+
50+
<div #carouselScroll class="flex gap-4 overflow-x-auto pb-2 hide-scrollbar scroll-smooth" data-testid="foundation-health-carousel" (scroll)="onScroll()">
4151
@for (card of metricCards(); track card.title) {
42-
<div class="p-4 bg-white rounded-lg border border-slate-200 flex-shrink-0 w-[calc(100vw-3rem)] md:w-80" [attr.data-testid]="card.testId">
52+
<div class="p-4 bg-white rounded-lg flex-shrink-0 w-[calc(100vw-3rem)] md:w-80 shadow-[0px_1px_2px_-1px_rgba(0,0,0,0.10),0px_1px_3px_0px_rgba(0,0,0,0.10)]" [attr.data-testid]="card.testId">
4353
<div class="flex flex-col h-full justify-between">
4454
<!-- Card Header -->
4555
<div class="flex items-center gap-2">
4656
<i [class]="card.icon + ' w-4 h-4 text-muted-foreground'"></i>
47-
<h5 class="text-sm font-medium">{{ card.title }}</h5>
57+
<h4>{{ card.title }}</h4>
4858
</div>
4959

5060
<!-- Custom Content -->

apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: MIT
33

44
import { CommonModule } from '@angular/common';
5-
import { Component, computed, ElementRef, signal, ViewChild, input } from '@angular/core';
5+
import { AfterViewInit, Component, computed, ElementRef, signal, ViewChild, input } from '@angular/core';
66
import { ChartComponent } from '@components/chart/chart.component';
77
import { FilterOption, FilterPillsComponent } from '@components/filter-pills/filter-pills.component';
88
import { AGGREGATE_FOUNDATION_METRICS, FOUNDATION_SPARKLINE_CHART_OPTIONS, FOUNDATION_BAR_CHART_OPTIONS } from '@lfx-one/shared/constants';
@@ -16,11 +16,14 @@ import { hexToRgba } from '@lfx-one/shared/utils';
1616
templateUrl: './foundation-health.component.html',
1717
styleUrl: './foundation-health.component.scss',
1818
})
19-
export class FoundationHealthComponent {
19+
export class FoundationHealthComponent implements AfterViewInit {
2020
@ViewChild('carouselScroll') public carouselScrollContainer!: ElementRef;
2121

2222
public readonly title = input<string>('Foundation Health');
2323

24+
public readonly canScrollLeft = signal<boolean>(false);
25+
public readonly canScrollRight = signal<boolean>(false);
26+
2427
public readonly selectedFilter = signal<string>('all');
2528

2629
public readonly filterOptions: FilterOption[] = [
@@ -34,6 +37,11 @@ export class FoundationHealthComponent {
3437

3538
public readonly barChartOptions = FOUNDATION_BAR_CHART_OPTIONS;
3639

40+
public ngAfterViewInit(): void {
41+
// Initialize scroll state after view is ready
42+
setTimeout(() => this.onScroll(), 0);
43+
}
44+
3745
private readonly allMetricCards = computed<FoundationMetricCard[]>(() => {
3846
const metrics = AGGREGATE_FOUNDATION_METRICS;
3947

@@ -168,6 +176,18 @@ export class FoundationHealthComponent {
168176
container.scrollBy({ left: 320, behavior: 'smooth' });
169177
}
170178

179+
public onScroll(): void {
180+
if (!this.carouselScrollContainer?.nativeElement) return;
181+
const container = this.carouselScrollContainer.nativeElement;
182+
183+
// Check if can scroll left (not at the start)
184+
this.canScrollLeft.set(container.scrollLeft > 0);
185+
186+
// Check if can scroll right (not at the end)
187+
const maxScrollLeft = container.scrollWidth - container.clientWidth;
188+
this.canScrollRight.set(container.scrollLeft < maxScrollLeft - 1);
189+
}
190+
171191
private formatSoftwareValue(valueInMillions: number): string {
172192
if (valueInMillions >= 1000) {
173193
const billions = valueInMillions / 1000;

apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<section class="flex flex-col flex-1" data-testid="dashboard-my-meetings-section">
55
<!-- Header -->
66
<div class="flex items-center justify-between mb-4">
7-
<h2 class="font-display font-semibold text-gray-900">My Meetings</h2>
7+
<h2>My Meetings</h2>
88
<lfx-button
99
label="View All"
1010
icon="fa-light fa-chevron-right"
@@ -17,19 +17,29 @@ <h2 class="font-display font-semibold text-gray-900">My Meetings</h2>
1717
</div>
1818

1919
<!-- Scrollable Content -->
20-
<div class="flex flex-col flex-1">
20+
<div class="relative flex flex-col flex-1 -my-1">
2121
@if (loading()) {
2222
<div class="flex flex-col gap-3" data-testid="dashboard-my-meetings-loading">
2323
<p-skeleton width="100%" height="140px"></p-skeleton>
2424
<p-skeleton width="100%" height="140px"></p-skeleton>
2525
</div>
2626
} @else {
27-
<div class="flex flex-col gap-6 overflow-scroll max-h-[30rem]" data-testid="dashboard-my-meetings-list">
27+
<!-- Top Fade Gradient -->
28+
@if (canScrollUp()) {
29+
<div class="absolute top-0 left-0 right-0 h-16 pointer-events-none z-10" style="background: linear-gradient(to bottom, #f8f8f9 0%, transparent 100%);"></div>
30+
}
31+
32+
<!-- Bottom Fade Gradient -->
33+
@if (canScrollDown()) {
34+
<div class="absolute bottom-0 left-0 right-0 h-16 pointer-events-none z-10" style="background: linear-gradient(to top, #f8f8f9 0%, transparent 100%);"></div>
35+
}
36+
37+
<div #scrollContainer class="flex flex-col gap-6 overflow-scroll max-h-[30rem] px-1 -mx-1 py-1" (scroll)="onScroll()" data-testid="dashboard-my-meetings-list">
2838
@if (todayMeetings().length > 0 || upcomingMeetings().length > 0) {
2939
<!-- TODAY Section - only show if there are meetings today -->
3040
@if (todayMeetings().length > 0) {
3141
<div>
32-
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Today</h4>
42+
<h4 class="text-gray-500 mb-3">Today</h4>
3343
<div class="flex flex-col gap-3">
3444
@for (item of todayMeetings(); track item.meeting.uid) {
3545
<lfx-dashboard-meeting-card
@@ -44,7 +54,7 @@ <h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Today
4454
<!-- UPCOMING Section - only show if there are upcoming meetings -->
4555
@if (upcomingMeetings().length > 0) {
4656
<div>
47-
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Upcoming</h4>
57+
<h4 class="text-gray-500 mb-3">Upcoming</h4>
4858
<div class="flex flex-col gap-3">
4959
@for (item of upcomingMeetings(); track item.meeting.uid) {
5060
<lfx-dashboard-meeting-card

apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: MIT
33

44
import { CommonModule } from '@angular/common';
5-
import { Component, computed, inject, signal } from '@angular/core';
5+
import { AfterViewInit, Component, computed, ElementRef, inject, signal, ViewChild } from '@angular/core';
66
import { toSignal } from '@angular/core/rxjs-interop';
77
import { Router } from '@angular/router';
88
import { MeetingService } from '@app/shared/services/meeting.service';
@@ -21,12 +21,34 @@ import type { MeetingWithOccurrence } from '@lfx-one/shared/interfaces';
2121
templateUrl: './my-meetings.component.html',
2222
styleUrl: './my-meetings.component.scss',
2323
})
24-
export class MyMeetingsComponent {
24+
export class MyMeetingsComponent implements AfterViewInit {
25+
@ViewChild('scrollContainer') private scrollContainer!: ElementRef;
26+
2527
private readonly meetingService = inject(MeetingService);
2628
private readonly router = inject(Router);
2729
protected readonly loading = signal(true);
2830
private readonly allMeetings = toSignal(this.meetingService.getMeetings().pipe(finalize(() => this.loading.set(false))), { initialValue: [] });
2931

32+
public readonly canScrollUp = signal<boolean>(false);
33+
public readonly canScrollDown = signal<boolean>(false);
34+
35+
public ngAfterViewInit(): void {
36+
// Initialize scroll state after view is ready
37+
setTimeout(() => this.onScroll(), 0);
38+
}
39+
40+
public onScroll(): void {
41+
if (!this.scrollContainer?.nativeElement) return;
42+
const container = this.scrollContainer.nativeElement;
43+
44+
// Check if can scroll up (not at the top)
45+
this.canScrollUp.set(container.scrollTop > 0);
46+
47+
// Check if can scroll down (not at the bottom)
48+
const maxScrollTop = container.scrollHeight - container.clientHeight;
49+
this.canScrollDown.set(container.scrollTop < maxScrollTop - 1);
50+
}
51+
3052
protected readonly todayMeetings = computed<MeetingWithOccurrence[]>(() => {
3153
const now = new Date();
3254
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
<div data-testid="dashboard-my-projects-section">
55
<div class="flex items-center justify-between mb-4">
6-
<h2 class="font-display font-semibold text-[16px]">My Projects</h2>
6+
<h2>My Projects</h2>
77
</div>
88

9-
<div class="rounded-lg border border-gray-200 overflow-hidden relative">
9+
<div class="rounded-lg overflow-hidden relative shadow-[0px_1px_2px_-1px_rgba(0,0,0,0.10),0px_1px_3px_0px_rgba(0,0,0,0.10)]">
1010
@if (loading()) {
1111
<div class="absolute inset-0 bg-white/60 flex items-center justify-center z-10" data-testid="dashboard-my-projects-loading">
1212
<div class="flex flex-col items-center gap-2">

apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<section data-testid="dashboard-organization-involvement-section">
55
<div class="flex items-center justify-between mb-4">
66
<div class="flex flex-col md:flex-row md:items-center gap-3">
7-
<h2 class="font-['Roboto_Slab'] font-semibold text-[16px]">{{ accountName() }}'s Involvement</h2>
7+
<h2>{{ accountName() }}'s Involvement</h2>
88

99
<!-- Filter Pills -->
1010
<lfx-filter-pills [options]="filterOptions" [selectedFilter]="selectedFilter()" (filterChange)="handleFilterChange($event)"></lfx-filter-pills>
@@ -49,18 +49,28 @@ <h2 class="font-['Roboto_Slab'] font-semibold text-[16px]">{{ accountName() }}'s
4949
</div>
5050
</div>
5151
} @else {
52-
<div class="overflow-hidden">
53-
<div #carouselScroll class="flex gap-4 overflow-x-auto pb-2 hide-scrollbar scroll-smooth" data-testid="dashboard-involvement-carousel">
52+
<div class="relative overflow-hidden">
53+
<!-- Left Fade Gradient -->
54+
@if (canScrollLeft()) {
55+
<div class="hidden md:block absolute top-0 bottom-0 left-0 w-24 pointer-events-none z-10" style="background: linear-gradient(to right, #f8f8f9 0%, transparent 100%);"></div>
56+
}
57+
58+
<!-- Right Fade Gradient -->
59+
@if (canScrollRight()) {
60+
<div class="hidden md:block absolute top-0 bottom-0 right-0 w-24 pointer-events-none z-10" style="background: linear-gradient(to left, #f8f8f9 0%, transparent 100%);"></div>
61+
}
62+
63+
<div #carouselScroll class="flex gap-4 overflow-x-auto pb-2 hide-scrollbar scroll-smooth" data-testid="dashboard-involvement-carousel" (scroll)="onScroll()">
5464
@for (metric of primaryMetrics(); track metric.title) {
5565
<!-- Membership Tier Card (Special) -->
5666
@if (metric.isMembershipTier) {
5767
<div
58-
class="p-4 bg-white rounded-lg border border-slate-200 hover:border-[#0094FF] transition-colors cursor-pointer flex-shrink-0 w-80"
68+
class="p-4 bg-white rounded-lg hover:border hover:border-[#0094FF] transition-colors cursor-pointer flex-shrink-0 w-80 shadow-[0px_1px_2px_-1px_rgba(0,0,0,0.10),0px_1px_3px_0px_rgba(0,0,0,0.10)]"
5969
[attr.data-testid]="'dashboard-involvement-metric-' + metric.title">
6070
<div class="space-y-3">
6171
<div class="flex items-center gap-1">
6272
<i [class]="metric.icon + ' text-gray-500'" class="w-4 h-4"></i>
63-
<h5 class="text-sm font-medium">{{ metric.title }}</h5>
73+
<h4>{{ metric.title }}</h4>
6474
</div>
6575

6676
<!-- Membership Info -->
@@ -84,11 +94,11 @@ <h5 class="text-sm font-medium">{{ metric.title }}</h5>
8494
</div>
8595
} @else {
8696
<!-- Regular Card -->
87-
<div class="p-4 bg-white rounded-lg border border-slate-200 flex-shrink-0 w-80" [attr.data-testid]="'dashboard-involvement-metric-' + metric.title">
97+
<div class="p-4 bg-white rounded-lg flex-shrink-0 w-80 shadow-[0px_1px_2px_-1px_rgba(0,0,0,0.10),0px_1px_3px_0px_rgba(0,0,0,0.10)]" [attr.data-testid]="'dashboard-involvement-metric-' + metric.title">
8898
<div class="space-y-3">
8999
<div class="flex items-center gap-3">
90100
<i [class]="metric.icon + ' text-gray-500'" class="w-4 h-4"></i>
91-
<h5 class="text-sm font-medium">{{ metric.title }}</h5>
101+
<h4>{{ metric.title }}</h4>
92102
</div>
93103
@if (metric.chartData) {
94104
<div class="w-full h-16">

apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: MIT
33

44
import { CommonModule } from '@angular/common';
5-
import { Component, computed, ElementRef, inject, signal, ViewChild } from '@angular/core';
5+
import { AfterViewInit, Component, computed, ElementRef, inject, signal, ViewChild } from '@angular/core';
66
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
77
import { AccountContextService } from '@app/shared/services/account-context.service';
88
import { AnalyticsService } from '@app/shared/services/analytics.service';
@@ -22,13 +22,16 @@ import { combineLatest, finalize, map, of, switchMap } from 'rxjs';
2222
templateUrl: './organization-involvement.component.html',
2323
styleUrl: './organization-involvement.component.scss',
2424
})
25-
export class OrganizationInvolvementComponent {
25+
export class OrganizationInvolvementComponent implements AfterViewInit {
2626
@ViewChild('carouselScroll') public carouselScrollContainer!: ElementRef;
2727

2828
private readonly analyticsService = inject(AnalyticsService);
2929
private readonly accountContextService = inject(AccountContextService);
3030
private readonly projectContextService = inject(ProjectContextService);
3131

32+
public readonly canScrollLeft = signal<boolean>(false);
33+
public readonly canScrollRight = signal<boolean>(false);
34+
3235
private readonly contributionsLoading = signal(true);
3336
private readonly dashboardLoading = signal(true);
3437
private readonly eventsLoading = signal(true);
@@ -50,6 +53,11 @@ export class OrganizationInvolvementComponent {
5053
{ id: 'education', label: 'Education' },
5154
];
5255

56+
public ngAfterViewInit(): void {
57+
// Initialize scroll state after view is ready
58+
setTimeout(() => this.onScroll(), 0);
59+
}
60+
5361
public readonly primaryMetrics = computed<OrganizationInvolvementMetricWithChart[]>((): OrganizationInvolvementMetricWithChart[] => {
5462
const contributionsData = this.contributionsOverviewData();
5563
const dashboardData = this.boardMemberDashboardData();
@@ -121,6 +129,18 @@ export class OrganizationInvolvementComponent {
121129
container.scrollBy({ left: 300, behavior: 'smooth' });
122130
}
123131

132+
public onScroll(): void {
133+
if (!this.carouselScrollContainer?.nativeElement) return;
134+
const container = this.carouselScrollContainer.nativeElement;
135+
136+
// Check if can scroll left (not at the start)
137+
this.canScrollLeft.set(container.scrollLeft > 0);
138+
139+
// Check if can scroll right (not at the end)
140+
const maxScrollLeft = container.scrollWidth - container.clientWidth;
141+
this.canScrollRight.set(container.scrollLeft < maxScrollLeft - 1);
142+
}
143+
124144
private initializeContributionsOverviewData() {
125145
return toSignal(
126146
this.selectedAccountId$.pipe(

apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<section class="flex flex-col flex-1" data-testid="dashboard-pending-actions-section">
55
<!-- Header -->
66
<div class="flex items-center justify-between mb-4">
7-
<h2 class="font-display font-semibold text-gray-900">Pending Actions</h2>
7+
<h2>Pending Actions</h2>
88
<lfx-button
99
label="View All"
1010
icon="fa-light fa-chevron-right"
@@ -17,16 +17,27 @@ <h2 class="font-display font-semibold text-gray-900">Pending Actions</h2>
1717
</div>
1818

1919
<!-- Scrollable Content -->
20-
<div class="flex flex-col flex-1 max-h-[28.125rem] overflow-y-auto">
21-
<div class="flex flex-col gap-3" data-testid="dashboard-pending-actions-list">
20+
<div class="relative flex flex-col flex-1 -my-1">
21+
<!-- Top Fade Gradient -->
22+
@if (canScrollUp()) {
23+
<div class="absolute top-0 left-0 right-0 h-16 pointer-events-none z-10" style="background: linear-gradient(to bottom, #f8f8f9 0%, transparent 100%);"></div>
24+
}
25+
26+
<!-- Bottom Fade Gradient -->
27+
@if (canScrollDown()) {
28+
<div class="absolute bottom-0 left-0 right-0 h-16 pointer-events-none z-10" style="background: linear-gradient(to top, #f8f8f9 0%, transparent 100%);"></div>
29+
}
30+
31+
<div #scrollContainer class="flex flex-col flex-1 max-h-[28.125rem] overflow-y-auto px-1 -mx-1 py-1" (scroll)="onScroll()">
32+
<div class="flex flex-col gap-3" data-testid="dashboard-pending-actions-list">
2233
@for (item of pendingActions(); track item.text) {
2334
<div
24-
class="p-4 border rounded-lg"
35+
class="p-4 rounded-lg shadow-[0px_1px_2px_-1px_rgba(0,0,0,0.10),0px_1px_3px_0px_rgba(0,0,0,0.10)]"
2536
[ngClass]="{
26-
'bg-amber-50 border-amber-200': item.color === 'amber',
27-
'bg-blue-50 border-blue-200': item.color === 'blue',
28-
'bg-green-50 border-green-200': item.color === 'green',
29-
'bg-purple-50 border-purple-200': item.color === 'purple',
37+
'bg-amber-50': item.color === 'amber',
38+
'bg-blue-50': item.color === 'blue',
39+
'bg-green-50': item.color === 'green',
40+
'bg-purple-50': item.color === 'purple',
3041
}"
3142
[attr.data-testid]="'dashboard-pending-actions-item-' + item.type">
3243
<!-- Header with Type and Badge -->
@@ -41,16 +52,15 @@ <h2 class="font-display font-semibold text-gray-900">Pending Actions</h2>
4152
}">
4253
<i [ngClass]="[item.icon, 'w-4', 'h-4']"></i>
4354
</div>
44-
<span
45-
class="text-xs font-medium"
55+
<h3
4656
[ngClass]="{
4757
'text-amber-700': item.color === 'amber',
4858
'text-blue-700': item.color === 'blue',
4959
'text-green-700': item.color === 'green',
5060
'text-purple-700': item.color === 'purple',
5161
}">
5262
{{ item.type }}
53-
</span>
63+
</h3>
5464
</div>
5565
<span
5666
class="inline-flex items-center justify-center rounded-md px-2 py-0.5 text-xs font-medium bg-white text-gray-700 border border-gray-200"
@@ -83,6 +93,7 @@ <h2 class="font-display font-semibold text-gray-900">Pending Actions</h2>
8393
</button>
8494
</div>
8595
}
96+
</div>
8697
</div>
8798
</div>
8899
</section>

0 commit comments

Comments
 (0)