Skip to content

Commit

Permalink
participating user and org selects populate options with full search
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcmichael committed Oct 30, 2024
1 parent 92b5c18 commit 3827c8b
Show file tree
Hide file tree
Showing 11 changed files with 389 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,39 +114,10 @@
filterTitle;
context: { $implicit: 'Participating User', value: userId() }
"></ng-container>
<nz-select
nzPlaceHolder="Select User"
nzMode="multiple"
[ngModel]="userId()"
(ngModelChange)="userId.set($event)"
[nzCustomTemplate]="userLabel"
[nzOptionOverflowSize]="12"
[nzOptionHeightPx]="28">
<nz-option
*ngFor="let user of cvcFilterOptions().uniqueParticipants"
nzCustomContent
[nzLabel]="user.displayName"
[nzValue]="user.id">
<ng-container
*ngTemplateOutlet="
userLabel;
context: {
$implicit: { nzLabel: user.displayName, nzValue: user.id },
}
">
</ng-container>
</nz-option>
<ng-template
#userLabel
let-selected>
<span
nz-icon
[nzType]="'civic-curator'"
nzTheme="twotone"
[nzTwotoneColor]="'Curator' | entityColor"></span>
{{ selected.nzLabel }}
</ng-template>
</nz-select>
<cvc-user-filter-select
[cvcUniqueParticipants]="cvcFilterOptions().uniqueParticipants"
[cvcUserId]="userId()"
(cvcUserIdChange)="userId.set($event)"></cvc-user-filter-select>
</nz-col>
}

Expand All @@ -160,43 +131,14 @@
value: organizationId(),
}
"></ng-container>
<nz-select
nzPlaceHolder="Select Organization"
nzMode="multiple"
[ngModel]="organizationId()"
(ngModelChange)="organizationId.set($event)"
[nzCustomTemplate]="organizationLabel"
[nzOptionHeightPx]="28">
<nz-option
*ngFor="
let organization of cvcFilterOptions().participatingOrganizations
"
nzCustomContent
[nzLabel]="organization.name"
[nzValue]="organization.id">
<ng-container
*ngTemplateOutlet="
organizationLabel;
context: {
$implicit: {
nzLabel: organization.name,
nzValue: organization.id,
},
}
">
</ng-container>
</nz-option>
<ng-template
#organizationLabel
let-selected>
<span
nz-icon
[nzType]="'civic-organization'"
nzTheme="twotone"
[nzTwotoneColor]="'Organization' | entityColor"></span>
{{ selected.nzLabel }}
</ng-template>
</nz-select>
<cvc-org-filter-select
[cvcParticipatingOrganizations]="
cvcFilterOptions().participatingOrganizations
"
[cvcOrganizationId]="organizationId()"
(cvcOrganizationIdChange)="
organizationId.set($event)
"></cvc-org-filter-select>
</nz-col>
}
<nz-col
Expand Down Expand Up @@ -237,11 +179,15 @@ <h4>Sort Direction</h4>
</nz-option>
</nz-select>
</nz-col>
<!-- <nz-col nzSpan="24">
<!-- <nz-col
nzSpan="24"
style="max-height: 200px; overflow-y: auto">
<h4>Filters</h4>
<pre>{{ cvcFilters() | json }}</pre>
<h4>Filter Options</h4>
<pre>{{ cvcFilterOptions() | json }}</pre>
<h4>
Unique Participants ({{ cvcFilterOptions().uniqueParticipants.length }})$
</h4>
<pre>{{ cvcFilterOptions().uniqueParticipants | json }}</pre>
</nz-col> -->
</nz-row>
<!-- TEMPLATES -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import {
DateSortColumns,
Maybe,
SortDirection,
UserFilterSearchGQL,
UserFilterSearchQuery,
UserFilterSearchQueryVariables,
} from '@app/generated/civic.apollo'
import { CommonModule, KeyValuePipe } from '@angular/common'
import { FormsModule } from '@angular/forms'
Expand All @@ -40,12 +43,19 @@ import { disableDates } from '../activity-feed.functions'
import { toObservable, toSignal } from '@angular/core/rxjs-interop'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { tag } from 'rxjs-spy/operators'
import { distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators'
import { timer, filter, of } from 'rxjs'
import { isNonNullObject } from '@apollo/client/utilities'
import {
distinctUntilChanged,
map,
skip,
startWith,
switchMap,
} from 'rxjs/operators'
import { timer, filter, of, Subject, from } from 'rxjs'
import { isNonNulled } from 'rxjs-etc'
import { NzButtonModule } from 'ng-zorro-antd/button'
import { NzAlertModule } from 'ng-zorro-antd/alert'
import { CvcUserFilterSelect } from './user-filter-select/user-filter-select.component'
import { CvcOrgFilterSelect } from './org-filter-select/org-filter-select.component'

export const defaultFilters = {}

Expand All @@ -65,6 +75,8 @@ export const defaultFilters = {}
NzSelectModule,
NzDatePickerModule,
CvcPipesModule,
CvcUserFilterSelect,
CvcOrgFilterSelect,
],
templateUrl: './feed-filters.component.html',
styleUrls: ['./feed-filters.component.less'],
Expand Down Expand Up @@ -118,7 +130,17 @@ export class CvcActivityFeedFilterSelects implements OnInit {
sortByDirection: this.sortByDirection(),
})
})

