Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,51 +28,6 @@ <h1>{{ committeeLabelPlural }}</h1>
</div>
<p class="mt-2 text-gray-500" data-testid="dashboard-hero-description">Organize people and governance for your project.</p>
</div>

<!-- Sticky Top Bar with Search and Filters -->
<div class="sticky top-0 md:top-6 z-10 bg-white rounded-lg border border-gray-200 p-4 -mx-0 mb-8">
<div class="flex flex-col md:flex-row md:items-center gap-3 md:gap-4">
<!-- Search Input -->
<div class="flex-1">
<lfx-input-text
[form]="searchForm"
control="search"
[placeholder]="'Search ' + committeeLabel.toLowerCase() + '...'"
icon="fa-light fa-search"
styleClass="w-full"
size="small"
data-testid="committee-search-input"></lfx-input-text>
</div>

<!-- Committee Type Filter Dropdown -->
<div class="w-full sm:w-64">
<lfx-select
[form]="searchForm"
control="category"
size="small"
[options]="categories()"
placeholder="Select Type"
[showClear]="true"
styleClass="w-full"
(onChange)="onCategoryChange($event.value)"
data-testid="committee-type-filter"></lfx-select>
</div>

<!-- Voting Status Filter Dropdown -->
<div class="w-full sm:w-64">
<lfx-select
[form]="searchForm"
control="votingStatus"
size="small"
[options]="votingStatusOptions()"
placeholder="Select Voting Status"
[showClear]="true"
styleClass="w-full"
(onChange)="onVotingStatusChange($event.value)"
data-testid="committee-voting-status-filter"></lfx-select>
</div>
</div>
</div>
</div>

