diff --git a/client/src/app/components/branches-table/branches-table.component.html b/client/src/app/components/branches-table/branches-table.component.html index 6a59550ae..06767c945 100644 --- a/client/src/app/components/branches-table/branches-table.component.html +++ b/client/src/app/components/branches-table/branches-table.component.html @@ -54,7 +54,7 @@ (mouseleave)="isHovered.set(branch.name, false)" >
- @if (!rowNode.node.children && isHovered.get(branch.name) && keycloakService.isLoggedIn()) { + @if (!rowNode.node.children && isHovered.get(branch.name) && isLoggedIn()) { @if (branch.isPinned) { diff --git a/client/src/app/components/branches-table/branches-table.component.ts b/client/src/app/components/branches-table/branches-table.component.ts index 1a97cdd67..f77652026 100644 --- a/client/src/app/components/branches-table/branches-table.component.ts +++ b/client/src/app/components/branches-table/branches-table.component.ts @@ -22,7 +22,7 @@ import { DividerModule } from 'primeng/divider'; import { TooltipModule } from 'primeng/tooltip'; import { FormsModule } from '@angular/forms'; import { TimeAgoPipe } from '@app/pipes/time-ago.pipe'; -import { FILTER_OPTIONS_TOKEN, SearchTableService } from '@app/core/services/search-table.service'; +import { FILTER_OPTIONS_TOKEN, FilterOption, SearchTableService } from '@app/core/services/search-table.service'; import { TableFilterComponent } from '../table-filter/table-filter.component'; import { WorkflowRunStatusComponent } from '@app/components/workflow-run-status-component/workflow-run-status.component'; import { HighlightPipe } from '@app/pipes/highlight.pipe'; @@ -31,46 +31,61 @@ import { KeycloakService } from '@app/core/services/keycloak/keycloak.service'; import { provideTablerIcons, TablerIconComponent } from 'angular-tabler-icons'; import { IconExternalLink, IconFilterPlus, IconGitBranch, IconGitCommit, IconPinned, IconPinnedOff, IconShieldHalf, IconBrandGithub } from 'angular-tabler-icons/icons'; -type BranchInfoWithLink = BranchInfoDto & { link: string; lastCommitLink: string }; - -const FILTER_OPTIONS = [ - { name: 'All Branches', filter: (branches: BranchInfoWithLink[]) => branches }, - { name: 'Default Branch', filter: (branches: BranchInfoWithLink[]) => branches.filter(branch => branch.isDefault) }, - { name: 'Protected Branches', filter: (branches: BranchInfoWithLink[]) => branches.filter(branch => branch.isProtected) }, - { - name: 'Active Branches', - filter: (branches: BranchInfoWithLink[]) => - branches.filter(branch => { - const date = new Date(branch.updatedAt || ''); - const staleThreshold = new Date(); - staleThreshold.setDate(staleThreshold.getDate() - 30); - - return date >= staleThreshold; - }), - }, - { - name: 'Last 7 Day', - filter: (branches: BranchInfoWithLink[]) => - branches.filter(branch => { - const date = new Date(branch.updatedAt || ''); - const staleThreshold = new Date(); - staleThreshold.setDate(staleThreshold.getDate() - 7); - - return date >= staleThreshold; - }), - }, - { - name: 'Stale Branches', - filter: (branches: BranchInfoWithLink[]) => - branches.filter(branch => { - const date = new Date(branch.updatedAt || ''); - const staleThreshold = new Date(); - staleThreshold.setDate(staleThreshold.getDate() - 30); - - return date < staleThreshold; - }), - }, -]; +export type BranchInfoWithLink = BranchInfoDto & { link: string; lastCommitLink: string }; + +export function createBranchFilterOptions(keycloakService: KeycloakService): FilterOption[] { + const isLoggedIn = keycloakService.isLoggedIn(); + const githubPreferredUsername = isLoggedIn ? keycloakService.getPreferredUsername() : ''; + + const options: FilterOption[] = [ + { name: 'All Branches', filter: (branches: BranchInfoWithLink[]) => branches }, + { name: 'Default Branch', filter: (branches: BranchInfoWithLink[]) => branches.filter(branch => branch.isDefault) }, + { name: 'Protected Branches', filter: (branches: BranchInfoWithLink[]) => branches.filter(branch => branch.isProtected) }, + { + name: 'Active Branches', + filter: (branches: BranchInfoWithLink[]) => + branches.filter(branch => { + const date = new Date(branch.updatedAt || ''); + const staleThreshold = new Date(); + staleThreshold.setDate(staleThreshold.getDate() - 30); + + return date >= staleThreshold; + }), + }, + { + name: 'Last 7 Day', + filter: (branches: BranchInfoWithLink[]) => + branches.filter(branch => { + const date = new Date(branch.updatedAt || ''); + const staleThreshold = new Date(); + staleThreshold.setDate(staleThreshold.getDate() - 7); + + return date >= staleThreshold; + }), + }, + { + name: 'Stale Branches', + filter: (branches: BranchInfoWithLink[]) => + branches.filter(branch => { + const date = new Date(branch.updatedAt || ''); + const staleThreshold = new Date(); + staleThreshold.setDate(staleThreshold.getDate() - 30); + + return date < staleThreshold; + }), + }, + ]; + + // Add the "Your Branches" filter option only if the user is logged in + if (isLoggedIn && githubPreferredUsername) { + options.splice(1, 0, { + name: 'Your Branches', + filter: (branches: BranchInfoWithLink[]) => branches.filter(branch => branch.updatedBy?.login?.toLowerCase() === githubPreferredUsername.toLowerCase()), + }); + } + + return options; +} @Component({ selector: 'app-branches-table', @@ -97,7 +112,7 @@ const FILTER_OPTIONS = [ ], providers: [ SearchTableService, - { provide: FILTER_OPTIONS_TOKEN, useValue: FILTER_OPTIONS }, + { provide: FILTER_OPTIONS_TOKEN, useFactory: createBranchFilterOptions, deps: [KeycloakService] }, provideTablerIcons({ IconFilterPlus, IconPinnedOff, IconPinned, IconShieldHalf, IconBrandGithub, IconExternalLink, IconGitCommit, IconGitBranch }), ], templateUrl: './branches-table.component.html', @@ -108,7 +123,8 @@ export class BranchTableComponent { messageService = inject(MessageService); queryClient = inject(QueryClient); searchTableService = inject(SearchTableService); - keycloakService = inject(KeycloakService); + private keycloakService = inject(KeycloakService); + readonly isLoggedIn = computed(() => this.keycloakService.isLoggedIn()); treeTable = viewChild('table'); diff --git a/client/src/app/core/services/search-table.service.ts b/client/src/app/core/services/search-table.service.ts index 725477858..34db0f9cf 100644 --- a/client/src/app/core/services/search-table.service.ts +++ b/client/src/app/core/services/search-table.service.ts @@ -4,7 +4,7 @@ import { TreeTable } from 'primeng/treetable'; import { UserProfile } from './keycloak/user-profile'; export type SearchTable = TreeTable | Table; -type FilterOption = { name: string; filter: (prs: T[], userProfile?: UserProfile) => T[] }; +export type FilterOption = { name: string; filter: (prs: T[], userProfile?: UserProfile) => T[] }; export const FILTER_OPTIONS_TOKEN = new InjectionToken('filterOptions'); diff --git a/client/src/app/pages/branch-list/branch-list.spec.ts b/client/src/app/pages/branch-list/branch-list.spec.ts index 7e17b35e3..d33c47082 100644 --- a/client/src/app/pages/branch-list/branch-list.spec.ts +++ b/client/src/app/pages/branch-list/branch-list.spec.ts @@ -2,6 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BranchListComponent } from './branch-list.component'; import { importProvidersFrom } from '@angular/core'; import { TestModule } from '@app/test.module'; +import { FILTER_OPTIONS_TOKEN, FilterOption } from '@app/core/services/search-table.service'; +import { KeycloakService } from '@app/core/services/keycloak/keycloak.service'; +import { BranchInfoWithLink } from '@app/components/branches-table/branches-table.component'; describe('Integration Test Branch List Page', () => { let component: BranchListComponent; @@ -10,7 +13,28 @@ describe('Integration Test Branch List Page', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [BranchListComponent], - providers: [importProvidersFrom(TestModule)], + providers: [ + importProvidersFrom(TestModule), + // Mock KeycloakService + { + provide: KeycloakService, + useValue: { + isLoggedIn: () => false, + getPreferredUsername: () => '', + }, + }, + // Provide FILTER_OPTIONS_TOKEN manually to match factory expectations + { + provide: FILTER_OPTIONS_TOKEN, + useFactory: (): FilterOption[] => [ + { + name: 'All Branches', + filter: (branches: BranchInfoWithLink[]) => branches, + }, + ], + deps: [KeycloakService], + }, + ], }).compileComponents(); fixture = TestBed.createComponent(BranchListComponent);