/**
* Observable that emits the count of new activities since the last refresh.
*
* Behavior:
* - Only activates if cvcCheckInterval > 0, else emits 0
* - Polls the API at the specified interval (cvcCheckInterval in seconds)
* - Uses the current filter settings but only looks for activities after the last refresh
* - Emits 0 initially and then the count of new activities
* - Only emits when the count changes (uses distinctUntilChanged)
* - Resets to 0 when filters change via cvcRefreshChanges
*/
const newActivities$ = toObservable(this.cvcRefreshChanges).pipe(
filter(isNonNulled),
switchMap((refetchEvent) => {
Expand Down Expand Up @@ -151,6 +173,7 @@ export class CvcActivityFeedFilterSelects implements OnInit {
)
this.newActivities = toSignal(newActivities$, { initialValue: 0 })
}

ngOnInit(): void {
this.eventType = signal(this.cvcFilters().activityType)
this.subjectType = signal(this.cvcFilters().subjectType)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<nz-select
nzPlaceHolder="Select Organization"
nzMode="multiple"
nzAllowClear
[(ngModel)]="cvcOrganizationId"
(nzOnSearch)="onSearch$.next($event)"
[nzCustomTemplate]="organizationLabel"
[nzNotFoundContent]="notFound"
[nzOptionHeightPx]="28">
<nz-option
*ngFor="let organization of filteredOrganizations()"
nzCustomContent
[nzLabel]="organization.name"
[nzValue]="organization.id">
<ng-container
*ngTemplateOutlet="
organizationLabel;
context: {
$implicit: {
nzLabel: organization.name,
nzValue: organization.id,
},
}
">
</ng-container>
</nz-option>
</nz-select>

<ng-template
#organizationLabel
let-selected>
<span
nz-icon
[nzType]="'civic-organization'"
nzTheme="twotone"
[nzTwotoneColor]="'Organization' | entityColor"></span>
{{ selected.nzLabel }}
</ng-template>

<ng-template #notFound>
<span>No organizationsfound matching "{{ onSearch() }}"</span>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Component, input, model, Signal } from '@angular/core'
import { ActivityFeedFilterOptions } from '../../activity-feed.types'
import { CvcPipesModule } from '@app/core/pipes/pipes.module'
import { CommonModule } from '@angular/common'
import { NzSelectModule } from 'ng-zorro-antd/select'
import { NzIconModule } from 'ng-zorro-antd/icon'
import { FormsModule } from '@angular/forms'
import {
BrowseOrganization,
Organization,
OrgFilterSearchGQL,
OrgFilterSearchQuery,
OrgFilterSearchQueryVariables,
} from '@app/generated/civic.apollo'
import { from, map, Subject, switchMap } from 'rxjs'
import { QueryRef } from 'apollo-angular'
import { tag } from 'rxjs-spy/operators'
import { toSignal } from '@angular/core/rxjs-interop'

@Component({
selector: 'cvc-org-filter-select',
standalone: true,
imports: [
CommonModule,
FormsModule,
NzIconModule,
NzSelectModule,
CvcPipesModule,
],
templateUrl: './org-filter-select.component.html',
styleUrl: './org-filter-select.component.less',
})
export class CvcOrgFilterSelect {
cvcParticipatingOrganizations =
input.required<ActivityFeedFilterOptions['participatingOrganizations']>()
cvcOrganizationId = model.required<number[]>()

onSearch$: Subject<string>
onSearch: Signal<string>
queryRef!: QueryRef<OrgFilterSearchQuery, OrgFilterSearchQueryVariables>
filteredOrganizations: Signal<BrowseOrganization[]>

constructor(private gql: OrgFilterSearchGQL) {
this.onSearch$ = new Subject<string>()
const filteredOrganizations$ = this.onSearch$.pipe(
tag(`filteredOrganizations$`),
switchMap((nameStr) => {
const query = { name: nameStr, first: 25 }
if (this.queryRef) {
const refetch = this.queryRef.refetch({ name: nameStr })
return from(refetch)
} else {
this.queryRef = this.gql.watch({ name: nameStr })
return this.queryRef.valueChanges
}
}),
map(
(result) =>
result.data?.organizations.edges.map(
(e) => e.node! as BrowseOrganization
) ?? []
),
tag(`${this.constructor.name} filteredOrganizations$ after`)
)
this.filteredOrganizations = toSignal(filteredOrganizations$, {
initialValue: [],
})
this.onSearch = toSignal(this.onSearch$, { initialValue: '' })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
query OrgFilterSearch($name: String) {
organizations(name: $name) {
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
edges {
node {
id
name
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<nz-select
nzPlaceHolder="Select User"
nzMode="multiple"
nzAllowClear
[(ngModel)]="cvcUserId"
(nzOnSearch)="onSearch$.next($event)"
[nzCustomTemplate]="userLabel"
[nzNotFoundContent]="notFound"
[nzOptionOverflowSize]="12"
[nzOptionHeightPx]="28">
<nz-option
*ngFor="let user of filteredUsers()"
nzCustomContent
[nzLabel]="user.displayName"
[nzValue]="user.id">
<ng-container
*ngTemplateOutlet="
userLabel;
context: {
$implicit: {
nzLabel: user.displayName ?? user.name ?? user.username,
nzValue: user.id,
},
}
">
</ng-container>
</nz-option>
</nz-select>

<ng-template
#userLabel
let-selected>
<span
nz-icon
[nzType]="'civic-curator'"
nzTheme="twotone"
[nzTwotoneColor]="'Curator' | entityColor"></span>
{{ selected.nzLabel }}
</ng-template>

<ng-template #notFound>
<span>No users found matching "{{ onSearch() }}"</span>
</ng-template>
Loading

0 comments on commit 3827c8b

Please sign in to comment.