<!-- Content Area - Table or Cards -->
Expand Down Expand Up @@ -107,6 +62,9 @@ <h3 class="text-xl font-semibold text-gray-900 mt-4">No {{ committeeLabelPlural.
[committees]="filteredCommittees()"
[canManageCommittee]="canCreateGroup()"
[committeeLabel]="committeeLabel"
[searchForm]="searchForm"
[categoryOptions]="categories()"
[votingStatusOptions]="votingStatusOptions()"
(refresh)="refreshCommittees()">
</lfx-committee-table>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

import { Component, computed, inject, signal, Signal, WritableSignal } from '@angular/core';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { ButtonComponent } from '@components/button/button.component';
import { CardComponent } from '@components/card/card.component';
import { InputTextComponent } from '@components/input-text/input-text.component';
import { SelectComponent } from '@components/select/select.component';
import { COMMITTEE_LABEL } from '@lfx-one/shared/constants';
import { Committee, ProjectContext } from '@lfx-one/shared/interfaces';
import { CommitteeService } from '@services/committee.service';
Expand All @@ -22,7 +20,7 @@ import { CommitteeTableComponent } from '../components/committee-table/committee

@Component({
selector: 'lfx-committee-dashboard',
imports: [ReactiveFormsModule, InputTextComponent, SelectComponent, ButtonComponent, CardComponent, CommitteeTableComponent],
imports: [ButtonComponent, CardComponent, CommitteeTableComponent],
templateUrl: './committee-dashboard.component.html',
styleUrl: './committee-dashboard.component.scss',
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,49 @@
<!-- SPDX-License-Identifier: MIT -->

<lfx-card>
<!-- Filter Bar Inside Card -->
<div class="pb-2 mb-2">
<div class="flex flex-col md:flex-row md:items-center gap-3 md:gap-4">
<!-- Search Input -->
<div class="flex-1">
<lfx-input-text
[form]="searchForm()"
control="search"
placeholder="Search committees..."
icon="fa-light fa-search"
styleClass="w-full"
size="small"
data-testid="committee-search-input"></lfx-input-text>
</div>

<!-- Category Filter Dropdown -->
<div class="w-full sm:w-64">
<lfx-select
[form]="searchForm()"
control="category"
size="small"
[options]="categoryOptions()"
placeholder="Select Type"
[showClear]="true"
styleClass="w-full"
data-testid="committee-type-filter"></lfx-select>
</div>

<!-- Voting Status Filter Dropdown -->
<div class="w-full sm:w-64">
<lfx-select
[form]="searchForm()"
control="votingStatus"
size="small"
[options]="votingStatusOptions()"
placeholder="Select Voting Status"
[showClear]="true"
styleClass="w-full"
data-testid="committee-voting-status-filter"></lfx-select>
</div>
</div>
</div>

<lfx-table
[value]="committees()"
[paginator]="committees().length > 10"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

import { DatePipe } from '@angular/common';
import { Component, inject, input, output, signal, WritableSignal } from '@angular/core';
import { ReactiveFormsModule, FormGroup } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { ButtonComponent } from '@components/button/button.component';
import { CardComponent } from '@components/card/card.component';
import { InputTextComponent } from '@components/input-text/input-text.component';
import { SelectComponent } from '@components/select/select.component';
import { TableComponent } from '@components/table/table.component';
import { TagComponent } from '@components/tag/tag.component';
import { Committee, COMMITTEE_LABEL } from '@lfx-one/shared';
Expand All @@ -23,11 +26,14 @@ import { MemberFormComponent } from '../member-form/member-form.component';
selector: 'lfx-committee-table',
imports: [
DatePipe,
ReactiveFormsModule,
RouterLink,
CardComponent,
ButtonComponent,
TableComponent,
TagComponent,
InputTextComponent,
SelectComponent,
TooltipModule,
ConfirmDialogModule,
DynamicDialogModule,
Expand All @@ -48,6 +54,9 @@ export class CommitteeTableComponent {
public committees = input.required<Committee[]>();
public canManageCommittee = input<boolean>(false);
public committeeLabel = input<string>(COMMITTEE_LABEL.singular);
public searchForm = input.required<FormGroup>();
public categoryOptions = input.required<{ label: string; value: string | null }[]>();
public votingStatusOptions = input.required<{ label: string; value: string | null }[]>();

// State
public isDeleting: WritableSignal<boolean> = signal<boolean>(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ <h2 class="mb-0">{{ title() }}</h2>
<div class="flex items-center gap-2">
<button
type="button"
(click)="scrollLeft()"
(click)="scrollShadowDirective.scrollLeft()"
class="h-8 w-8 p-0 flex items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 hover:border-gray-400 transition-colors"
data-testid="foundation-health-carousel-prev"
aria-label="Scroll left">
<i class="fa-light fa-chevron-left"></i>
</button>
<button
type="button"
(click)="scrollRight()"
(click)="scrollShadowDirective.scrollRight()"
class="h-8 w-8 p-0 flex items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 hover:border-gray-400 transition-colors"
data-testid="foundation-health-carousel-next"
aria-label="Scroll right">
Expand All @@ -52,8 +52,8 @@ <h2 class="mb-0">{{ title() }}</h2>
</div>
} @else {
<!-- Carousel Container -->
<div class="overflow-hidden">
<div #carouselScroll class="flex gap-4 overflow-x-auto pb-2 hide-scrollbar scroll-smooth" data-testid="foundation-health-carousel">
<div class="relative overflow-hidden">
<div lfxScrollShadow [scrollDistance]="320" class="flex gap-4 overflow-x-auto hide-scrollbar scroll-smooth" data-testid="foundation-health-carousel">
@for (card of metricCards(); track card.testId) {
@switch (card.customContentType) {
<!-- Bar Chart - Use shared metric card -->
Expand Down Expand Up @@ -176,6 +176,20 @@ <h2 class="mb-0">{{ title() }}</h2>
}
}
</div>

<!-- Right shadow fade -->
@if (scrollShadowDirective && scrollShadowDirective.showRightShadow()) {
<div
class="absolute top-0 bottom-0 right-0 w-32 z-10 pointer-events-none bg-[linear-gradient(to_right,transparent,#f9fafb)]"
data-testid="foundation-health-shadow-right"></div>
}

<!-- Left shadow fade -->
@if (scrollShadowDirective && scrollShadowDirective.showLeftShadow()) {
<div
class="absolute top-0 bottom-0 left-0 w-32 z-10 pointer-events-none bg-[linear-gradient(to_left,transparent,#f9fafb)]"
data-testid="foundation-health-shadow-left"></div>
}
</div>
}
</section>
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT

import { Component, computed, ElementRef, inject, input, signal, ViewChild } from '@angular/core';
import { Component, computed, inject, input, signal, ViewChild } from '@angular/core';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { DataCopilotComponent } from '@app/shared/components/data-copilot/data-copilot.component';
import { FilterOption, FilterPillsComponent } from '@components/filter-pills/filter-pills.component';
import { MetricCardComponent } from '@components/metric-card/metric-card.component';
import { ScrollShadowDirective } from '@shared/directives/scroll-shadow.directive';
import { BASE_BAR_CHART_OPTIONS, BASE_LINE_CHART_OPTIONS, lfxColors, PRIMARY_FOUNDATION_HEALTH_METRICS } from '@lfx-one/shared/constants';
import { hexToRgba } from '@lfx-one/shared/utils';
import { AnalyticsService } from '@services/analytics.service';
Expand All @@ -23,12 +24,12 @@ import type {

@Component({
selector: 'lfx-foundation-health',
imports: [FilterPillsComponent, MetricCardComponent, DataCopilotComponent],
imports: [FilterPillsComponent, MetricCardComponent, DataCopilotComponent, ScrollShadowDirective],
templateUrl: './foundation-health.component.html',
styleUrl: './foundation-health.component.scss',
})
export class FoundationHealthComponent {
@ViewChild('carouselScroll') public carouselScrollContainer!: ElementRef;
@ViewChild(ScrollShadowDirective) public scrollShadowDirective!: ScrollShadowDirective;

private readonly analyticsService = inject(AnalyticsService);
private readonly projectContextService = inject(ProjectContextService);
Expand Down Expand Up @@ -88,18 +89,6 @@ export class FoundationHealthComponent {
this.selectedFilter.set(filter);
}

public scrollLeft(): void {
if (!this.carouselScrollContainer?.nativeElement) return;
const container = this.carouselScrollContainer.nativeElement;
container.scrollBy({ left: -320, behavior: 'smooth' });
}

public scrollRight(): void {
if (!this.carouselScrollContainer?.nativeElement) return;
const container = this.carouselScrollContainer.nativeElement;
container.scrollBy({ left: 320, behavior: 'smooth' });
}

private initializeTotalProjectsCard() {
return computed(() => this.transformTotalProjects(this.getMetricConfig('Total Projects')));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ <h2 class="mb-0">{{ accountName() }}'s Involvement</h2>
<div class="flex items-center gap-2">
<button
type="button"
(click)="scrollLeft()"
(click)="scrollShadowDirective.scrollLeft()"
class="h-8 w-8 p-0 flex items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 hover:border-gray-400 transition-colors"
data-testid="dashboard-involvement-scroll-left"
aria-label="Scroll left">
<i class="fa-light fa-chevron-left"></i>
</button>
<button
type="button"
(click)="scrollRight()"
(click)="scrollShadowDirective.scrollRight()"
class="h-8 w-8 p-0 flex items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 hover:border-gray-400 transition-colors"
data-testid="dashboard-involvement-scroll-right"
aria-label="Scroll right">
Expand All @@ -47,8 +47,8 @@ <h2 class="mb-0">{{ accountName() }}'s Involvement</h2>
</div>
</div>
} @else {
<div class="overflow-hidden">
<div #carouselScroll class="flex gap-4 overflow-x-auto pb-2 hide-scrollbar scroll-smooth" data-testid="dashboard-involvement-carousel">
<div class="relative overflow-hidden">
<div lfxScrollShadow class="flex gap-4 overflow-x-auto hide-scrollbar scroll-smooth" data-testid="dashboard-involvement-carousel">
@for (metric of primaryMetrics(); track metric.testId) {
<!-- Membership Tier Card (Special) -->
@if (metric.isMembershipTier) {
Expand Down Expand Up @@ -93,6 +93,20 @@ <h5 class="text-sm font-medium">{{ metric.title }}</h5>
}
}
</div>

<!-- Right shadow fade -->
@if (scrollShadowDirective && scrollShadowDirective.showRightShadow()) {
<div
class="absolute top-0 bottom-0 right-0 w-32 z-10 pointer-events-none bg-[linear-gradient(to_right,transparent,#f9fafb)]"
data-testid="dashboard-involvement-shadow-right"></div>
}

<!-- Left shadow fade -->
@if (scrollShadowDirective && scrollShadowDirective.showLeftShadow()) {
<div
class="absolute top-0 bottom-0 left-0 w-32 z-10 pointer-events-none bg-[linear-gradient(to_left,transparent,#f9fafb)]"
data-testid="dashboard-involvement-shadow-left"></div>
}
</div>
}
</section>
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT

import { Component, computed, ElementRef, inject, signal, ViewChild } from '@angular/core';
import { Component, computed, inject, signal, ViewChild } from '@angular/core';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { DataCopilotComponent } from '@app/shared/components/data-copilot/data-copilot.component';
import { FilterOption, FilterPillsComponent } from '@components/filter-pills/filter-pills.component';
import { MetricCardComponent } from '@components/metric-card/metric-card.component';
import { TagComponent } from '@components/tag/tag.component';
import { ScrollShadowDirective } from '@shared/directives/scroll-shadow.directive';
import { BASE_BAR_CHART_OPTIONS, BASE_LINE_CHART_OPTIONS, lfxColors, PRIMARY_INVOLVEMENT_METRICS } from '@lfx-one/shared/constants';
import { hexToRgba } from '@lfx-one/shared/utils';
import { AccountContextService } from '@services/account-context.service';
Expand All @@ -27,12 +28,12 @@ import type { ChartOptions, ChartType } from 'chart.js';

@Component({
selector: 'lfx-organization-involvement',
imports: [FilterPillsComponent, MetricCardComponent, TagComponent, DataCopilotComponent],
imports: [FilterPillsComponent, MetricCardComponent, TagComponent, DataCopilotComponent, ScrollShadowDirective],
templateUrl: './organization-involvement.component.html',
styleUrl: './organization-involvement.component.scss',
})
export class OrganizationInvolvementComponent {
@ViewChild('carouselScroll') public carouselScrollContainer!: ElementRef;
@ViewChild(ScrollShadowDirective) public scrollShadowDirective!: ScrollShadowDirective;

private readonly analyticsService = inject(AnalyticsService);
private readonly accountContextService = inject(AccountContextService);
Expand Down Expand Up @@ -87,18 +88,6 @@ export class OrganizationInvolvementComponent {
this.selectedFilter.set(filter);
}

public scrollLeft(): void {
if (!this.carouselScrollContainer?.nativeElement) return;
const container = this.carouselScrollContainer.nativeElement;
container.scrollBy({ left: -300, behavior: 'smooth' });
}

public scrollRight(): void {
if (!this.carouselScrollContainer?.nativeElement) return;
const container = this.carouselScrollContainer.nativeElement;
container.scrollBy({ left: 300, behavior: 'smooth' });
}

private getMetricConfig(title: string): DashboardMetricCard {
return PRIMARY_INVOLVEMENT_METRICS.find((m) => m.title === title || (title === 'Membership Tier' && m.isMembershipTier))!;
}
Expand Down
Loading