Browse Source

Streamline filter actions and remove redundant code

pull/5618/head
Germán Martín 3 months ago
parent
commit
973063a5ed
  1. 16
      libs/ui/src/lib/assistant/assistant.component.ts
  2. 31
      libs/ui/src/lib/assistant/assistant.html
  3. 21
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html
  4. 69
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.spec.ts
  5. 4
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts
  6. 2
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts

16
libs/ui/src/lib/assistant/assistant.component.ts

@ -242,7 +242,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
); );
} }
// Accounts
const accounts$: Observable<Partial<ISearchResults>> = const accounts$: Observable<Partial<ISearchResults>> =
this.searchAccounts(searchTerm).pipe( this.searchAccounts(searchTerm).pipe(
map((accounts) => ({ map((accounts) => ({
@ -261,7 +260,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
// Asset profiles
const assetProfiles$: Observable<Partial<ISearchResults>> = this const assetProfiles$: Observable<Partial<ISearchResults>> = this
.hasPermissionToAccessAdminControl .hasPermissionToAccessAdminControl
? this.searchAssetProfiles(searchTerm).pipe( ? this.searchAssetProfiles(searchTerm).pipe(
@ -290,7 +288,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
// Holdings
const holdings$: Observable<Partial<ISearchResults>> = const holdings$: Observable<Partial<ISearchResults>> =
this.searchHoldings(searchTerm).pipe( this.searchHoldings(searchTerm).pipe(
map((holdings) => ({ map((holdings) => ({
@ -309,7 +306,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
// Quick links
const quickLinks$: Observable<Partial<ISearchResults>> = of( const quickLinks$: Observable<Partial<ISearchResults>> = of(
this.searchQuickLinks(searchTerm) this.searchQuickLinks(searchTerm)
).pipe( ).pipe(
@ -325,7 +321,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}) })
); );
// Merge all results
return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe( return merge(accounts$, assetProfiles$, holdings$, quickLinks$).pipe(
scan( scan(
(acc: ISearchResults, curr: Partial<ISearchResults>) => ({ (acc: ISearchResults, curr: Partial<ISearchResults>) => ({
@ -365,11 +360,9 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
} }
public ngOnChanges() { public ngOnChanges() {
// Use accountsWithValue if provided, otherwise transform user.accounts as fallback
if (this.accountsWithValue?.length > 0) { if (this.accountsWithValue?.length > 0) {
this.accounts = this.accountsWithValue; this.accounts = this.accountsWithValue;
} else { } else {
// Transform basic accounts to AccountWithValue format for compatibility
this.accounts = (this.user?.accounts ?? []).map((account) => ({ this.accounts = (this.user?.accounts ?? []).map((account) => ({
...account, ...account,
allocationInPercentage: 0, allocationInPercentage: 0,
@ -379,7 +372,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
platform: account.platformId platform: account.platformId
? { ? {
id: account.platformId, id: account.platformId,
name: account.platformId, // Fallback, ideally should be resolved name: account.platformId,
url: '' url: ''
} }
: undefined, : undefined,
@ -566,12 +559,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
} }
public onResetFilters() { public onResetFilters() {
this.portfolioFilterFormControl.setValue({ this.portfolioFilterFormControl.reset();
account: null,
assetClass: null,
holding: null,
tag: null
});
this.filtersChanged.emit( this.filtersChanged.emit(
this.filterTypes.map((type) => { this.filterTypes.map((type) => {

31
libs/ui/src/lib/assistant/assistant.html

@ -180,6 +180,7 @@
</div> </div>
<div class="p-3"> <div class="p-3">
<gf-portfolio-filter-form <gf-portfolio-filter-form
#portfolioFilterForm
[accounts]="accounts" [accounts]="accounts"
[assetClasses]="assetClasses" [assetClasses]="assetClasses"
[disabled]="!hasPermissionToChangeFilters" [disabled]="!hasPermissionToChangeFilters"
@ -188,7 +189,35 @@
[tags]="tags" [tags]="tags"
(applyFilters)="onApplyFilters()" (applyFilters)="onApplyFilters()"
(resetFilters)="onResetFilters()" (resetFilters)="onResetFilters()"
/> >
<div class="d-flex w-100" gfPortfolioFilterActions>
<button
i18n
mat-button
type="button"
[disabled]="
!portfolioFilterForm.hasFilters() || portfolioFilterForm.disabled
"
(click)="portfolioFilterForm.onResetFilters()"
>
Reset Filters
</button>
<span class="gf-spacer"></span>
<button
color="primary"
i18n
mat-flat-button
type="button"
[disabled]="
!portfolioFilterForm.filterForm.dirty ||
portfolioFilterForm.disabled
"
(click)="portfolioFilterForm.onApplyFilters()"
>
Apply Filters
</button>
</div>
</gf-portfolio-filter-form>
</div> </div>
} }
</div> </div>

21
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html

@ -72,24 +72,5 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="d-flex w-100"> <ng-content select="[gfPortfolioFilterActions]"></ng-content>
<button
i18n
mat-button
[disabled]="!hasFilters() || disabled"
(click)="onResetFilters()"
>
Reset Filters
</button>
<span class="gf-spacer"></span>
<button
color="primary"
i18n
mat-flat-button
[disabled]="!filterForm.dirty || disabled"
(click)="onApplyFilters()"
>
Apply Filters
</button>
</div>
</form> </form>

69
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.spec.ts

@ -1,69 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { GfPortfolioFilterFormComponent } from './portfolio-filter-form.component';
// Mock $localize for testing
(global as any).$localize = (template: any) => {
return template.raw ? template.raw.join('') : template;
};
describe('GfPortfolioFilterFormComponent', () => {
let component: GfPortfolioFilterFormComponent;
let fixture: ComponentFixture<GfPortfolioFilterFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
GfPortfolioFilterFormComponent,
MatButtonModule,
MatFormFieldModule,
MatSelectModule,
NoopAnimationsModule,
ReactiveFormsModule
]
}).compileComponents();
fixture = TestBed.createComponent(GfPortfolioFilterFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should initialize with empty form values', () => {
expect(component.filterForm.value).toEqual({
account: null,
assetClass: null,
holding: null,
tag: null
});
});
it('should detect when filters are applied', () => {
component.filterForm.patchValue({ account: 'test-account-id' });
expect(component.hasFilters()).toBeTruthy();
});
it('should detect when no filters are applied', () => {
expect(component.hasFilters()).toBeFalsy();
});
it('should emit resetFilters event when onResetFilters is called', () => {
jest.spyOn(component.resetFilters, 'emit');
component.onResetFilters();
expect(component.resetFilters.emit).toHaveBeenCalled();
});
it('should emit applyFilters event when onApplyFilters is called', () => {
jest.spyOn(component.applyFilters, 'emit');
component.onApplyFilters();
expect(component.applyFilters.emit).toHaveBeenCalled();
});
});

4
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts

@ -36,9 +36,9 @@ export const Default: Story = {
} }
] as any, ] as any,
assetClasses: [ assetClasses: [
{ id: 'COMMODITY', label: 'Commodity', type: 'ASSET_CLASS' },
{ id: 'EQUITY', label: 'Equity', type: 'ASSET_CLASS' }, { id: 'EQUITY', label: 'Equity', type: 'ASSET_CLASS' },
{ id: 'FIXED_INCOME', label: 'Fixed Income', type: 'ASSET_CLASS' }, { id: 'FIXED_INCOME', label: 'Fixed Income', type: 'ASSET_CLASS' }
{ id: 'COMMODITY', label: 'Commodity', type: 'ASSET_CLASS' }
] as any, ] as any,
holdings: [ holdings: [
{ {

2
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts

@ -25,7 +25,6 @@ import {
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
ReactiveFormsModule ReactiveFormsModule
} from '@angular/forms'; } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -39,7 +38,6 @@ import { PortfolioFilterFormValue } from './interfaces';
FormsModule, FormsModule,
GfEntityLogoComponent, GfEntityLogoComponent,
GfSymbolPipe, GfSymbolPipe,
MatButtonModule,
MatFormFieldModule, MatFormFieldModule,
MatSelectModule, MatSelectModule,
ReactiveFormsModule ReactiveFormsModule

Loading…
Cancel
Save