diff --git a/projects/core/src/consts/filters/feed-filter.const.ts b/projects/core/src/consts/filters/feed-filter.const.ts index cfb85b78f..8c62b63de 100644 --- a/projects/core/src/consts/filters/feed-filter.const.ts +++ b/projects/core/src/consts/filters/feed-filter.const.ts @@ -4,7 +4,7 @@ export const feedFilter = [ { id: 1, name: "новости проектов", - value: "projects", + value: "project", icon: "projects", }, { diff --git a/projects/skills/src/app/app.component.scss b/projects/skills/src/app/app.component.scss index 02e36e5f5..056326355 100644 --- a/projects/skills/src/app/app.component.scss +++ b/projects/skills/src/app/app.component.scss @@ -4,6 +4,7 @@ .app { display: flex; height: 100%; + padding: 20px; background-color: var(--light-gray); &__wrapper { diff --git a/projects/skills/src/app/profile/profile.component.ts b/projects/skills/src/app/profile/profile.component.ts index 031905722..1cb0df6c6 100644 --- a/projects/skills/src/app/profile/profile.component.ts +++ b/projects/skills/src/app/profile/profile.component.ts @@ -7,6 +7,7 @@ import { InfoBlockComponent } from "./shared/info-block/info-block.component"; import { toSignal } from "@angular/core/rxjs-interop"; import { map } from "rxjs"; import { BarComponent } from "@ui/components"; +import { BarNewComponent } from "@ui/components/bar-new/bar.component"; /** * Основной компонент профиля пользователя @@ -20,7 +21,7 @@ import { BarComponent } from "@ui/components"; @Component({ selector: "app-profile", standalone: true, - imports: [CommonModule, RouterOutlet, InfoBlockComponent, BarComponent], + imports: [CommonModule, RouterOutlet, InfoBlockComponent, BarComponent, BarNewComponent], templateUrl: "./profile.component.html", styleUrl: "./profile.component.scss", }) diff --git a/projects/skills/src/app/profile/shared/info-block/info-block.component.html b/projects/skills/src/app/profile/shared/info-block/info-block.component.html index 86bddefb7..0cef2c8a4 100644 --- a/projects/skills/src/app/profile/shared/info-block/info-block.component.html +++ b/projects/skills/src/app/profile/shared/info-block/info-block.component.html @@ -41,8 +41,8 @@

{{ userData.firstName }} {{ userData.lastName }}

{{ userData.points }} {{ userData.points | pluralize: ["балл", "балла", "баллов"] }} - Вернуться на procollab.ruвернуться на procollab.ru diff --git a/projects/skills/src/app/shared/task-card/task-card.component.html b/projects/skills/src/app/shared/task-card/task-card.component.html index d56ad592d..4b5ed00e4 100644 --- a/projects/skills/src/app/shared/task-card/task-card.component.html +++ b/projects/skills/src/app/shared/task-card/task-card.component.html @@ -15,7 +15,7 @@

{{ task.name }}

@if (!status?.isDone) { - + } @else { diff --git a/projects/skills/src/app/task/subtask/subtask.component.html b/projects/skills/src/app/task/subtask/subtask.component.html index e21b147a6..f30dd70e7 100644 --- a/projects/skills/src/app/task/subtask/subtask.component.html +++ b/projects/skills/src/app/task/subtask/subtask.component.html @@ -38,7 +38,7 @@ } } Продолжитьпродолжить wave
@@ -75,7 +75,7 @@ } } Продолжитьпродолжить wave @@ -122,7 +122,7 @@ } } Продолжитьпродолжить wave @@ -169,7 +169,7 @@ } } Продолжитьпродолжить wave @@ -216,7 +216,7 @@ } } Продолжитьпродолжить wave @@ -226,13 +226,15 @@ }
+ продолжить +
- Вышла неточность... +

вышла неточность...

+
- Всё верно! Так держать +

всё верно! так держать

- Продолжить
} diff --git a/projects/skills/src/app/task/subtask/subtask.component.scss b/projects/skills/src/app/task/subtask/subtask.component.scss index 89c9bb739..efbd9d8cb 100644 --- a/projects/skills/src/app/task/subtask/subtask.component.scss +++ b/projects/skills/src/app/task/subtask/subtask.component.scss @@ -16,6 +16,7 @@ .action { position: relative; + margin-top: 20px; &__button { position: relative; @@ -29,13 +30,16 @@ bottom: 0; left: 0; display: flex; + display: none; justify-content: center; - padding: 15px 10px 0; + padding: 12px 12px 35px; + text-align: center; border-radius: var(--rounded-xl); transition: all 0.2s; transform: translateY(0%); &--open { + display: block; transform: translateY(-80%); } } diff --git a/projects/skills/src/app/webinars/shared/webinar/webinar.component.html b/projects/skills/src/app/webinars/shared/webinar/webinar.component.html index d74ad4fbb..307c63c4a 100644 --- a/projects/skills/src/app/webinars/shared/webinar/webinar.component.html +++ b/projects/skills/src/app/webinars/shared/webinar/webinar.component.html @@ -12,10 +12,10 @@

{{ webinar.title }}

>

@if (descriptionExpandable) {
- {{ readFullDescription ? "Скрыть" : "Читать полностью" }} + {{ readFullDescription ? "скрыть" : "подробнее" }}
} diff --git a/projects/skills/src/styles/_colors.scss b/projects/skills/src/styles/_colors.scss index 4e52a6ba6..c30eec8b2 100644 --- a/projects/skills/src/styles/_colors.scss +++ b/projects/skills/src/styles/_colors.scss @@ -17,6 +17,7 @@ // GRAY --white: #fafafa; + --light-white: #fff; --black: #333; --dark-grey: #e7e7e7; --gray: #d3d3d3; @@ -27,9 +28,9 @@ --grey-for-text: #a59fb9; // FUNCTIONAL - --green: #88c9a1; - --light-green: #97ecb8; - --red: #d48a9e; + --green: #92e3a9; + --light-green: #e3f8e9; + --red: rgb(242 66 72 / 50%); + --light-red: #ffd2d2; --red-dark: #{color.adjust(#d48a9e, $blackness: 10%)}; - --light-red: #e8a5b7; } diff --git a/projects/skills/src/styles/_rounded.scss b/projects/skills/src/styles/_rounded.scss index 1ea5819a8..b2602fac5 100644 --- a/projects/skills/src/styles/_rounded.scss +++ b/projects/skills/src/styles/_rounded.scss @@ -5,4 +5,5 @@ --rounded-md: 5px; --rounded-lg: 8px; --rounded-xl: 15px; + --rounded-xxl: 45px; } diff --git a/projects/social_platform/src/app/office/features/detail/detail.component.html b/projects/social_platform/src/app/office/features/detail/detail.component.html index d354d0abc..56e54c261 100644 --- a/projects/social_platform/src/app/office/features/detail/detail.component.html +++ b/projects/social_platform/src/app/office/features/detail/detail.component.html @@ -101,12 +101,13 @@ } } @if (isUserMember && !isUserManager && !isUserExpert) { - подать проект + {{ isProjectAssigned ? "вы подали проект" : "подать проект" }} } @if ((isUserManager || isUserExpert) && isUserMember) { @@ -235,7 +236,11 @@

- {{ project.name }} + {{ + project.name.length > 20 + ? project.name.slice(0, 16) + "..." + : project.name + }}

project.leader === this.profile?.id); } // Методы для управления состоянием ошибок через сервис @@ -414,6 +418,7 @@ export class DeatilComponent implements OnInit, OnDestroy { const profileDataSub$ = this.authService.profile.pipe(filter(user => !!user)).subscribe({ next: user => { this.userType.set(user!.userType); + this.profile = user; this.cdRef.detectChanges(); }, }); @@ -444,10 +449,6 @@ export class DeatilComponent implements OnInit, OnDestroy { this.isInProfileInfo(); - this.skillsProfileService.getSubscriptionData().subscribe(r => { - this.isSubscriptionActive.set(r.isSubscribed); - }); - this.subscriptions.push(profileDataSub$); } } diff --git a/projects/social_platform/src/app/office/features/info-card/info-card.component.html b/projects/social_platform/src/app/office/features/info-card/info-card.component.html index 27884d4b2..3b9e44ad3 100644 --- a/projects/social_platform/src/app/office/features/info-card/info-card.component.html +++ b/projects/social_platform/src/app/office/features/info-card/info-card.component.html @@ -77,7 +77,7 @@ - @if (type === 'projects') { @if (info.name) { @if(info.name.length > 12) { + @if (type === 'projects' || type === 'invite') { @if (info.name) { @if(info.name.length > 12) {

{{ info.name.slice(0, 12) }}...

} @else {

{{ info.name }}

@@ -129,6 +129,21 @@
@if (type === 'projects') {
+ @if (info.partnerProgram || info.draft) { @if (info.partnerProgram && info.draft) { +
+ +
+ + @if (programProjectHovered) { +
+

проект привязан к программе {{ info.partnerProgram.name }}

+
+ } } + - @if (info.partnerProgramId) {
- +
- @if (programProjectHovered) { + @if (iconHovered) {
-

проект привязан к программе

+

+ @if (info.draft) { проект пока еще не опубликовали. } @else { проект привязан к программе + {{ info.partnerProgram.name }} + } +

} }
- } @else if (type === 'members') { + } @else if (type === 'members') { @if (leaderId === loggedUserId && loggedUserId !== + info?.userId) { +
+ выдать роль + удалить +
+ } @else { профиль - } @else { + } } @else {
(); @Output() onRejectingInvite = new EventEmitter(); @Output() onCreate = new EventEmitter(); + @Output() onRemoveCollaborator = new EventEmitter(); // Состояние компонента isUnsubscribeModalOpen = false; @@ -58,8 +61,12 @@ export class InfoCardComponent implements OnInit { haveBadge = this.calculateHaveBadge(); programProjectHovered = false; + iconHovered = false; + draftProjectHovered = false; - ngOnInit(): void {} + removeCollaboratorFromProject(userId: number): void { + this.onRemoveCollaborator.emit(userId); + } /** * Определяет, нужно ли показывать информацию о проекте diff --git a/projects/social_platform/src/app/office/features/news-card/news-card.component.html b/projects/social_platform/src/app/office/features/news-card/news-card.component.html index 9f9da3ba7..1a20e7c81 100644 --- a/projects/social_platform/src/app/office/features/news-card/news-card.component.html +++ b/projects/social_platform/src/app/office/features/news-card/news-card.component.html @@ -63,7 +63,7 @@ class="read-more text-body-10" (click)="onExpandNewsText(newsTextEl?.nativeElement, 'expanded', readMore)" > - {{ readMore ? "скрыть" : "читать полностью" }} + {{ readMore ? "скрыть" : "подробнее" }}
} @if (!editMode) { diff --git a/projects/social_platform/src/app/office/features/project-rating/project-rating.component.scss b/projects/social_platform/src/app/office/features/project-rating/project-rating.component.scss index 61443cced..732df24ce 100644 --- a/projects/social_platform/src/app/office/features/project-rating/project-rating.component.scss +++ b/projects/social_platform/src/app/office/features/project-rating/project-rating.component.scss @@ -35,7 +35,7 @@ app-textarea { ::ng-deep .field__input { min-height: 66px !important; - color: var(--dark-grey); + color: var(--grey-for-text) !important; background-color: transparent; border-color: var(--grey-button); diff --git a/projects/social_platform/src/app/office/features/project-rating/project-rating.component.ts b/projects/social_platform/src/app/office/features/project-rating/project-rating.component.ts index b7fb1ff1e..c47be1253 100644 --- a/projects/social_platform/src/app/office/features/project-rating/project-rating.component.ts +++ b/projects/social_platform/src/app/office/features/project-rating/project-rating.component.ts @@ -88,6 +88,20 @@ export class ProjectRatingComponent implements OnDestroy, ControlValueAccessor, return this._criteria(); } + @Input() + set disabled(value: boolean) { + this._disabled = value; + if (this.form) { + value ? this.form.disable() : this.form.enable(); + } + } + + get disabled(): boolean { + return this._disabled; + } + + private _disabled = false; + /** Сигнал для хранения критериев оценки */ _criteria = signal([]); @@ -183,6 +197,10 @@ export class ProjectRatingComponent implements OnDestroy, ControlValueAccessor, }); this.form = new FormGroup(formGroupControls); + + if (this.disabled) { + this.form.disable(); + } } /** diff --git a/projects/social_platform/src/app/office/feed/feed.component.ts b/projects/social_platform/src/app/office/feed/feed.component.ts index 0ade50883..0dc4e9128 100644 --- a/projects/social_platform/src/app/office/feed/feed.component.ts +++ b/projects/social_platform/src/app/office/feed/feed.component.ts @@ -77,7 +77,7 @@ export class FeedComponent implements OnInit, AfterViewInit, OnDestroy { this.totalItemsCount.set(0); this.feedPage.set(0); - return this.onFetch(0, this.perFetchTake(), includes ?? ["vacancy", "projects", "news"]); + return this.onFetch(0, this.perFetchTake(), includes ?? ["vacancy", "project", "news"]); }) ) .subscribe(feed => { diff --git a/projects/social_platform/src/app/office/feed/feed.resolver.ts b/projects/social_platform/src/app/office/feed/feed.resolver.ts index 91640b8db..8f4529270 100644 --- a/projects/social_platform/src/app/office/feed/feed.resolver.ts +++ b/projects/social_platform/src/app/office/feed/feed.resolver.ts @@ -29,6 +29,6 @@ export const FeedResolver: ResolveFn> = route => { return feedService.getFeed( 0, 20, - route.queryParams["includes"] ?? ["vacancy", "news", "projects"] + route.queryParams["includes"] ?? ["vacancy", "news", "project"] ); }; diff --git a/projects/social_platform/src/app/office/feed/filter/feed-filter.component.ts b/projects/social_platform/src/app/office/feed/filter/feed-filter.component.ts index 32d0ae57d..fa2020263 100644 --- a/projects/social_platform/src/app/office/feed/filter/feed-filter.component.ts +++ b/projects/social_platform/src/app/office/feed/filter/feed-filter.component.ts @@ -149,25 +149,25 @@ export class FeedFilterComponent implements OnInit, OnDestroy { */ setFilter(keyword: string): void { this.includedFilters.update(included => { - if (keyword.startsWith("projects/")) { + if (keyword.startsWith("project/")) { // Если уже активен этот же вложенный фильтр - сбрасываем к "projects" if (included === keyword) { - return "projects"; + return "project"; } return keyword; } // Если кликнули на "projects" - if (keyword === "projects") { - if (included.startsWith("projects/")) { - return "projects"; + if (keyword === "project") { + if (included.startsWith("project/")) { + return "project"; } - if (included === "projects") { + if (included === "project") { return ""; } - return "projects"; + return "project"; } if (included === keyword) { diff --git a/projects/social_platform/src/app/office/feed/models/feed-item.model.ts b/projects/social_platform/src/app/office/feed/models/feed-item.model.ts index 0c1af9bc8..14dd6cbeb 100644 --- a/projects/social_platform/src/app/office/feed/models/feed-item.model.ts +++ b/projects/social_platform/src/app/office/feed/models/feed-item.model.ts @@ -2,6 +2,7 @@ import { FeedNews } from "@office/projects/models/project-news.model"; import { Vacancy } from "@models/vacancy.model"; +import { Program } from "@office/program/models/program.model"; /** * МОДЕЛИ ДАННЫХ ДЛЯ ЭЛЕМЕНТОВ ЛЕНТЫ @@ -36,6 +37,10 @@ export interface FeedProject { imageAddress: string; viewsCount: number; leader: number; + partnerProgram: { + id: Program["id"]; + name: Program["name"]; + } | null; } /** diff --git a/projects/social_platform/src/app/office/feed/shared/open-vacancy/open-vacancy.component.html b/projects/social_platform/src/app/office/feed/shared/open-vacancy/open-vacancy.component.html index 0d839a34a..98f6f5c91 100644 --- a/projects/social_platform/src/app/office/feed/shared/open-vacancy/open-vacancy.component.html +++ b/projects/social_platform/src/app/office/feed/shared/open-vacancy/open-vacancy.component.html @@ -75,7 +75,7 @@

{{ vacancy.role }}

class="read-more text-body-10" (click)="onExpandDescription(descEl, 'expanded', readFullDescription)" > - {{ readFullDescription ? "cкрыть" : "читать полностью" }} + {{ readFullDescription ? "скрыть" : "подробнее" }}
} diff --git a/projects/social_platform/src/app/office/models/project.model.ts b/projects/social_platform/src/app/office/models/project.model.ts index eead36f68..0fe689d4f 100644 --- a/projects/social_platform/src/app/office/models/project.model.ts +++ b/projects/social_platform/src/app/office/models/project.model.ts @@ -58,7 +58,6 @@ export class Project { leader!: number; leaderInfo?: { firstName: string; lastName: string }; partnerProgramsTags?: string[]; - partnerProgramId!: number | null; partnerProgram!: PartnerProgramInfo | null; vacancies!: Vacancy[]; isCompany!: boolean; @@ -83,7 +82,6 @@ export class Project { industry: 0, viewsCount: 0, links: [], - partnerProgramId: null, partnerProgram: null, cover: null, coverImageAddress: null, diff --git a/projects/social_platform/src/app/office/office.component.html b/projects/social_platform/src/app/office/office.component.html index 9fd78ca19..8d0c98b96 100644 --- a/projects/social_platform/src/app/office/office.component.html +++ b/projects/social_platform/src/app/office/office.component.html @@ -18,6 +18,7 @@ class="office__sidebar" logoSrc="/assets/images/shared/logo.svg" [navItems]="navItems" + style="align-self: flex-start" >

Платформа создана компанией ООО «Молодежный форсайт»

diff --git a/projects/social_platform/src/app/office/profile/detail/main/main.component.html b/projects/social_platform/src/app/office/profile/detail/main/main.component.html index 91845a197..fed9c5805 100644 --- a/projects/social_platform/src/app/office/profile/detail/main/main.component.html +++ b/projects/social_platform/src/app/office/profile/detail/main/main.component.html @@ -16,11 +16,12 @@

метаданные

{{ (user.birthday | yearsFromBirthday) ?? "не указан" }}

+ @if (user.city) {
  • {{ user.city ?? "не указан" }}

  • - + } @if (user.speciality) {
  • @@ -31,9 +32,11 @@

    метаданные

    }}

  • + }
    + @if (user.userLanguages.length > 0) {

    языки

    @@ -49,8 +52,7 @@

    языки

    }
    - - @if (user.programs.length; as programsLength) { + } @if (user.programs.length; as programsLength) {
      @for (p of user.programs.slice(0, 3); track p.id) { @@ -83,7 +85,7 @@

      языки

      @if (programsLength > 3) {
      - {{ readAllPrograms ? "Скрыть" : "Читать полностью" }} + {{ readAllPrograms ? "скрыть" : "подробнее" }}
      }
    @@ -92,13 +94,13 @@

    языки

    @if (loggedUserId) {
    + @if (user.aboutMe; as about) {

    обо мне

    - @if (user.aboutMe; as about) {

    @if (descriptionExpandable) { @@ -106,13 +108,12 @@

    обо мне

    class="read-more text-body-10" (click)="onExpandDescription(descEl, 'expanded', readFullDescription)" > - {{ readFullDescription ? "Скрыть" : "Читать полностью" }} + {{ readFullDescription ? "скрыть" : "подробнее" }}
    }
    - }
    - + } @if (user.skills.length || user.achievements.length) {
    @for (directionItem of directions; track $index) { обо мне > }
    - + } @if (isProfileEmpty) { @if (loggedUserId === user.id) { +
    + +

    + заполните профиль и начните пользоваться PROCOLLAB +

    + заполнить +
    + } } @else {
    @if (loggedUserId === user.id) { @@ -146,6 +161,7 @@

    обо мне

    }
    + }
    } @@ -186,7 +202,7 @@

    контакты

    @if (linksLength > 3) {
    - {{ readAllLinks ? "Скрыть" : "Читать полностью" }} + {{ readAllLinks ? "скрыть" : "подробнее" }}
    } @@ -242,7 +258,7 @@

    образование

    @if (educationLength > 3) {
    - {{ readAllEducation ? "Скрыть" : "Читать полностью" }} + {{ readAllEducation ? "скрыть" : "подробнее" }}
    } @@ -316,7 +332,7 @@

    работа

    class="read-more text-body-10" (click)="readAllWorkExperience = !readAllWorkExperience" > - {{ readAllWorkExperience ? "Скрыть" : "Читать полностью" }} + {{ readAllWorkExperience ? "скрыть" : "подробнее" }} } @@ -367,7 +383,7 @@

    проекты

    @if (projectsLength > 3) {
    - {{ readAllProjects ? "Скрыть" : "Читать полностью" }} + {{ readAllProjects ? "скрыть" : "подробнее" }}
    } diff --git a/projects/social_platform/src/app/office/profile/detail/main/main.component.scss b/projects/social_platform/src/app/office/profile/detail/main/main.component.scss index 6f5c6b430..3684ef670 100644 --- a/projects/social_platform/src/app/office/profile/detail/main/main.component.scss +++ b/projects/social_platform/src/app/office/profile/detail/main/main.component.scss @@ -104,6 +104,21 @@ align-items: center; margin-top: 14px; } + + &__empty { + display: flex; + flex-direction: column; + gap: 24px; + align-items: center; + + &--text { + color: var(--grey-for-text); + } + + i { + color: var(--grey-for-text); + } + } } .info { diff --git a/projects/social_platform/src/app/office/profile/detail/main/main.component.ts b/projects/social_platform/src/app/office/profile/detail/main/main.component.ts index 73e137696..19d60148e 100644 --- a/projects/social_platform/src/app/office/profile/detail/main/main.component.ts +++ b/projects/social_platform/src/app/office/profile/detail/main/main.component.ts @@ -26,7 +26,7 @@ import { YearsFromBirthdayPipe, } from "projects/core"; import { UserLinksPipe } from "@core/pipes/user-links.pipe"; -import { IconComponent } from "@ui/components"; +import { IconComponent, ButtonComponent } from "@ui/components"; import { TagComponent } from "@ui/components/tag/tag.component"; import { AsyncPipe, CommonModule, NgTemplateOutlet } from "@angular/common"; import { ProfileService } from "@auth/services/profile.service"; @@ -81,17 +81,20 @@ import { DirectionItem, directionItemBuilder } from "@utils/helpers/directionIte NewsCardComponent, NewsFormComponent, ProjectDirectionCard, + ButtonComponent, ], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProfileMainComponent implements OnInit, AfterViewInit, OnDestroy { private readonly route = inject(ActivatedRoute); + private readonly authService = inject(AuthService); private readonly profileNewsService = inject(ProfileNewsService); private readonly profileDataService = inject(ProfileDataService); private readonly cdRef = inject(ChangeDetectorRef); user?: User; loggedUserId?: number; + isProfileEmpty?: boolean; directions: DirectionItem[] = []; @@ -119,14 +122,19 @@ export class ProfileMainComponent implements OnInit, AfterViewInit, OnDestroy { }, }); - const profileIdDataSub$ = this.profileDataService - .getProfileId() - .pipe(filter(userId => !!userId)) - .subscribe({ - next: profileId => { - this.loggedUserId = profileId; - }, - }); + const profileIdDataSub$ = this.authService.profile.subscribe({ + next: user => { + this.loggedUserId = user?.id; + }, + }); + + this.isProfileEmpty = !( + this.user?.firstName && + this.user?.lastName && + this.user?.email && + this.user?.avatar && + this.user?.birthday + ); const route$ = this.route.params .pipe( diff --git a/projects/social_platform/src/app/office/profile/edit/edit.component.html b/projects/social_platform/src/app/office/profile/edit/edit.component.html index b75efc90a..230c6d60e 100644 --- a/projects/social_platform/src/app/office/profile/edit/edit.component.html +++ b/projects/social_platform/src/app/office/profile/edit/edit.component.html @@ -84,21 +84,23 @@

    редактирование профиля

    @if (profileForm.get("avatar"); as avatar) { -
    +
    - - @if (avatar | controlError: "required") { -
    - {{ errorMessage.EMPTY_AVATAR }} -
    - } -
    +
    + + @if (avatar | controlError: "required") { +
    + {{ errorMessage.EMPTY_AVATAR }} +
    + } +
    +
    } @if (profileForm.get("coverImageAddress"); as coverImageAddress) {
    } @if (!editMode) { @@ -63,7 +63,11 @@

    оценка проекта

    - + @if (form | controlError: "required"; as error) {
    {{ error }} @@ -71,23 +75,29 @@ }
    - @if (showRatingForm || showRatedWithEdit || showConfirmedState) { + @if (showRatingForm || showRatedStatus || showConfirmedState) {
    - {{ showRatingForm ? "оценить проект" : "проект оценен" }} + {{ + programDateFinished() + ? "проект оценен" + : showRatingForm + ? "оценить проект" + : "проект оценен" + }} - @if (showRatedWithEdit) { + @if (showEditButton) {

    подтвердите оценку

    - + +
    + +

    {{ project.name }}

    +
    - - подтверждаю - +
    + @if (showConfirmRateModal()) { + + } +
    + +
    + + подтверждаю + + + + изменить оценку + +
    diff --git a/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.scss b/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.scss index 3a5755d5e..5fe7d8abc 100644 --- a/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.scss +++ b/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.scss @@ -170,11 +170,6 @@ width: 350px; max-height: calc(100vh - 40px); - i { - padding: 36px 0; - color: var(--green); - } - &__top { display: flex; flex-direction: column; @@ -182,6 +177,17 @@ margin-bottom: 10px; } + &__project { + display: flex; + gap: 8px; + align-items: center; + margin: 20px 0; + } + + &__criterias { + margin-bottom: 20px; + } + &__title { text-align: center; } @@ -191,4 +197,11 @@ color: var(--dark-grey); text-align: center; } + + &__buttons { + display: flex; + gap: 20px; + align-items: center; + justify-content: center; + } } diff --git a/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.ts b/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.ts index 58f936dfe..f89eaa0d3 100644 --- a/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.ts +++ b/projects/social_platform/src/app/office/program/shared/rating-card/rating-card.component.ts @@ -19,7 +19,16 @@ import { CommonModule } from "@angular/common"; import { ProjectRate } from "@office/program/models/project-rate"; import { ControlErrorPipe, ParseBreaksPipe, ParseLinksPipe } from "projects/core"; import { expandElement } from "@utils/expand-element"; -import { debounceTime, finalize, fromEvent, map, Observable, Subscription } from "rxjs"; +import { + debounceTime, + filter, + finalize, + fromEvent, + map, + Observable, + Subscription, + tap, +} from "rxjs"; import { BreakpointObserver } from "@angular/cdk/layout"; import { IndustryService } from "@office/services/industry.service"; import { ProjectRatingComponent } from "@office/features/project-rating/project-rating.component"; @@ -28,6 +37,9 @@ import { ProjectRatingService } from "@office/program/services/project-rating.se import { RouterLink } from "@angular/router"; import { TagComponent } from "@ui/components/tag/tag.component"; import { ModalComponent } from "@ui/components/modal/modal.component"; +import { ProgramDataService } from "@office/program/services/program-data.service"; +import { AuthService } from "@auth/services"; +import { User } from "@auth/models/user.model"; /** * Компонент карточки оценки проекта @@ -92,6 +104,8 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy { constructor( public industryService: IndustryService, private projectRatingService: ProjectRatingService, + private readonly programDataService: ProgramDataService, + private readonly authService: AuthService, private breakpointObserver: BreakpointObserver, private cdRef: ChangeDetectorRef ) {} @@ -111,6 +125,8 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy { _currentIndex = signal(0); _projects = signal([]); + profile = signal(null); + form = new FormControl(); submitLoading = signal(false); @@ -126,8 +142,12 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy { showConfirmRateModal = signal(false); + locallyRatedByCurrentUser = signal(false); + isProjectCriterias = signal(0); + programDateFinished = signal(false); + desktopMode$: Observable = this.breakpointObserver .observe("(min-width: 920px)") .pipe(map(result => result.matches)); @@ -140,6 +160,27 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy { this.projectConfirmed.set(isScored); this.projectRated.set(isScored); } + + const program$ = this.programDataService.program$ + .pipe( + filter(program => !!program), + tap(program => { + if (program && program.datetimeFinished) { + this.programDateFinished.set(Date.now() > Date.parse(program.datetimeFinished)); + } + }) + ) + .subscribe(); + + this.subscriptions$().push(program$); + + const profileId$ = this.authService.profile.subscribe({ + next: profile => { + this.profile.set(profile); + }, + }); + + this.subscriptions$().push(profileId$); } ngAfterViewInit(): void { @@ -186,36 +227,51 @@ export class RatingCardComponent implements OnInit, AfterViewInit, OnDestroy { .pipe(finalize(() => this.submitLoading.set(false))) .subscribe({ next: () => { - if (this.showConfirmRateModal()) { - this.projectConfirmed.set(true); - this.showConfirmRateModal.set(false); - } else { - this.projectRated.set(true); - this.showConfirmRateModal.set(true); - } + this.locallyRatedByCurrentUser.set(true); + this.projectRated.set(true); + this.projectConfirmed.set(true); + this.showConfirmRateModal.set(false); }, }); } redoRating(): void { - if (!this.projectConfirmed()) { - this.projectRated.set(false); - } + this.projectRated.set(false); + this.projectConfirmed.set(false); + this.locallyRatedByCurrentUser.set(false); } get canEdit(): boolean { - return !this.projectConfirmed(); + return !this.programDateFinished(); + } + + get isCurrentUserExpert(): boolean { + const currentProfile = this.profile(); + const project = this.project; + + if (!currentProfile || !project) return false; + + const isExpertFromBackend = + !!project.scoredExpertId && project.scoredExpertId === currentProfile.id; + + const isExpertLocally = this.locallyRatedByCurrentUser(); + + return isExpertFromBackend || isExpertLocally; } get showRatingForm(): boolean { return !this.projectRated() && this.canEdit; } - get showRatedWithEdit(): boolean { - return this.projectRated() && !this.projectConfirmed(); + get showRatedStatus(): boolean { + return this.projectRated() || this.projectConfirmed(); + } + + get showEditButton(): boolean { + return this.showRatedStatus && this.canEdit && this.isCurrentUserExpert; } get showConfirmedState(): boolean { - return this.projectConfirmed(); + return this.projectConfirmed() && !this.canEdit; } } diff --git a/projects/social_platform/src/app/office/projects/detail/info/info.component.html b/projects/social_platform/src/app/office/projects/detail/info/info.component.html index 6e9235d99..efa0ba7ca 100644 --- a/projects/social_platform/src/app/office/projects/detail/info/info.component.html +++ b/projects/social_platform/src/app/office/projects/detail/info/info.component.html @@ -65,10 +65,10 @@

    о проекте

    @if (descriptionExpandable) {
    - {{ readFullDescription ? "Скрыть" : "Читать полностью" }} + {{ readFullDescription ? "скрыть" : "подробнее" }}
    } @@ -196,8 +196,11 @@

    достижения

    } } @if (project.achievements.length > 3) { -
    - {{ readAllAchievements ? "Скрыть" : "Читать полностью" }} +
    + {{ readAllAchievements ? "скрыть" : "подробнее" }}
    }
    diff --git a/projects/social_platform/src/app/office/projects/detail/services/project-data.service.ts b/projects/social_platform/src/app/office/projects/detail/services/project-data.service.ts index 8fa8d6701..94bd854bc 100644 --- a/projects/social_platform/src/app/office/projects/detail/services/project-data.service.ts +++ b/projects/social_platform/src/app/office/projects/detail/services/project-data.service.ts @@ -28,4 +28,12 @@ export class ProjectDataService { filter(vacancies => !!vacancies) ); } + + getProjectLeaderId() { + return this.project$.pipe(map(project => project?.leader)); + } + + getProjectId() { + return this.project$.pipe(map(project => project?.id)); + } } diff --git a/projects/social_platform/src/app/office/projects/detail/team/team.component.html b/projects/social_platform/src/app/office/projects/detail/team/team.component.html index a0ea62bda..63e2fc80e 100644 --- a/projects/social_platform/src/app/office/projects/detail/team/team.component.html +++ b/projects/social_platform/src/app/office/projects/detail/team/team.component.html @@ -3,7 +3,13 @@ @if(team) { @if (team.length) {
    @for (collaborator of team; track $index) { - + }
    } } diff --git a/projects/social_platform/src/app/office/projects/detail/team/team.component.ts b/projects/social_platform/src/app/office/projects/detail/team/team.component.ts index 7c8ce5784..896071959 100644 --- a/projects/social_platform/src/app/office/projects/detail/team/team.component.ts +++ b/projects/social_platform/src/app/office/projects/detail/team/team.component.ts @@ -1,12 +1,14 @@ /** @format */ import { CommonModule } from "@angular/common"; -import { Component, inject, OnDestroy, OnInit } from "@angular/core"; +import { Component, inject, OnDestroy, OnInit, signal } from "@angular/core"; import { IconComponent } from "@uilib"; import { ProjectDataService } from "../services/project-data.service"; import { InfoCardComponent } from "@office/features/info-card/info-card.component"; import { Subscription } from "rxjs"; import { Project } from "@office/models/project.model"; +import { ProjectService } from "@office/services/project.service"; +import { AuthService } from "@auth/services"; /** * Компонент страницы команды в деательной информации о проекте @@ -19,11 +21,15 @@ import { Project } from "@office/models/project.model"; standalone: true, }) export class ProjectTeamComponent implements OnInit, OnDestroy { - // сервис для работы с данными детальной информации проекта private readonly projectDataService = inject(ProjectDataService); + private readonly projectService = inject(ProjectService); + private readonly authService = inject(AuthService); // массив пользователей в команде team?: Project["collaborators"]; + projectId = signal(0); + loggedUserId = signal(0); + leaderId = signal(0); // массив подписок subscriptions: Subscription[] = []; @@ -37,10 +43,48 @@ export class ProjectTeamComponent implements OnInit, OnDestroy { }); teamSub$ && this.subscriptions.push(teamSub$); + + const projectId$ = this.projectDataService.getProjectId().subscribe({ + next: projectId => { + if (projectId) { + this.projectId.set(projectId); + } + }, + }); + + if (location.href.includes("/team")) { + const leaderId$ = this.projectDataService.getProjectLeaderId().subscribe({ + next: leaderId => { + if (leaderId) { + this.leaderId.set(leaderId); + } + }, + }); + + const currentProfileId$ = this.authService.profile.subscribe({ + next: profile => { + if (profile) { + this.loggedUserId.set(profile.id); + } + }, + }); + + this.subscriptions.push(leaderId$, currentProfileId$); + } + + this.subscriptions.push(projectId$); } ngOnDestroy(): void { - // отписка от поток, которые не нужны this.subscriptions.forEach($ => $.unsubscribe()); } + + removeCollaboratorFromProject(userId: number): void { + const index = this.team?.findIndex(p => p.userId === userId); + if (index !== -1) { + this.team?.splice(index!, 1); + } + + this.projectService.removeColloborator(this.projectId(), userId).subscribe(); + } } diff --git a/projects/social_platform/src/app/office/projects/edit/edit.component.html b/projects/social_platform/src/app/office/projects/edit/edit.component.html index 7fe9a7a38..7d93b91ef 100644 --- a/projects/social_platform/src/app/office/projects/edit/edit.component.html +++ b/projects/social_platform/src/app/office/projects/edit/edit.component.html @@ -76,6 +76,7 @@

    редактировать проект

    } diff --git a/projects/social_platform/src/app/office/projects/edit/edit.component.ts b/projects/social_platform/src/app/office/projects/edit/edit.component.ts index f6e1ac806..82a33c29d 100644 --- a/projects/social_platform/src/app/office/projects/edit/edit.component.ts +++ b/projects/social_platform/src/app/office/projects/edit/edit.component.ts @@ -214,6 +214,7 @@ export class ProjectEditComponent implements OnInit, AfterViewInit, OnDestroy { // Маркер того является ли проект привязанный к конкурсной программе isCompetitive = false; + isProjectAssignToProgram = false; // Маркер что проект привязан isProjectBoundToProgram = false; @@ -660,6 +661,7 @@ export class ProjectEditComponent implements OnInit, AfterViewInit, OnDestroy { if (project.partnerProgram) { this.isCompetitive = project.partnerProgram.canSubmit; + this.isProjectAssignToProgram = !!project.partnerProgram.programId; this.projectAdditionalService.initializeAdditionalForm( project.partnerProgram?.programFields, diff --git a/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.html b/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.html index d39967ea9..aefe48a85 100644 --- a/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.html +++ b/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.html @@ -4,7 +4,7 @@
    @if (partnerProgramFields.length) { @for (field of partnerProgramFields; track field.id) { - +
    @switch (field.fieldType) { @case ("text") { @if (additionalForm.get(field.name); as control) { @@ -92,7 +92,14 @@
    } } - } } @else { + } } @else { @if (isProjectAssignToProgram) { +
    +

    + проект привязан к программе, но дополнительных полей для заполнения нет +

    + +
    + } @else {

    пока ты не участвуешь ни в одной программе

    @@ -112,7 +119,7 @@
    - } + } }
    diff --git a/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.scss b/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.scss index 6092cd72f..66a80cc31 100644 --- a/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.scss +++ b/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.scss @@ -95,7 +95,7 @@ } @include responsive.apply-desktop { - width: 60%; + width: 70%; ::ng-deep { app-input { diff --git a/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.ts b/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.ts index aba9d8d48..e4e0486a3 100644 --- a/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.ts +++ b/projects/social_platform/src/app/office/projects/edit/shared/project-additional-step/project-additional-step.component.ts @@ -47,6 +47,8 @@ export class ProjectAdditionalStepComponent implements OnInit { readonly errorMessage = ErrorMessage; + @Input() isProjectAssignToProgram?: boolean; + ngOnInit(): void { // Инициализация уже должна быть выполнена в родительском компоненте this.cdRef.detectChanges(); diff --git a/projects/social_platform/src/app/office/projects/projects.component.html b/projects/social_platform/src/app/office/projects/projects.component.html index bba538ea1..ede266c82 100644 --- a/projects/social_platform/src/app/office/projects/projects.component.html +++ b/projects/social_platform/src/app/office/projects/projects.component.html @@ -74,8 +74,8 @@
      @for (invite of myInvites; track invite.id) { diff --git a/projects/social_platform/src/app/office/projects/projects.component.ts b/projects/social_platform/src/app/office/projects/projects.component.ts index 695b53239..7243d9616 100644 --- a/projects/social_platform/src/app/office/projects/projects.component.ts +++ b/projects/social_platform/src/app/office/projects/projects.component.ts @@ -67,6 +67,7 @@ export class ProjectsComponent implements OnInit, OnDestroy { this.route.data.pipe(map(r => r["data"])).subscribe({ next: invites => { + this.allInvites = inviteToProjectMapper(invites); this.myInvites = inviteToProjectMapper(invites.slice(0, 1)); }, }); @@ -102,8 +103,7 @@ export class ProjectsComponent implements OnInit, OnDestroy { searchForm: FormGroup; myInvites: Project[] = []; - - subscriptions$: Subscription[] = []; + private allInvites: Project[] = []; isMy = location.href.includes("/my"); isAll = location.href.includes("/all"); @@ -113,6 +113,8 @@ export class ProjectsComponent implements OnInit, OnDestroy { isFilterOpen = false; + subscriptions$: Subscription[] = []; + private swipeStartY = 0; private swipeThreshold = 50; private isSwiping = false; @@ -151,8 +153,10 @@ export class ProjectsComponent implements OnInit, OnDestroy { this.renderer.setStyle(this.filterBody.nativeElement, "transform", "translateY(0)"); } - acceptOrRejectInvite(): void { - this.myInvites = []; + acceptOrRejectInvite(inviteId: number): void { + this.allInvites = this.allInvites.filter(invite => invite.inviteId !== inviteId); + + this.myInvites = this.allInvites.slice(0, 1); } closeFilter(): void { diff --git a/projects/social_platform/src/app/office/services/invite.service.ts b/projects/social_platform/src/app/office/services/invite.service.ts index 18cecee3a..787186781 100644 --- a/projects/social_platform/src/app/office/services/invite.service.ts +++ b/projects/social_platform/src/app/office/services/invite.service.ts @@ -99,16 +99,9 @@ export class InviteService { * @returns Observable - массив приглашений, адресованных текущему пользователю */ getMy(): Observable { - return this.authService.profile.pipe( - take(1), - concatMap(profile => - this.apiService.get( - `${this.INVITES_URL}/`, - new HttpParams({ fromObject: { user_id: profile.id } }) - ) - ), - map(invites => plainToInstance(Invite, invites)) - ); + return this.apiService + .get(`${this.INVITES_URL}/`) + .pipe(map(invites => plainToInstance(Invite, invites))); } /** diff --git a/projects/social_platform/src/app/office/vacancies/detail/info/info.component.html b/projects/social_platform/src/app/office/vacancies/detail/info/info.component.html index 6a0d60ce0..f4b12eed9 100644 --- a/projects/social_platform/src/app/office/vacancies/detail/info/info.component.html +++ b/projects/social_platform/src/app/office/vacancies/detail/info/info.component.html @@ -14,10 +14,10 @@

      @if (descriptionExpandable) {
      - {{ readFullDescription ? "Скрыть" : "Читать полностью" }} + {{ readFullDescription ? "скрыть" : "подробнее" }}
      } @@ -52,8 +52,8 @@ } } @if (skillsLength > 8) { -
      - {{ readFullSkills ? "Скрыть" : "Читать полностью" }} +
      + {{ readFullSkills ? "скрыть" : "подробнее" }}
      }
      diff --git a/projects/social_platform/src/app/ui/components/bar/bar.component.scss b/projects/social_platform/src/app/ui/components/bar/bar.component.scss index e2b4c3190..15a85c9fb 100644 --- a/projects/social_platform/src/app/ui/components/bar/bar.component.scss +++ b/projects/social_platform/src/app/ui/components/bar/bar.component.scss @@ -79,4 +79,8 @@ } } } + + &__back { + margin-top: 45px; + } } diff --git a/projects/social_platform/src/app/ui/components/input/input.component.scss b/projects/social_platform/src/app/ui/components/input/input.component.scss index c970ca070..7a8fd0951 100644 --- a/projects/social_platform/src/app/ui/components/input/input.component.scss +++ b/projects/social_platform/src/app/ui/components/input/input.component.scss @@ -47,7 +47,7 @@ } &--error { - border-color: var(--red); + border: 0.5px solid var(--red); } @include typography.body-10;