Skip to content

Commit 3423f47

Browse files
committed
add filters for rating section & unify projects-filter component for rating & projects sections
1 parent 6f3e6f9 commit 3423f47

11 files changed

Lines changed: 171 additions & 136 deletions

File tree

projects/social_platform/src/app/office/program/detail/list/list.component.html

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</div>
1414

1515
<div class="filter-toggle page__filter-toggle" (click)="isFilterOpen = !isFilterOpen">
16-
<span>Фильтр</span>
16+
<span>Фильтры</span>
1717
<i appIcon icon="filter" appSquare="34"></i>
1818
</div>
1919

@@ -40,7 +40,7 @@
4040
@if (listType !== 'members') {
4141
<div class="page__create">
4242
<div class="page__left">
43-
@if (listType === 'projects') {
43+
@if (listType === 'projects' || listType === 'rating') {
4444
<div class="filter page__filter" [class.page__filter--open]="isFilterOpen">
4545
<div class="filter__overlay" (click)="isFilterOpen = false"></div>
4646
<div class="filter__body" #filterBody>
@@ -50,19 +50,13 @@
5050
(touchmove)="onSwipeMove($event)"
5151
(touchend)="onSwipeEnd($event)"
5252
></div>
53-
<app-projects-filter (closeFilter)="closeFilter()"></app-projects-filter>
53+
<app-projects-filter
54+
[listType]="listType"
55+
(closeFilter)="closeFilter()"
56+
(clear)="onClearFilters()"
57+
></app-projects-filter>
5458
</div>
5559
</div>
56-
} @else {
57-
<form class="filter__form" [formGroup]="filterForm">
58-
<p class="text-body-12">фильтр</p>
59-
<div class="filter__controls">
60-
<label class="filter__tags" (click)="setValue($event)">
61-
<app-checkbox [checked]="this.filterForm.get('filterTag')?.value"></app-checkbox>
62-
<p class="text-body-12">проекты с оценкой</p>
63-
</label>
64-
</div>
65-
</form>
6660
}
6761
</div>
6862
</div>

projects/social_platform/src/app/office/program/detail/list/list.component.scss

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -87,23 +87,6 @@
8787
}
8888
}
8989

90-
&__controls {
91-
display: flex;
92-
flex-direction: column;
93-
gap: 14px;
94-
margin-top: 18px;
95-
}
96-
97-
&__tags {
98-
display: flex;
99-
gap: 12px;
100-
align-items: center;
101-
102-
p {
103-
color: var(--grey-for-text);
104-
}
105-
}
106-
10790
&__bar {
10891
position: fixed;
10992
display: flex;

projects/social_platform/src/app/office/program/detail/list/list.component.ts

Lines changed: 75 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,12 @@ import { tagsFilter } from "projects/core/src/consts/filters/tags-filter.const";
5757
ProjectsFilterComponent,
5858
SearchComponent,
5959
RatingCardComponent,
60-
CheckboxComponent,
6160
InfoCardComponent,
6261
],
6362
standalone: true,
6463
})
6564
export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
6665
constructor() {
67-
const isRatedByExpert =
68-
this.route.snapshot.queryParams["is_rated_by_expert"] === "true"
69-
? true
70-
: this.route.snapshot.queryParams["is_rated_by_expert"] === "false"
71-
? false
72-
: null;
73-
7466
const searchValue =
7567
this.route.snapshot.queryParams["search"] ||
7668
this.route.snapshot.queryParams["name__contains"];
@@ -79,10 +71,6 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
7971
this.searchForm = this.fb.group({
8072
search: [decodedSearchValue],
8173
});
82-
83-
this.filterForm = this.fb.group({
84-
filterTag: [isRatedByExpert, Validators.required],
85-
});
8674
}
8775

8876
@ViewChild("listRoot") listRoot?: ElementRef<HTMLUListElement>;
@@ -102,7 +90,6 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
10290
private availableFilters: PartnerProgramFields[] = [];
10391

10492
searchForm: FormGroup;
105-
filterForm: FormGroup;
10693

