Skip to content

Commit 4caad55

Browse files
committed
add rating project for several experts & fix filters for json body
1 parent 5b3432e commit 4caad55

4 files changed

Lines changed: 154 additions & 90 deletions

File tree

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

Lines changed: 58 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -220,74 +220,48 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
220220

221221
const filtersObservable$ = this.route.queryParams
222222
.pipe(
223-
distinctUntilChanged(),
224223
concatMap(q => {
225-
const reqQuery = this.buildFilterQuery(q);
224+
const { filters, extraParams } = this.buildFilterQuery(q);
226225
const programId = this.route.parent?.snapshot.params["programId"];
227226

228-
if (JSON.stringify(reqQuery) !== JSON.stringify(this.previousReqQuery)) {
229-
this.previousReqQuery = reqQuery;
230-
231-
const hasFilters =
232-
reqQuery && reqQuery["filters"] && Object.keys(reqQuery["filters"]).length > 0;
233-
const params = new HttpParams({ fromObject: { offset: 0, limit: this.perPage } });
234-
235-
if (hasFilters) {
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-
}
227+
const filtersChanged =
228+
JSON.stringify(filters) !== JSON.stringify(this.previousReqQuery["filters"]);
229+
const extraParamsChanged =
230+
JSON.stringify(extraParams) !== JSON.stringify(this.previousReqQuery["filters"]);
231+
232+
this.previousReqQuery = { filters, extraParams };
262233

263-
if (this.listType === "projects") {
264-
return this.programService.getAllProjects(programId, params).pipe(
265-
catchError(err => {
266-
console.error("getAllProjects failed", err);
267-
return this.programService.getAllProjects(programId, params);
268-
})
269-
);
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-
);
234+
const params = new HttpParams({
235+
fromObject: {
236+
offset: 0,
237+
limit: this.itemsPerPage,
238+
...extraParams,
239+
},
240+
});
241+
242+
if (this.listType === "rating") {
243+
if (Object.keys(filters).length > 0) {
244+
return this.projectRatingService.postFilters(programId, filters, params);
277245
}
246+
247+
return this.projectRatingService.getAll(programId, params);
278248
}
279249

280-
return of(null);
250+
if (Object.keys(filters).length > 0) {
251+
return this.programService.createProgramFilters(programId, filters);
252+
}
253+
254+
return this.programService.getAllProjects(programId, params);
281255
})
282256
)
283257
.subscribe(result => {
284-
if (result && typeof result !== "number") {
285-
this.list = result.results;
286-
this.searchedList = result.results;
287-
this.listTotalCount = result.count;
288-
this.listPage = 0;
289-
this.cdref.detectChanges();
290-
}
258+
if (!result) return;
259+
260+
this.list = result.results;
261+
this.searchedList = result.results;
262+
this.listTotalCount = result.count;
263+
this.listPage = 0;
264+
this.cdref.detectChanges();
291265
});
292266

293267
this.subscriptions$.push(filtersObservable$);
@@ -389,23 +363,34 @@ export class ProgramListComponent implements OnInit, OnDestroy, AfterViewInit {
389363
if (this.listType === "members") return {};
390364

391365
const filters: Record<string, any[]> = {};
366+
const extraParams: Record<string, any> = {};
392367

393-
if (this.availableFilters.length === 0) {
394-
Object.keys(q).forEach(key => {
395-
if (key !== "search" && key !== "name__contains" && q[key] !== undefined && q[key] !== "") {
396-
filters[key] = Array.isArray(q[key]) ? q[key] : [q[key]];
397-
}
398-
});
399-
} else {
400-
this.availableFilters.forEach((filter: PartnerProgramFields) => {
401-
const value = q[filter.name];
402-
if (value !== undefined && value !== "") {
403-
filters[filter.name] = Array.isArray(value) ? value : [value];
404-
}
405-
});
406-
}
368+
Object.keys(q).forEach(key => {
369+
const value = q[key];
370+
if (value === undefined || value === "") return;
371+
372+
// ⭐ Для rating search → name__contains
373+
if (this.listType === "rating" && key === "search") {
374+
extraParams["name__contains"] = value;
375+
return;
376+
}
377+
378+
// ⭐ Эти два ключа должны идти ТОЛЬКО как GET-параметры
379+
if (this.listType === "rating" && key === "name__contains") {
380+
extraParams["name__contains"] = value;
381+
return;
382+
}
383+
384+
if (this.listType === "rating" && key === "is_rated_by_expert") {
385+
extraParams["is_rated_by_expert"] = value;
386+
return;
387+
}
388+
389+
// ⭐ Для других ключей: обычные filters
390+
filters[key] = Array.isArray(value) ? value : [value];
391+
});
407392

408-
return { filters };
393+
return { filters, extraParams };
409394
}
410395

411396
onFiltersLoaded(filters: PartnerProgramFields[]): void {

projects/social_platform/src/app/office/program/services/project-rating.service.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@ export class ProjectRatingService {
4747
return this.apiService.get(`${this.RATE_PROJECT_URL}/${programId}`, params);
4848
}
4949

50+
postFilters(
51+
programId: number,
52+
filters: Record<string, string[]>,
53+
params?: HttpParams
54+
): Observable<ApiPagination<ProjectRate>> {
55+
let url = `${this.RATE_PROJECT_URL}/${programId}`;
56+
57+
if (params) {
58+
url += `?${params.toString()}`;
59+
}
60+
61+
return this.apiService.post(url, { filters: filters });
62+
}
63+
5064
rate(projectId: number, scores: ProjectRatingCriterionOutput[]): Observable<void> {
5165
return this.apiService.post(`${this.RATE_PROJECT_URL}/rate/${projectId}`, scores);
5266
}

projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.html

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
</div>
6363

6464
<div class="card__experts">
65-
<p class="text-body-12">{{ project.ratedCount }} / {{ project.maxRates }}</p>
65+
<p class="text-body-12">{{ ratedCount() }} / {{ project.maxRates }}</p>
6666
<i appIcon icon="person" appSquare="10"></i>
6767
</div>
6868
</div>
@@ -92,19 +92,12 @@
9292
customTypographyClass="text-body-12"
9393
size="big"
9494
[loader]="submitLoading() || confirmLoading()"
95-
[color]="showRatedStatus ? 'green' : 'primary'"
96-
[disabled]="programDateFinished()"
97-
[appearance]="programDateFinished() ? 'outline' : 'inline'"
98-
[style.opacity]="showConfirmedState ? '0.5' : '1'"
99-
(click)="showRatingForm ? showConfirmRateModal.set(true) : null"
95+
[disabled]="isButtonDisabled"
96+
[style.opacity]="buttonOpacity"
97+
[color]="buttonColor"
98+
(click)="canRate ? showConfirmRateModal.set(true) : null"
10099
>
101-
{{
102-
programDateFinished()
103-
? "проект оценен"
104-
: showRatingForm
105-
? "оценить проект"
106-
: "проект оценен"
107-
}}
100+
{{ rateButtonText }}
108101
</app-button>
109102

110103
@if (showEditButton) {
@@ -115,7 +108,7 @@
115108
class="card__rated--icon"
116109
(click)="redoRating()"
117110
>
118-
<i appIcon icon="edit-pen" appSquare="10" class="card__redo-rating-icon"></i>
111+
<i appIcon icon="edit-pen" appSquare="10"></i>
119112
</app-button>
120113
}
121114
</div>

projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.ts

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy {
128128
_currentIndex = signal<number>(0);
129129
_projects = signal<ProjectRate[]>([]);
130130

131-
profile = signal<User | null>(null);
131+
profile = signal<any | null>(null);
132132

133133
form = new FormControl();
134134

@@ -148,6 +148,7 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy {
148148
locallyRatedByCurrentUser = signal(false);
149149

150150
isProjectCriterias = signal(0);
151+
ratedCount = signal(0);
151152

152153
programDateFinished = signal(false);
153154

@@ -162,6 +163,7 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy {
162163
const isScored = this.project?.scored || false;
163164
this.projectConfirmed.set(isScored);
164165
this.projectRated.set(isScored);
166+
this.ratedCount.set(this.project.ratedCount);
165167
}
166168

167169
const program$ = this.programDataService.program$
@@ -230,9 +232,31 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy {
230232
.pipe(finalize(() => this.submitLoading.set(false)))
231233
.subscribe({
232234
next: () => {
235+
const profile = this.profile();
236+
const project = this.project as ProjectRate;
237+
233238
this.locallyRatedByCurrentUser.set(true);
234239
this.projectRated.set(true);
235240
this.projectConfirmed.set(true);
241+
242+
let isFirstTimeRating = false;
243+
244+
if (profile) {
245+
if (!Array.isArray(project.ratedExperts)) {
246+
project.ratedExperts = [];
247+
}
248+
249+
if (!project.ratedExperts.includes(profile.id)) {
250+
project.ratedExperts = [...project.ratedExperts, profile.id];
251+
isFirstTimeRating = true;
252+
}
253+
}
254+
255+
if (isFirstTimeRating) {
256+
this.ratedCount.update(count => count + 1);
257+
}
258+
this._project.set({ ...project });
259+
236260
this.showConfirmRateModal.set(false);
237261
},
238262
error: err => {
@@ -276,6 +300,21 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy {
276300
return isExpertFromBackend || isExpertLocally;
277301
}
278302

303+
get canRate(): boolean {
304+
if (this.programDateFinished()) return false;
305+
306+
if (this.isLimitReached && !this.userRatedThisProject) return false;
307+
308+
return true;
309+
}
310+
311+
get rateButtonText(): string {
312+
if (this.projectConfirmed()) return "проект оценен";
313+
if (this.isLimitReached) return "проект оценен";
314+
315+
return "оценить проект";
316+
}
317+
279318
get showRatingForm(): boolean {
280319
return !this.projectRated() && this.canEdit;
281320
}
@@ -285,10 +324,43 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy {
285324
}
286325

287326
get showEditButton(): boolean {
288-
return this.showRatedStatus && this.canEdit && this.isCurrentUserExpert;
327+
return this.projectConfirmed() && !this.programDateFinished() && this.userRatedThisProject;
328+
}
329+
330+
get userRatedThisProject(): boolean {
331+
const profile = this.profile();
332+
const project = this.project;
333+
334+
if (!profile || !project) return false;
335+
336+
return (
337+
this.locallyRatedByCurrentUser() ||
338+
(Array.isArray(project.ratedExperts) && project.ratedExperts.includes(profile.id))
339+
);
340+
}
341+
342+
get isButtonDisabled(): boolean {
343+
if (this.isLimitReached && !this.userRatedThisProject) return true;
344+
345+
if (!this.canRate) return true;
346+
347+
return false;
348+
}
349+
350+
get buttonColor(): "green" | "primary" {
351+
if (this.userRatedThisProject) return "green";
352+
return "primary";
353+
}
354+
355+
get buttonOpacity(): string {
356+
return this.isButtonDisabled ? "0.5" : "1";
357+
}
358+
359+
get isLimitReached(): boolean {
360+
return !!this.project && this.project.ratedCount >= this.project.maxRates;
289361
}
290362

291363
get showConfirmedState(): boolean {
292-
return this.projectConfirmed() && !this.canEdit;
364+
return (this.projectConfirmed() && !this.canEdit) || this.isLimitReached;
293365
}
294366
}

0 commit comments

Comments
 (0)