Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @format */

import { inject, Injectable } from "@angular/core";
import { ManagementCriteriaWrite } from "@domain/program/program-criteria.model";
import { ProgramManagementRepositoryPort } from "@domain/program/ports/program-management.repository.port";
import { ManagementProjectFormWrite } from "@domain/program/program-project-form.model";
import {
Expand All @@ -13,6 +14,12 @@ import {
@Injectable()
export class ProgramManagementUseCase {
private readonly repository = inject(ProgramManagementRepositoryPort);
criteria(id: number) {
return this.repository.criteria(id);
}
saveCriteria(id: number, body: ManagementCriteriaWrite) {
return this.repository.saveCriteria(id, body);
}
projectForm(id: number) {
return this.repository.projectForm(id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ export class RateProjectUseCase {
projectId: number,
criteria: ProjectRatingCriterion[],
outputVals: Record<string, string | number | boolean>,
criteriaRevision?: string,
): Observable<Result<void, { kind: "rate_project_error"; cause?: unknown }>> {
const dto = this.projectRatingRepositoryPort.formValuesToDTO(criteria, outputVals);

return this.projectRatingRepositoryPort.rate(projectId, dto).pipe(
const request =
criteriaRevision === undefined
? this.projectRatingRepositoryPort.rate(projectId, dto)
: this.projectRatingRepositoryPort.rate(projectId, dto, criteriaRevision);
return request.pipe(
tap(() =>
this.events.emit({ type: "ProgramWidgetChanged", payload: {}, occurredAt: new Date() }),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @format */

import { Observable } from "rxjs";
import { ManagementCriteria, ManagementCriteriaWrite } from "../program-criteria.model";
import { ManagementProjectForm, ManagementProjectFormWrite } from "../program-project-form.model";
import {
ManagementEvaluation,
Expand All @@ -16,6 +17,11 @@ import {
} from "../program-management.model";

export abstract class ProgramManagementRepositoryPort {
abstract criteria(programId: number): Observable<ManagementCriteria>;
abstract saveCriteria(
programId: number,
body: ManagementCriteriaWrite,
): Observable<ManagementCriteria>;
abstract projectForm(programId: number): Observable<ManagementProjectForm>;
abstract saveProjectForm(
programId: number,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** @format */

export type CriterionType = "int" | "bool" | "str" | "float";
export interface ManagementCriterion {
id: number;
name: string;
description: string | null;
type: CriterionType;
minValue: number | null;
maxValue: number | null;
}
export interface ManagementCriteria {
updatedAt: string;
revision: string;
locked: boolean;
supportedTypes: CriterionType[];
criteria: ManagementCriterion[];
}
export interface ManagementCriteriaWrite {
expectedUpdatedAt: string;
expectedRevision: string;
criteria: (Omit<ManagementCriterion, "id"> & { id: number | null })[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type ManagementSection =
| "analytics"
| "settings"
| "evaluation";
export type SettingsSection = "basic" | "dates" | "materials" | "project-form";
export type SettingsSection = "basic" | "dates" | "materials" | "project-form" | "criteria";
export interface ManagementMaterial {
id: number;
title: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export abstract class ProjectRatingRepositoryPort {
params?: HttpParams,
): Observable<ApiPagination<ProjectRate>>;

abstract rate(projectId: number, scores: ProjectRatingCriterionOutput[]): Observable<void>;
abstract rate(
projectId: number,
scores: ProjectRatingCriterionOutput[],
criteriaRevision?: string,
): Observable<void>;

abstract formValuesToDTO(
criteria: ProjectRatingCriterion[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ProjectRatingCriterion } from "./project-rating-criterion"; // Assuming

/** Интерфейс проекта для оценки */
export interface ProjectRate {
criteriaRevision?: string;
id: number;
name: string;
leader: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @format */

/** Тип критерия оценки проекта */
export type ProjectRatingCriterionType = "bool" | "int" | "str";
export type ProjectRatingCriterionType = "bool" | "int" | "str" | "float";
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import { inject, Injectable } from "@angular/core";
import { HttpParams } from "@angular/common/http";
import { ApiService } from "@corelib";
import {
ManagementCriteria,
ManagementCriteriaWrite,
} from "@domain/program/program-criteria.model";
import {
ManagementProjectForm,
ManagementProjectFormWrite,
Expand All @@ -24,6 +28,12 @@ import {
@Injectable()
export class ProgramManagementHttpAdapter implements ProgramManagementRepositoryPort {
private readonly api = inject(ApiService);
criteria(id: number) {
return this.api.get<ManagementCriteria>(`${this.root(id)}/criteria/`);
}
saveCriteria(id: number, body: ManagementCriteriaWrite) {
return this.api.put<ManagementCriteria>(`${this.root(id)}/criteria/`, body);
}
projectForm(id: number) {
return this.api.get<ManagementProjectForm>(`${this.root(id)}/project-form/`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ export class ProjectRatingHttpAdapter {
return this.apiService.post(url, { filters });
}

rate(projectId: number, scores: ProjectRatingCriterionOutput[]): Observable<void> {
return this.apiService.post(`${this.RATE_PROJECT_URL}/rate/${projectId}`, scores);
rate(
projectId: number,
scores: ProjectRatingCriterionOutput[],
criteriaRevision?: string,
): Observable<void> {
return this.apiService.post(
`${this.RATE_PROJECT_URL}/rate/${projectId}`,
criteriaRevision === undefined ? scores : { expectedRevision: criteriaRevision, scores },
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { inject, Injectable } from "@angular/core";
import { tap } from "rxjs";
import { ManagementCriteriaWrite } from "@domain/program/program-criteria.model";
import { ManagementProjectFormWrite } from "@domain/program/program-project-form.model";
import { EventBus } from "@domain/shared/event-bus";
import { ProgramManagementRepositoryPort } from "@domain/program/ports/program-management.repository.port";
Expand All @@ -18,6 +19,12 @@ import {
export class ProgramManagementRepository implements ProgramManagementRepositoryPort {
private readonly adapter = inject(ProgramManagementHttpAdapter);
private readonly events = inject(EventBus);
criteria(id: number) {
return this.adapter.criteria(id);
}
saveCriteria(id: number, body: ManagementCriteriaWrite) {
return this.adapter.saveCriteria(id, body).pipe(tap(() => this.changed(id)));
}
projectForm(id: number) {
return this.adapter.projectForm(id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ export class ProjectRatingRepository implements ProjectRatingRepositoryPort {
return this.projectRatingAdapter.postFilters(programId, filters, params);
}

rate(projectId: number, scores: ProjectRatingCriterionOutput[]): Observable<void> {
return this.projectRatingAdapter.rate(projectId, scores);
rate(
projectId: number,
scores: ProjectRatingCriterionOutput[],
criteriaRevision?: string,
): Observable<void> {
return criteriaRevision === undefined
? this.projectRatingAdapter.rate(projectId, scores)
: this.projectRatingAdapter.rate(projectId, scores, criteriaRevision);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
<input
appField
type="number"
placeholder="Введите баллы"
[value]="value"
[min]="0"
[attr.aria-label]="label()"
[min]="min()"
[max]="max()"
[step]="1"
[step]="step()"
[disabled]="disabled"
(input)="onInput($event)"
(keydown)="onKeydown($event)"
(blur)="onBlur()"
(paste)="onPaste($event)"
(focus)="moveCursorToEnd($event)"
class="field__input text-body-10"
[class.field__input--error]="error()"
/>
<span class="text-body-10">&nbsp;/</span>
<span class="text-body-10">{{ max() }}</span>
</div>
Original file line number Diff line number Diff line change
@@ -1,70 +1,18 @@
/** @format */

@use "styles/typography";
@use "styles/responsive";
@use "styles/ui-foundation";

:host {
display: block;
min-width: 0;
}

.field {
text-wrap: nowrap;

span {
color: var(--black);
}

&__input {
width: 15px;
height: 15px;
color: transparent;
text-align: center;
text-shadow: 0 0 0 var(--black);
background-color: var(--white);
border: 0.5px solid var(--gray);
border-radius: var(--rounded-sm);
outline: none;
transition: all 0.2s;

&--error {
border-color: var(--red);
}

&--disabled {
pointer-events: none;
}

&:focus {
border-color: var(--accent);

@media screen and (max-width: 999px) {
box-shadow: var(--ui-focus-ring);
}

@media screen and (min-width: 1000px) {
box-shadow: 0 0 6px rgb(109 40 255 / 30%);
}
}
}
}

input {
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
appearance: none;
}
.field__input {
@include ui-foundation.control;

&[type="number"] {
appearance: textfield;
}
width: 100%;
min-height: 44px;
}

@media screen and (max-width: 999px) {
@include responsive.apply-mobile {
.field__input {
width: 44px;
height: 44px;
font-size: var(--ui-font-h3);
}
}
.field__input--error {
border-color: var(--red-dark);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
standalone: true,
})
export class RangeCriterionInputComponent implements ControlValueAccessor {
readonly max = input<number>(10);
readonly max = input<number | null>(10);
readonly min = input<number | null>(0);
readonly step = input<number | "any">(1);
readonly label = input<string>("Оценка");
readonly error = input<boolean>(false);

value!: number | null;
Expand All @@ -39,41 +42,36 @@ export class RangeCriterionInputComponent implements ControlValueAccessor {

onInput(event: Event): void {
const target = event.currentTarget as HTMLInputElement;
const value = target.value ? Number.parseInt(target.value) : null;
const value = target.value ? Number(target.value) : null;

this.value = value;
this.onChange(value);
}

onPaste(event: ClipboardEvent): void {
const pasteData = event.clipboardData?.getData("text/plain");
if (pasteData && pasteData.match(/[^0-9]/)) {
if (pasteData && !Number.isFinite(Number(pasteData))) {
event.preventDefault();
}
}

onKeydown(event: KeyboardEvent): void {
if (["e", "E", "+", "-"].some(char => event.key === char)) {
if (
["e", "E", "+"].includes(event.key) ||
(event.key === "-" && this.min() !== null && this.min()! >= 0)
) {
event.preventDefault();
}
}

onBlur(): void {
if (this.value) {
const val = Math.min(this.value, this.max());

this.value = val;
this.onChange(val);
}
this.onTouch();
}

// Методы ControlValueAccessor
writeValue(value: number): void {
setTimeout(() => {
this.value = value;
this.cdref.detectChanges();
});
this.value = value ?? null;
this.cdref.markForCheck();
}

onChange: (value: number | null) => void = () => {};
Expand All @@ -92,12 +90,6 @@ export class RangeCriterionInputComponent implements ControlValueAccessor {

setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled;
}

moveCursorToEnd(event: FocusEvent) {
const input = event.target as HTMLInputElement;
input.type = "text";
input.selectionStart = input.selectionEnd = input.value.length;
input.type = "number";
this.cdref.markForCheck();
}
}
Loading