10794
listTotalCount?: number;
10895
listPage = 0;
@@ -157,10 +144,6 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
157144
}
158145

159146
this.setupFilters();
160-
161-
if (this.listType === "rating") {
162-
this.setupRatingQueryParams();
163-
}
164147
}
165148

166149
ngAfterViewInit(): void {
@@ -233,7 +216,7 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
233216
}
234217

235218
private setupFilters(): void {
236-
if (this.listType !== "projects") return;
219+
if (this.listType === "members") return;
237220

238221
const filtersObservable$ = this.route.queryParams
239222
.pipe(
@@ -250,20 +233,48 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
250233
const params = new HttpParams({ fromObject: { offset: 0, limit: this.perPage } });
251234

252235
if (hasFilters) {
253-
return this.programService.createProgramFilters(programId, reqQuery["filters"]).pipe(
236+
if (this.listType === "projects") {
237+
return this.programService
238+
.createProgramFilters(programId, reqQuery["filters"])
239+
.pipe(
240+
catchError(err => {
241+
console.error("createFilters failed, fallback to getAllProjects()", err);
242+
return this.programService.getAllProjects(programId, params);
243+
})
244+
);
245+
} else if (this.listType === "rating") {
246+
const filterParams = new HttpParams({
247+
fromObject: {
248+
offset: 0,
249+
limit: this.perPage,
250+
...this.flattenFilters(reqQuery["filters"]),
251+
},
252+
});
253+
254+
return this.projectRatingService.getAll(programId, filterParams).pipe(
255+
catchError(err => {
256+
console.error("getAllRatingProjects failed", err);
257+
return this.projectRatingService.getAll(programId, params);
258+
})
259+
);
260+
}
261+
}
262+
263+
if (this.listType === "projects") {
264+
return this.programService.getAllProjects(programId, params).pipe(
254265
catchError(err => {
255-
console.error("createFilters failed, fallback to getAllProjects()", err);
266+
console.error("getAllProjects failed", err);
256267
return this.programService.getAllProjects(programId, params);
257268
})
258269
);
270+
} else if (this.listType === "rating") {
271+
return this.projectRatingService.getAll(programId, params).pipe(
272+
catchError(err => {
273+
console.error("getAllRatingProjects failed", err);
274+
return this.projectRatingService.getAll(programId, params);
275+
})
276+
);
259277
}
260-
261-
return this.programService.getAllProjects(programId, params).pipe(
262-
catchError(err => {
263-
console.error("getAllProjects failed", err);
264-
return this.programService.getAllProjects(programId, params);
265-
})
266-
);
267278
}
268279

269280
return of(null);
@@ -282,44 +293,6 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
282293
this.subscriptions$.push(filtersObservable$);
283294
}
284295

285-
private setupRatingQueryParams(): void {
286-
const queryParams$ = this.route.queryParams
287-
.pipe(
288-
debounceTime(200),
289-
tap(params => {
290-
const isRatedByExpert =
291-
params["is_rated_by_expert"] === "true"
292-
? true
293-
: params["is_rated_by_expert"] === "false"
294-
? false
295-
: undefined;
296-
const searchValue = params["name__contains"] || "";
297-
298-
this.isRatedByExpert.set(isRatedByExpert);
299-
this.searchValue.set(searchValue);
300-
}),
301-
switchMap(() => {
302-
this.listPage = 0;
303-
return this.onFetch();
304-
})
305-
)
306-
.subscribe();
307-
308-
this.subscriptions$.push(queryParams$);
309-
}
310-
311-
// Методы фильтрации
312-
setValue(event: Event): void {
313-
event.stopPropagation();
314-
this.filterForm.get("filterTag")?.setValue(!this.filterForm.get("filterTag")?.value);
315-
316-
this.router.navigate([], {
317-
queryParams: { is_rated_by_expert: this.filterForm.get("filterTag")?.value },
318-
relativeTo: this.route,
319-
queryParamsHandling: "merge",
320-
});
321-
}
322-
323296
// Универсальный метод скролла
324297
private onScroll() {
325298
if (this.listTotalCount && this.list.length >= this.listTotalCount) return of({});
@@ -392,7 +365,7 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
392365

393366
case "rating":
394367
return this.projectRatingService
395-
.getAll(programId, offset, this.itemsPerPage, this.isRatedByExpert(), this.searchValue())
368+
.getAll(programId, new HttpParams({ fromObject: { offset, limit: this.itemsPerPage } }))
396369
.pipe(
397370
tap(({ count, results }) => {
398371
this.listTotalCount = count;
@@ -411,15 +384,15 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
411384
}
412385
}
413386

414-
// Построение запроса для фильтров (только для проектов)
387+
// Построение запроса для фильтров (кроме участников)
415388
private buildFilterQuery(q: any): Record<string, any> {
416-
if (this.listType !== "projects") return {};
389+
if (this.listType === "members") return {};
417390

418391
const filters: Record<string, any[]> = {};
419392

420393
if (this.availableFilters.length === 0) {
421394
Object.keys(q).forEach(key => {
422-
if (key !== "search" && q[key] !== undefined && q[key] !== "") {
395+
if (key !== "search" && key !== "name__contains" && q[key] !== undefined && q[key] !== "") {
423396
filters[key] = Array.isArray(q[key]) ? q[key] : [q[key]];
424397
}
425398
});
@@ -482,6 +455,24 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
482455
this.isFilterOpen = false;
483456
}
484457

458+
/**
459+
* Сброс всех активных фильтров
460+
* Очищает все query параметры и возвращает к состоянию по умолчанию
461+
*/
462+
onClearFilters(): void {
463+
this.searchForm.reset();
464+
465+
this.router
466+
.navigate([], {
467+
queryParams: {
468+
search: undefined,
469+
},
470+
relativeTo: this.route,
471+
queryParamsHandling: "merge",
472+
})
473+
.then(() => console.log("Query change from ProjectsComponent"));
474+
}
475+
485476
private get itemsPerPage(): number {
486477
return this.listType === "rating"
487478
? 8
@@ -493,4 +484,19 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
493484
private get searchParamName(): string {
494485
return this.listType === "rating" ? "name__contains" : "search";
495486
}
487+
488+
private flattenFilters(filters: Record<string, any[]>): Record<string, string> {
489+
const flattened: Record<string, string> = {};
490+
491+
Object.keys(filters).forEach(key => {
492+
const value = filters[key];
493+
if (Array.isArray(value) && value.length > 0) {
494+
flattened[key] = Array.isArray(value[0]) ? value.join(",") : value.toString();
495+
} else if (value !== undefined && value !== null) {
496+
flattened[key] = value.toString();
497+
}
498+
});
499+
500+
return flattened;
501+
}
496502
}

projects/social_platform/src/app/office/program/detail/list/projects-filter/projects-filter.component.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- @format -->
22
<div class="filter__titles">
33
<p class="text-body-14">фильтры</p>
4-
<!-- <a class="text-body-12 filter__clear" (click)="clearFilters()">Сбросить фильтры</a> -->
4+
<a class="text-body-12 filter__clear" (click)="clearFilters()">cбросить</a>
55
</div>
66

77
@if (filters()?.length) {
@@ -52,7 +52,12 @@
5252
</div>
5353
} }
5454
</div>
55-
} } }
55+
} } } @if (listType === 'rating') {
56+
<label class="filter__tags" (click)="setValue($event)">
57+
<app-checkbox [checked]="this.filterForm.get('is_rated_by_expert')?.value"></app-checkbox>
58+
<p class="text-body-12">проекты с оценкой</p>
59+
</label>
60+
}
5661
</div>
5762
</div>
5863
}

projects/social_platform/src/app/office/program/detail/list/projects-filter/projects-filter.component.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@
3434
flex-direction: column;
3535
}
3636

37+
&__tags {
38+
display: flex;
39+
gap: 12px;
40+
align-items: center;
41+
42+
p {
43+
color: var(--grey-for-text);
44+
}
45+
}
46+
3747
&__titles {
3848
display: flex;
3949
align-items: center;

0 commit comments

Comments
 (0)