From 9f3769995e565bc3e007aed1369c2988fe1c7705 Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 8 Jul 2025 13:44:09 +0300 Subject: [PATCH 1/7] fix(contributors): fixed contributors resource type --- .../project/contributors/contributors.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/features/project/contributors/contributors.component.ts b/src/app/features/project/contributors/contributors.component.ts index 115703c18..f2cbb0747 100644 --- a/src/app/features/project/contributors/contributors.component.ts +++ b/src/app/features/project/contributors/contributors.component.ts @@ -160,7 +160,7 @@ export class ContributorsComponent implements OnInit { const updatedContributors = findChangedItems(this.initialContributors(), this.contributors(), 'id'); const updateRequests = updatedContributors.map((payload) => - this.actions.updateContributor(this.resourceId(), ResourceType.Project, payload) + this.actions.updateContributor(this.resourceId(), this.resourceType(), payload) ); forkJoin(updateRequests).subscribe(() => { @@ -190,7 +190,7 @@ export class ContributorsComponent implements OnInit { this.openAddUnregisteredContributorDialog(); } else { const addRequests = res.data.map((payload) => - this.actions.addContributor(this.resourceId(), ResourceType.Project, payload) + this.actions.addContributor(this.resourceId(), this.resourceType(), payload) ); forkJoin(addRequests).subscribe(() => { @@ -221,7 +221,7 @@ export class ContributorsComponent implements OnInit { const successMessage = this.translateService.instant('project.contributors.toastMessages.addSuccessMessage'); const params = { name: res.data[0].fullName }; - this.actions.addContributor(this.resourceId(), ResourceType.Project, res.data[0]).subscribe({ + this.actions.addContributor(this.resourceId(), this.resourceType(), res.data[0]).subscribe({ next: () => this.toastService.showSuccess(successMessage, params), }); } @@ -236,7 +236,7 @@ export class ContributorsComponent implements OnInit { acceptLabelKey: 'common.buttons.remove', onConfirm: () => { this.actions - .deleteContributor(this.resourceId(), ResourceType.Project, contributor.userId) + .deleteContributor(this.resourceId(), this.resourceType(), contributor.userId) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => From 848aa09c518841f2b85bf812cd01584dbf34a52a Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 8 Jul 2025 13:45:23 +0300 Subject: [PATCH 2/7] fix(static-pages): updated styles --- .../privacy-policy.component.scss | 41 +++++++++---------- .../terms-of-use/terms-of-use.component.scss | 41 +++++++++---------- 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/app/features/static/privacy-policy/privacy-policy.component.scss b/src/app/features/static/privacy-policy/privacy-policy.component.scss index 62035948d..89c6fc51e 100644 --- a/src/app/features/static/privacy-policy/privacy-policy.component.scss +++ b/src/app/features/static/privacy-policy/privacy-policy.component.scss @@ -1,31 +1,30 @@ -@use "assets/styles/variables" as var; @use "assets/styles/mixins" as mix; :host { - background-color: var.$white; + background-color: var(--white); +} - .container { - h2 { - margin-bottom: mix.rem(12px); - } +.container { + h2 { + margin-bottom: mix.rem(12px); + } - h4 { - font-size: mix.rem(14px); - font-weight: 400; - } + h4 { + font-size: mix.rem(14px); + font-weight: 400; + } - ul, - p { - margin-bottom: mix.rem(24px); - } + ul, + p { + margin-bottom: mix.rem(24px); + } - li { - margin-left: mix.rem(24px); - list-style: initial; - } + li { + margin-left: mix.rem(24px); + list-style: initial; + } - a { - font-weight: 700; - } + a { + font-weight: 700; } } diff --git a/src/app/features/static/terms-of-use/terms-of-use.component.scss b/src/app/features/static/terms-of-use/terms-of-use.component.scss index 62035948d..89c6fc51e 100644 --- a/src/app/features/static/terms-of-use/terms-of-use.component.scss +++ b/src/app/features/static/terms-of-use/terms-of-use.component.scss @@ -1,31 +1,30 @@ -@use "assets/styles/variables" as var; @use "assets/styles/mixins" as mix; :host { - background-color: var.$white; + background-color: var(--white); +} - .container { - h2 { - margin-bottom: mix.rem(12px); - } +.container { + h2 { + margin-bottom: mix.rem(12px); + } - h4 { - font-size: mix.rem(14px); - font-weight: 400; - } + h4 { + font-size: mix.rem(14px); + font-weight: 400; + } - ul, - p { - margin-bottom: mix.rem(24px); - } + ul, + p { + margin-bottom: mix.rem(24px); + } - li { - margin-left: mix.rem(24px); - list-style: initial; - } + li { + margin-left: mix.rem(24px); + list-style: initial; + } - a { - font-weight: 700; - } + a { + font-weight: 700; } } From 2a8915294445f7c5d2f2deb0b728013a21be2b64 Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 8 Jul 2025 14:13:02 +0300 Subject: [PATCH 3/7] fix(meetings): updated meetings landing --- src/app/features/meetings/components/index.ts | 1 + .../meetings-feature-card.component.html | 7 +++ .../meetings-feature-card.component.scss | 0 .../meetings-feature-card.component.spec.ts | 22 ++++++++++ .../meetings-feature-card.component.ts | 19 ++++++++ src/app/features/meetings/constants/index.ts | 2 + .../meetings-feature-cards.constants.ts | 22 ++++++++++ .../partner-organizations.constants.ts | 32 ++++++++++++++ .../meetings/mappers/meetings.mapper.ts | 15 ++++--- src/app/features/meetings/models/index.ts | 3 ++ .../models/meeting-feature-card.model.ts | 6 +++ .../models/meetings-json-api.model.ts | 28 ++++++++++++ .../meetings/models/meetings.models.ts | 29 ------------- .../models/partner-organization.model.ts | 7 +++ .../meetings-landing.component.html | 43 +++++++------------ .../meetings-landing.component.ts | 17 +++++++- .../meetings/services/meetings.service.ts | 10 ++--- .../meetings/store/meetings.actions.ts | 2 +- .../features/meetings/store/meetings.model.ts | 5 ++- .../meetings/store/meetings.selectors.ts | 7 +-- .../features/meetings/store/meetings.state.ts | 7 +-- 21 files changed, 206 insertions(+), 78 deletions(-) create mode 100644 src/app/features/meetings/components/index.ts create mode 100644 src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.html create mode 100644 src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.scss create mode 100644 src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.spec.ts create mode 100644 src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.ts create mode 100644 src/app/features/meetings/constants/meetings-feature-cards.constants.ts create mode 100644 src/app/features/meetings/constants/partner-organizations.constants.ts create mode 100644 src/app/features/meetings/models/meeting-feature-card.model.ts create mode 100644 src/app/features/meetings/models/meetings-json-api.model.ts create mode 100644 src/app/features/meetings/models/partner-organization.model.ts diff --git a/src/app/features/meetings/components/index.ts b/src/app/features/meetings/components/index.ts new file mode 100644 index 000000000..39d4b6efc --- /dev/null +++ b/src/app/features/meetings/components/index.ts @@ -0,0 +1 @@ +export { MeetingsFeatureCardComponent } from './meetings-feature-card/meetings-feature-card.component'; diff --git a/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.html b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.html new file mode 100644 index 000000000..a97702d2b --- /dev/null +++ b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.html @@ -0,0 +1,7 @@ + +
+ +

{{ titleKey() | translate }}

+

{{ descriptionKey() | translate }}

+
+
diff --git a/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.scss b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.spec.ts b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.spec.ts new file mode 100644 index 000000000..479bb16c6 --- /dev/null +++ b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MeetingsFeatureCardComponent } from './meetings-feature-card.component'; + +describe('MeetingsFeatureCardComponent', () => { + let component: MeetingsFeatureCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MeetingsFeatureCardComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MeetingsFeatureCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.ts b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.ts new file mode 100644 index 000000000..28c809ed5 --- /dev/null +++ b/src/app/features/meetings/components/meetings-feature-card/meetings-feature-card.component.ts @@ -0,0 +1,19 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { Card } from 'primeng/card'; + +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +@Component({ + selector: 'osf-meetings-feature-card', + imports: [Card, TranslatePipe], + templateUrl: './meetings-feature-card.component.html', + styleUrl: './meetings-feature-card.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MeetingsFeatureCardComponent { + iconSrc = input.required(); + iconAlt = input.required(); + titleKey = input.required(); + descriptionKey = input.required(); +} diff --git a/src/app/features/meetings/constants/index.ts b/src/app/features/meetings/constants/index.ts index 361431603..cfee319c2 100644 --- a/src/app/features/meetings/constants/index.ts +++ b/src/app/features/meetings/constants/index.ts @@ -1,2 +1,4 @@ export * from './meeting-submissions-table.constants'; +export * from './meetings-feature-cards.constants'; export * from './meetings-fields.constants'; +export * from './partner-organizations.constants'; diff --git a/src/app/features/meetings/constants/meetings-feature-cards.constants.ts b/src/app/features/meetings/constants/meetings-feature-cards.constants.ts new file mode 100644 index 000000000..f335b9d1a --- /dev/null +++ b/src/app/features/meetings/constants/meetings-feature-cards.constants.ts @@ -0,0 +1,22 @@ +import { MeetingFeatureCard } from '../models'; + +export const MEETINGS_FEATURE_CARDS: MeetingFeatureCard[] = [ + { + iconSrc: 'assets/icons/colored/discover.svg', + iconAlt: 'Discover', + titleKey: 'meetings.landing.features.discover.title', + descriptionKey: 'meetings.landing.features.discover.description', + }, + { + iconSrc: 'assets/icons/colored/share.svg', + iconAlt: 'Share', + titleKey: 'meetings.landing.features.share.title', + descriptionKey: 'meetings.landing.features.share.description', + }, + { + iconSrc: 'assets/icons/colored/enhance.svg', + iconAlt: 'Enhance', + titleKey: 'meetings.landing.features.enhance.title', + descriptionKey: 'meetings.landing.features.enhance.description', + }, +]; diff --git a/src/app/features/meetings/constants/partner-organizations.constants.ts b/src/app/features/meetings/constants/partner-organizations.constants.ts new file mode 100644 index 000000000..c256c523c --- /dev/null +++ b/src/app/features/meetings/constants/partner-organizations.constants.ts @@ -0,0 +1,32 @@ +import { PartnerOrganization } from '../models'; + +export const PARTNER_ORGANIZATIONS: PartnerOrganization[] = [ + { + name: 'APS', + url: 'http://www.psychologicalscience.org/', + imageSrc: 'assets/icons/colored/aps.svg', + alt: 'APS', + height: 90, + }, + { + name: 'BITSS', + url: 'http://www.bitss.org/', + imageSrc: 'assets/icons/colored/bitss.svg', + alt: 'BITSS', + height: 90, + }, + { + name: 'NRAO', + url: 'http://www.nrao.edu/', + imageSrc: 'assets/icons/colored/nrao.svg', + alt: 'NRAO', + height: 90, + }, + { + name: 'SPSP', + url: 'http://www.spsp.org/', + imageSrc: 'assets/icons/colored/spsp.svg', + alt: 'SPSP', + height: 90, + }, +]; diff --git a/src/app/features/meetings/mappers/meetings.mapper.ts b/src/app/features/meetings/mappers/meetings.mapper.ts index aa9cc0cea..d17e04a58 100644 --- a/src/app/features/meetings/mappers/meetings.mapper.ts +++ b/src/app/features/meetings/mappers/meetings.mapper.ts @@ -1,13 +1,16 @@ import { JsonApiResponseWithPaging } from '@core/models'; + import { - MeetingGetResponse, - MeetingSubmissionGetResponse, + MeetingGetResponseJsonApi, + MeetingSubmissionGetResponseJsonApi, MeetingSubmissionsWithPaging, MeetingsWithPaging, -} from '@osf/features/meetings/models'; +} from '../models'; export class MeetingsMapper { - static fromMeetingsGetResponse(response: JsonApiResponseWithPaging): MeetingsWithPaging { + static fromMeetingsGetResponse( + response: JsonApiResponseWithPaging + ): MeetingsWithPaging { return { data: response.data.map((item) => ({ id: item.id, @@ -22,7 +25,7 @@ export class MeetingsMapper { } static fromMeetingSubmissionGetResponse( - response: JsonApiResponseWithPaging + response: JsonApiResponseWithPaging ): MeetingSubmissionsWithPaging { return { data: response.data.map((item) => ({ @@ -38,7 +41,7 @@ export class MeetingsMapper { }; } - static fromMeetingGetResponse(response: MeetingGetResponse) { + static fromMeetingGetResponse(response: MeetingGetResponseJsonApi) { return { id: response.id, name: response.attributes.name, diff --git a/src/app/features/meetings/models/index.ts b/src/app/features/meetings/models/index.ts index 781399be8..4ff56dcc5 100644 --- a/src/app/features/meetings/models/index.ts +++ b/src/app/features/meetings/models/index.ts @@ -1 +1,4 @@ +export * from './meeting-feature-card.model'; export * from './meetings.models'; +export * from './meetings-json-api.model'; +export * from './partner-organization.model'; diff --git a/src/app/features/meetings/models/meeting-feature-card.model.ts b/src/app/features/meetings/models/meeting-feature-card.model.ts new file mode 100644 index 000000000..3039cdae3 --- /dev/null +++ b/src/app/features/meetings/models/meeting-feature-card.model.ts @@ -0,0 +1,6 @@ +export interface MeetingFeatureCard { + iconSrc: string; + iconAlt: string; + titleKey: string; + descriptionKey: string; +} diff --git a/src/app/features/meetings/models/meetings-json-api.model.ts b/src/app/features/meetings/models/meetings-json-api.model.ts new file mode 100644 index 000000000..930a7730f --- /dev/null +++ b/src/app/features/meetings/models/meetings-json-api.model.ts @@ -0,0 +1,28 @@ +import { NumberOrNull, StringOrNull } from '@osf/core/helpers'; + +export interface MeetingGetResponseJsonApi { + id: string; + type: 'meetings'; + attributes: { + name: string; + location: string; + start_date: Date; + end_date: Date; + submissions_count: number; + }; +} + +export interface MeetingSubmissionGetResponseJsonApi { + id: string; + type: 'meeting-submissions'; + attributes: { + title: string; + date_created: Date; + author_name: string; + download_count: NumberOrNull; + meeting_category: string; + }; + links: { + download: StringOrNull; + }; +} diff --git a/src/app/features/meetings/models/meetings.models.ts b/src/app/features/meetings/models/meetings.models.ts index dd405cb6e..2153a7d0e 100644 --- a/src/app/features/meetings/models/meetings.models.ts +++ b/src/app/features/meetings/models/meetings.models.ts @@ -1,4 +1,3 @@ -// domain models import { NumberOrNull, StringOrNull } from '@core/helpers'; export interface Meeting { @@ -29,31 +28,3 @@ export interface MeetingSubmissionsWithPaging { data: MeetingSubmission[]; totalCount: number; } - -//api models -export interface MeetingGetResponse { - id: string; - type: 'meetings'; - attributes: { - name: string; - location: string; - start_date: Date; - end_date: Date; - submissions_count: number; - }; -} - -export interface MeetingSubmissionGetResponse { - id: string; - type: 'meeting-submissions'; - attributes: { - title: string; - date_created: Date; - author_name: string; - download_count: NumberOrNull; - meeting_category: string; - }; - links: { - download: StringOrNull; - }; -} diff --git a/src/app/features/meetings/models/partner-organization.model.ts b/src/app/features/meetings/models/partner-organization.model.ts new file mode 100644 index 000000000..202943c79 --- /dev/null +++ b/src/app/features/meetings/models/partner-organization.model.ts @@ -0,0 +1,7 @@ +export interface PartnerOrganization { + name: string; + url: string; + imageSrc: string; + alt: string; + height: number; +} diff --git a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html index ed3d06734..b05ceb797 100644 --- a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html +++ b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html @@ -4,7 +4,7 @@ [description]="'meetings.landing.description' | translate" /> -
+

{{ 'meetings.landing.submissionsNote' | translate }}

@@ -14,6 +14,7 @@ [control]="searchControl" [placeholder]="'meetings.landing.searchPlaceholder' | translate" /> +
- -
- Discover -

{{ 'meetings.landing.features.discover.title' | translate }}

-

{{ 'meetings.landing.features.discover.description' | translate }}

-
-
- - -
- Share -

{{ 'meetings.landing.features.share.title' | translate }}

-

{{ 'meetings.landing.features.share.description' | translate }}

+ @for (card of meetingsFeatureCards; track $index) { +
+
- - - -
- Enhance -

{{ 'meetings.landing.features.enhance.title' | translate }}

-

{{ 'meetings.landing.features.enhance.description' | translate }}

-
-
+ }

{{ 'meetings.landing.users.title' | translate }}

- APS - BITSS - NRAO - SPSP + @for (org of partnerOrganizations; track $index) { + + + + }
diff --git a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts index 2740a9cb7..8a4949355 100644 --- a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts +++ b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts @@ -3,7 +3,6 @@ import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; import { SortEvent } from 'primeng/api'; -import { Card } from 'primeng/card'; import { Skeleton } from 'primeng/skeleton'; import { TableModule, TablePageEvent } from 'primeng/table'; @@ -34,9 +33,20 @@ import { SortOrder } from '@shared/enums'; import { QueryParams, TableParameters } from '@shared/models'; import { SearchFilters } from '@shared/models/filters'; +import { MeetingsFeatureCardComponent } from '../../components'; +import { MEETINGS_FEATURE_CARDS, PARTNER_ORGANIZATIONS } from '../../constants'; + @Component({ selector: 'osf-meetings-landing', - imports: [SubHeaderComponent, Card, SearchInputComponent, DatePipe, TableModule, TranslatePipe, Skeleton], + imports: [ + SubHeaderComponent, + SearchInputComponent, + MeetingsFeatureCardComponent, + DatePipe, + TableModule, + TranslatePipe, + Skeleton, + ], templateUrl: './meetings-landing.component.html', styleUrl: './meetings-landing.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -61,6 +71,9 @@ export class MeetingsLandingComponent { firstRowIndex: 0, }); + partnerOrganizations = PARTNER_ORGANIZATIONS; + meetingsFeatureCards = MEETINGS_FEATURE_CARDS; + meetings = select(MeetingsSelectors.getAllMeetings); totalMeetingsCount = select(MeetingsSelectors.getMeetingsTotalCount); isMeetingsLoading = select(MeetingsSelectors.isMeetingsLoading); diff --git a/src/app/features/meetings/services/meetings.service.ts b/src/app/features/meetings/services/meetings.service.ts index f6c1cf640..a95c22357 100644 --- a/src/app/features/meetings/services/meetings.service.ts +++ b/src/app/features/meetings/services/meetings.service.ts @@ -6,8 +6,8 @@ import { JsonApiService } from '@core/services'; import { JsonApiResponse, JsonApiResponseWithPaging } from '@osf/core/models'; import { MeetingsMapper } from '@osf/features/meetings/mappers'; import { - MeetingGetResponse, - MeetingSubmissionGetResponse, + MeetingGetResponseJsonApi, + MeetingSubmissionGetResponseJsonApi, MeetingSubmissionsWithPaging, MeetingsWithPaging, } from '@osf/features/meetings/models'; @@ -35,7 +35,7 @@ export class MeetingsService { ); return this.jsonApiService - .get>(this.baseUrl, params) + .get>(this.baseUrl, params) .pipe(map((response) => MeetingsMapper.fromMeetingsGetResponse(response))); } @@ -55,14 +55,14 @@ export class MeetingsService { return this.jsonApiService .get< - JsonApiResponseWithPaging + JsonApiResponseWithPaging >(`${this.baseUrl}${meetingId}/submissions/`, params) .pipe(map((response) => MeetingsMapper.fromMeetingSubmissionGetResponse(response))); } getMeetingById(meetingId: string) { return this.jsonApiService - .get>(this.baseUrl + meetingId) + .get>(this.baseUrl + meetingId) .pipe(map((response) => MeetingsMapper.fromMeetingGetResponse(response.data))); } } diff --git a/src/app/features/meetings/store/meetings.actions.ts b/src/app/features/meetings/store/meetings.actions.ts index 0e278fbbb..66ba74eae 100644 --- a/src/app/features/meetings/store/meetings.actions.ts +++ b/src/app/features/meetings/store/meetings.actions.ts @@ -1,4 +1,4 @@ -import { SearchFilters } from '@shared/models/filters'; +import { SearchFilters } from '@osf/shared/models'; export class GetAllMeetings { static readonly type = '[Meetings] Get All'; diff --git a/src/app/features/meetings/store/meetings.model.ts b/src/app/features/meetings/store/meetings.model.ts index 0e8b6b92d..9f6d5b1bc 100644 --- a/src/app/features/meetings/store/meetings.model.ts +++ b/src/app/features/meetings/store/meetings.model.ts @@ -1,5 +1,6 @@ -import { Meeting, MeetingSubmission } from '@osf/features/meetings/models'; -import { AsyncStateWithTotalCount } from '@shared/models/store'; +import { AsyncStateWithTotalCount } from '@osf/shared/models'; + +import { Meeting, MeetingSubmission } from '../models'; export interface MeetingsStateModel { meetings: AsyncStateWithTotalCount; diff --git a/src/app/features/meetings/store/meetings.selectors.ts b/src/app/features/meetings/store/meetings.selectors.ts index ae994992a..ce5ca9b2d 100644 --- a/src/app/features/meetings/store/meetings.selectors.ts +++ b/src/app/features/meetings/store/meetings.selectors.ts @@ -1,8 +1,9 @@ import { Selector } from '@ngxs/store'; -import { Meeting, MeetingSubmission } from '@osf/features/meetings/models'; -import { MeetingsStateModel } from '@osf/features/meetings/store/meetings.model'; -import { MeetingsState } from '@osf/features/meetings/store/meetings.state'; +import { Meeting, MeetingSubmission } from '../models'; + +import { MeetingsStateModel } from './meetings.model'; +import { MeetingsState } from './meetings.state'; export class MeetingsSelectors { @Selector([MeetingsState]) diff --git a/src/app/features/meetings/store/meetings.state.ts b/src/app/features/meetings/store/meetings.state.ts index cd63bb5c3..b92458b62 100644 --- a/src/app/features/meetings/store/meetings.state.ts +++ b/src/app/features/meetings/store/meetings.state.ts @@ -5,9 +5,10 @@ import { tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { MeetingsService } from '@osf/features/meetings/services'; -import { GetAllMeetings, GetMeetingById, GetMeetingSubmissions } from '@osf/features/meetings/store/meetings.actions'; -import { MeetingsStateModel } from '@osf/features/meetings/store/meetings.model'; +import { MeetingsService } from '../services'; + +import { GetAllMeetings, GetMeetingById, GetMeetingSubmissions } from './meetings.actions'; +import { MeetingsStateModel } from './meetings.model'; @State({ name: 'meetings', From 057acc3018f9ad90ec6b3301ba2ad7429dea9639 Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 8 Jul 2025 14:21:27 +0300 Subject: [PATCH 4/7] fix(meetings): added empty message --- .../pages/meeting-details/meeting-details.component.html | 5 +++++ .../meetings-landing/meetings-landing.component.html | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/app/features/meetings/pages/meeting-details/meeting-details.component.html b/src/app/features/meetings/pages/meeting-details/meeting-details.component.html index be1ba096d..c94fef53f 100644 --- a/src/app/features/meetings/pages/meeting-details/meeting-details.component.html +++ b/src/app/features/meetings/pages/meeting-details/meeting-details.component.html @@ -86,5 +86,10 @@ } + + + {{ 'common.search.noResultsFound' | translate }} + +
diff --git a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html index b05ceb797..18d8e8720 100644 --- a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html +++ b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.html @@ -4,7 +4,7 @@ [description]="'meetings.landing.description' | translate" /> -
+

{{ 'meetings.landing.submissionsNote' | translate }}

@@ -73,6 +73,11 @@ } + + + {{ 'common.search.noResultsFound' | translate }} + +
@@ -93,7 +98,7 @@

{{ 'meetings.landing.users.title' | translate }
@for (org of partnerOrganizations; track $index) { - + } From 78303959f3a389f0649b39276e1051f1a2b54fa1 Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 8 Jul 2025 18:36:11 +0300 Subject: [PATCH 5/7] fix(settings-page): updated tokens and dev apps --- .../components/footer/footer.component.scss | 17 ++--- .../forbidden-page.component.scss | 9 +-- .../nav-menu/nav-menu.component.scss | 8 +- .../page-not-found.component.scss | 9 +-- .../request-access.component.scss | 11 ++- .../components/topnav/topnav.component.html | 1 + .../meetings-landing.component.ts | 2 - ...developer-app-add-edit-form.component.html | 58 +++++++-------- .../developer-app-add-edit-form.component.ts | 69 +++++++++-------- .../developer-apps-container.component.html | 2 +- .../developer-apps-container.component.scss | 6 -- .../developer-apps-container.component.ts | 28 +++---- .../mappers/developer-app.mapper.ts | 12 +-- .../models/developer-app-json-api.model.ts | 39 ++++++++++ .../models/developer-apps.models.ts | 40 ---------- .../settings/developer-apps/models/index.ts | 1 + .../developer-apps-list.component.html | 32 ++++---- .../developer-apps-list.component.scss | 49 ++---------- .../developer-apps-list.component.ts | 18 +---- .../services/developer-apps.service.ts | 12 +-- .../store/developer-apps.selectors.ts | 9 ++- .../store/developer-apps.state-model.ts | 6 +- .../store/developer-apps.state.ts | 74 +++++++++++++------ .../settings-container.component.spec.ts | 6 +- .../token-created-dialog.component.spec.ts | 15 ---- .../tokens-list/tokens-list.component.html | 5 +- .../features/support/support.component.html | 1 - .../features/support/support.component.scss | 0 .../support/support.component.spec.ts | 26 ------- src/app/features/support/support.component.ts | 9 --- .../shared/constants/input-limits.const.ts | 6 ++ 31 files changed, 245 insertions(+), 335 deletions(-) create mode 100644 src/app/features/settings/developer-apps/models/developer-app-json-api.model.ts delete mode 100644 src/app/features/support/support.component.html delete mode 100644 src/app/features/support/support.component.scss delete mode 100644 src/app/features/support/support.component.spec.ts delete mode 100644 src/app/features/support/support.component.ts diff --git a/src/app/core/components/footer/footer.component.scss b/src/app/core/components/footer/footer.component.scss index 882994d28..d5aab8005 100644 --- a/src/app/core/components/footer/footer.component.scss +++ b/src/app/core/components/footer/footer.component.scss @@ -1,9 +1,8 @@ @use "assets/styles/mixins" as mix; -@use "assets/styles/variables" as var; .footer-nav, .footer-secondary-nav { - color: var.$dark-blue-1; + color: var(--dark-blue-1); padding: 0 1.5rem; .separator { @@ -11,20 +10,20 @@ } a { - color: var.$dark-blue-1; + color: var(--dark-blue-1); text-align: center; } .social-link { - background-color: var.$pr-blue-1; + background-color: var(--pr-blue-1); border-radius: mix.rem(6px); - color: var.$white; + color: var(--white); padding: mix.rem(6px); width: mix.rem(36px); height: mix.rem(36px); &:hover { - background-color: var.$pr-blue-3; + background-color: var(--pr-blue-3); text-decoration: none; } } @@ -35,13 +34,13 @@ } .footer-nav { - background-color: var.$bg-blue-3; + background-color: var(--bg-blue-3); .footer-socials { - gap: mix.rem(8px); + gap: 0.5rem; } } .footer-secondary-nav { - background: var.$bg-blue-2; + background: var(--bg-blue-2); } diff --git a/src/app/core/components/forbidden-page/forbidden-page.component.scss b/src/app/core/components/forbidden-page/forbidden-page.component.scss index c58add548..ea23a76d4 100644 --- a/src/app/core/components/forbidden-page/forbidden-page.component.scss +++ b/src/app/core/components/forbidden-page/forbidden-page.component.scss @@ -1,5 +1,4 @@ @use "assets/styles/mixins" as mix; -@use "assets/styles/variables" as var; :host { @include mix.flex-center; @@ -9,9 +8,9 @@ .container { position: relative; - background: var.$white; - border-radius: mix.rem(12px); - box-shadow: 0 2px 4px var.$grey-outline; - color: var.$dark-blue-1; + background: var(--white); + border-radius: 0.75rem; + box-shadow: 0 2px 4px var(--grey-outline); + color: var(--dark-blue-1); max-width: mix.rem(448px); } diff --git a/src/app/core/components/nav-menu/nav-menu.component.scss b/src/app/core/components/nav-menu/nav-menu.component.scss index 8dd89b684..c16ce5464 100644 --- a/src/app/core/components/nav-menu/nav-menu.component.scss +++ b/src/app/core/components/nav-menu/nav-menu.component.scss @@ -1,16 +1,10 @@ -@use "assets/styles/mixins" as mix; - .nav-menu { width: 250px; max-width: 250px; .active { background-color: var(--dark-blue-2); - border-radius: mix.rem(8px); + border-radius: 0.5rem; font-weight: 700; } - - ::ng-deep li[aria-label="navigation.registriesSubRoutes.registryDetails"] { - border-left-color: transparent !important; - } } diff --git a/src/app/core/components/page-not-found/page-not-found.component.scss b/src/app/core/components/page-not-found/page-not-found.component.scss index c58add548..ea23a76d4 100644 --- a/src/app/core/components/page-not-found/page-not-found.component.scss +++ b/src/app/core/components/page-not-found/page-not-found.component.scss @@ -1,5 +1,4 @@ @use "assets/styles/mixins" as mix; -@use "assets/styles/variables" as var; :host { @include mix.flex-center; @@ -9,9 +8,9 @@ .container { position: relative; - background: var.$white; - border-radius: mix.rem(12px); - box-shadow: 0 2px 4px var.$grey-outline; - color: var.$dark-blue-1; + background: var(--white); + border-radius: 0.75rem; + box-shadow: 0 2px 4px var(--grey-outline); + color: var(--dark-blue-1); max-width: mix.rem(448px); } diff --git a/src/app/core/components/request-access/request-access.component.scss b/src/app/core/components/request-access/request-access.component.scss index 008ad4a72..c27d0d43b 100644 --- a/src/app/core/components/request-access/request-access.component.scss +++ b/src/app/core/components/request-access/request-access.component.scss @@ -1,5 +1,4 @@ @use "assets/styles/mixins" as mix; -@use "assets/styles/variables" as var; :host { @include mix.flex-center; @@ -9,13 +8,13 @@ .container { position: relative; - background: var.$white; - border-radius: mix.rem(12px); - box-shadow: 0 2px 4px var.$grey-outline; - color: var.$dark-blue-1; + background: var(--white); + border-radius: 0.75rem; + box-shadow: 0 2px 4px var(--grey-outline); + color: var(--dark-blue-1); max-width: mix.rem(448px); .comment-input { - font-size: mix.rem(14px); + font-size: 0.875rem; } } diff --git a/src/app/core/components/topnav/topnav.component.html b/src/app/core/components/topnav/topnav.component.html index 18ba37568..96662a3a0 100644 --- a/src/app/core/components/topnav/topnav.component.html +++ b/src/app/core/components/topnav/topnav.component.html @@ -14,6 +14,7 @@ class="topnav-btn custom-light-hover flex" [icon]="isDrawerVisible() ? 'fas fa-close fa-2xl' : 'fas fa-bars fa-2xl'" severity="contrast" + ariaLabel="Toggle" (onClick)="toggleMenuVisibility()" >
diff --git a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts index 8a4949355..fbba344a2 100644 --- a/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts +++ b/src/app/features/meetings/pages/meetings-landing/meetings-landing.component.ts @@ -26,7 +26,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { parseQueryFilterParams } from '@core/helpers'; import { Meeting } from '@osf/features/meetings/models'; import { GetAllMeetings, MeetingsSelectors } from '@osf/features/meetings/store'; -import { IS_XSMALL } from '@osf/shared/utils'; import { SearchInputComponent, SubHeaderComponent } from '@shared/components'; import { TABLE_PARAMS } from '@shared/constants'; import { SortOrder } from '@shared/enums'; @@ -53,7 +52,6 @@ import { MEETINGS_FEATURE_CARDS, PARTNER_ORGANIZATIONS } from '../../constants'; }) export class MeetingsLandingComponent { @HostBinding('class') classes = 'flex-1 flex flex-column w-full h-full'; - readonly isXSmall = toSignal(inject(IS_XSMALL)); private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); private readonly destroyRef = inject(DestroyRef); diff --git a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html index 55785f2a8..e9652105b 100644 --- a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html +++ b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.html @@ -1,50 +1,48 @@
- - +
- - + [control]="appForm.controls[DeveloperAppFormFormControls.ProjectHomePageUrl]" + [maxLength]="inputLimits.link.maxLength" + >
- - +
- - + [control]="appForm.controls[DeveloperAppFormFormControls.AuthorizationCallbackUrl]" + [maxLength]="inputLimits.link.maxLength" + >
@if (isEditMode()) { - +
+ +
} @else { (null); - protected readonly isMobile = toSignal(inject(IS_XSMALL)); + private readonly router = inject(Router); + private readonly actions = createDispatchMap({ + createDeveloperApp: CreateDeveloperApp, + updateDeveloperApp: UpdateDeveloperApp, + }); + + inputLimits = InputLimits; + protected readonly dialogRef = inject(DynamicDialogRef); protected readonly DeveloperAppFormFormControls = DeveloperAppFormFormControls; protected readonly appForm: DeveloperAppForm = new FormGroup({ [DeveloperAppFormFormControls.AppName]: new FormControl('', { nonNullable: true, - validators: [Validators.required], + validators: [CustomValidators.requiredTrimmed(), Validators.maxLength(InputLimits.name.maxLength)], }), [DeveloperAppFormFormControls.ProjectHomePageUrl]: new FormControl('', { nonNullable: true, - validators: [Validators.required, CustomValidators.linkValidator()], + validators: [ + CustomValidators.requiredTrimmed(), + CustomValidators.linkValidator(), + Validators.maxLength(InputLimits.link.maxLength), + ], }), [DeveloperAppFormFormControls.AppDescription]: new FormControl('', { nonNullable: false, + validators: [Validators.maxLength(InputLimits.description.maxLength)], }), [DeveloperAppFormFormControls.AuthorizationCallbackUrl]: new FormControl('', { nonNullable: true, - validators: [Validators.required, CustomValidators.linkValidator()], + validators: [ + CustomValidators.requiredTrimmed(), + CustomValidators.linkValidator(), + Validators.maxLength(InputLimits.link.maxLength), + ], }), }); @@ -70,30 +83,16 @@ export class DeveloperAppAddEditFormComponent implements OnInit { } if (!this.isEditMode()) { - this.#store - .dispatch( - new CreateDeveloperApp({ - ...this.appForm.value, - } as DeveloperAppCreateUpdate) - ) - .subscribe({ - complete: () => { - this.dialogRef.close(); - }, - }); + this.actions.createDeveloperApp({ ...this.appForm.value } as DeveloperAppCreateUpdate).subscribe({ + complete: () => this.dialogRef.close(), + }); } else { - this.#store - .dispatch( - new UpdateDeveloperApp(this.initialValues()!.clientId, { - ...this.appForm.value, - id: this.initialValues()!.id, - } as DeveloperAppCreateUpdate) - ) - .subscribe({ - complete: () => { - this.#router.navigate(['settings/developer-apps']); - }, - }); + this.actions + .updateDeveloperApp(this.initialValues()!.clientId, { + ...this.appForm.value, + id: this.initialValues()!.id, + } as DeveloperAppCreateUpdate) + .subscribe({ complete: () => this.router.navigate(['settings/developer-apps']) }); } } } diff --git a/src/app/features/settings/developer-apps/developer-apps-container.component.html b/src/app/features/settings/developer-apps/developer-apps-container.component.html index ce1c1498a..87a9f114d 100644 --- a/src/app/features/settings/developer-apps/developer-apps-container.component.html +++ b/src/app/features/settings/developer-apps/developer-apps-container.component.html @@ -6,6 +6,6 @@ (buttonClick)="createDeveloperApp()" /> -
+
diff --git a/src/app/features/settings/developer-apps/developer-apps-container.component.scss b/src/app/features/settings/developer-apps/developer-apps-container.component.scss index 3a0294d96..e9dab5325 100644 --- a/src/app/features/settings/developer-apps/developer-apps-container.component.scss +++ b/src/app/features/settings/developer-apps/developer-apps-container.component.scss @@ -1,12 +1,6 @@ @use "assets/styles/mixins" as mix; -@use "assets/styles/variables" as var; :host { @include mix.flex-column; flex: 1; - - section { - @include mix.flex-column; - flex: 1; - } } diff --git a/src/app/features/settings/developer-apps/developer-apps-container.component.ts b/src/app/features/settings/developer-apps/developer-apps-container.component.ts index 0c0f6b4fc..e1275d6ee 100644 --- a/src/app/features/settings/developer-apps/developer-apps-container.component.ts +++ b/src/app/features/settings/developer-apps/developer-apps-container.component.ts @@ -9,7 +9,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { Router, RouterOutlet } from '@angular/router'; import { SubHeaderComponent } from '@osf/shared/components'; -import { IS_MEDIUM, IS_XSMALL } from '@osf/shared/utils'; +import { IS_MEDIUM } from '@osf/shared/utils'; import { DeveloperAppAddEditFormComponent } from './components'; @@ -22,29 +22,23 @@ import { DeveloperAppAddEditFormComponent } from './components'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class DeveloperAppsContainerComponent { - #dialogService = inject(DialogService); - #router = inject(Router); - #isXSmall = toSignal(inject(IS_XSMALL)); - #isMedium = toSignal(inject(IS_MEDIUM)); - #translateService = inject(TranslateService); + private readonly dialogService = inject(DialogService); + private readonly router = inject(Router); + private readonly isMedium = toSignal(inject(IS_MEDIUM)); + private readonly translateService = inject(TranslateService); protected readonly isBaseRoute = toSignal( - this.#router.events.pipe(map(() => this.#router.url === '/settings/developer-apps')), - { initialValue: this.#router.url === '/settings/developer-apps' } + this.router.events.pipe(map(() => this.router.url === '/settings/developer-apps')), + { initialValue: this.router.url === '/settings/developer-apps' } ); createDeveloperApp(): void { - let dialogWidth = '850px'; - if (this.#isXSmall()) { - dialogWidth = '345px'; - } else if (this.#isMedium()) { - dialogWidth = '500px'; - } - - this.#dialogService.open(DeveloperAppAddEditFormComponent, { + const dialogWidth = this.isMedium() ? '500px' : '340px'; + + this.dialogService.open(DeveloperAppAddEditFormComponent, { width: dialogWidth, focusOnShow: false, - header: this.#translateService.instant('settings.developerApps.form.createTitle'), + header: this.translateService.instant('settings.developerApps.form.createTitle'), closeOnEscape: true, modal: true, closable: true, diff --git a/src/app/features/settings/developer-apps/mappers/developer-app.mapper.ts b/src/app/features/settings/developer-apps/mappers/developer-app.mapper.ts index cc67667b1..14c7139a9 100644 --- a/src/app/features/settings/developer-apps/mappers/developer-app.mapper.ts +++ b/src/app/features/settings/developer-apps/mappers/developer-app.mapper.ts @@ -1,13 +1,13 @@ import { DeveloperApp, - DeveloperAppCreateRequest, + DeveloperAppCreateRequestJsonApi, DeveloperAppCreateUpdate, - DeveloperAppGetResponse, - DeveloperAppUpdateRequest, + DeveloperAppGetResponseJsonApi, + DeveloperAppUpdateRequestJsonApi, } from '../models'; export class DeveloperAppMapper { - static toCreateRequest(developerCreate: DeveloperAppCreateUpdate): DeveloperAppCreateRequest { + static toCreateRequest(developerCreate: DeveloperAppCreateUpdate): DeveloperAppCreateRequestJsonApi { return { data: { attributes: { @@ -21,7 +21,7 @@ export class DeveloperAppMapper { }; } - static toUpdateRequest(developerUpdate: DeveloperAppCreateUpdate): DeveloperAppUpdateRequest { + static toUpdateRequest(developerUpdate: DeveloperAppCreateUpdate): DeveloperAppUpdateRequestJsonApi { return { data: { id: developerUpdate.id!, @@ -48,7 +48,7 @@ export class DeveloperAppMapper { }; } - static fromGetResponse(response: DeveloperAppGetResponse): DeveloperApp { + static fromGetResponse(response: DeveloperAppGetResponseJsonApi): DeveloperApp { return { id: response.id, name: response.attributes.name, diff --git a/src/app/features/settings/developer-apps/models/developer-app-json-api.model.ts b/src/app/features/settings/developer-apps/models/developer-app-json-api.model.ts new file mode 100644 index 000000000..c5cd34151 --- /dev/null +++ b/src/app/features/settings/developer-apps/models/developer-app-json-api.model.ts @@ -0,0 +1,39 @@ +import { StringOrNull } from '@osf/core/helpers'; + +export interface DeveloperAppCreateRequestJsonApi { + data: { + type: 'applications'; + attributes: { + name: string; + description: StringOrNull; + home_url: string; + callback_url: string; + }; + }; +} + +export interface DeveloperAppUpdateRequestJsonApi { + data: { + id: string; + type: 'applications'; + attributes: { + name: string; + description: StringOrNull; + home_url: string; + callback_url: string; + }; + }; +} + +export interface DeveloperAppGetResponseJsonApi { + id: string; + type: 'applications'; + attributes: { + name: string; + description: string; + home_url: string; + callback_url: string; + client_id: string; + client_secret: string; + }; +} diff --git a/src/app/features/settings/developer-apps/models/developer-apps.models.ts b/src/app/features/settings/developer-apps/models/developer-apps.models.ts index 73a9a92e1..d75878dd2 100644 --- a/src/app/features/settings/developer-apps/models/developer-apps.models.ts +++ b/src/app/features/settings/developer-apps/models/developer-apps.models.ts @@ -1,6 +1,5 @@ import { StringOrNull } from '@osf/core/helpers'; -//Domain models export interface DeveloperApp { id: string; name: string; @@ -18,42 +17,3 @@ export interface DeveloperAppCreateUpdate { projHomePageUrl: string; authorizationCallbackUrl: string; } - -// API Request/Response Models -export interface DeveloperAppCreateRequest { - data: { - type: 'applications'; - attributes: { - name: string; - description: StringOrNull; - home_url: string; - callback_url: string; - }; - }; -} - -export interface DeveloperAppUpdateRequest { - data: { - id: string; - type: 'applications'; - attributes: { - name: string; - description: StringOrNull; - home_url: string; - callback_url: string; - }; - }; -} - -export interface DeveloperAppGetResponse { - id: string; - type: 'applications'; - attributes: { - name: string; - description: string; - home_url: string; - callback_url: string; - client_id: string; - client_secret: string; - }; -} diff --git a/src/app/features/settings/developer-apps/models/index.ts b/src/app/features/settings/developer-apps/models/index.ts index 3c387f1e6..362372cf6 100644 --- a/src/app/features/settings/developer-apps/models/index.ts +++ b/src/app/features/settings/developer-apps/models/index.ts @@ -1,2 +1,3 @@ export * from './developer-app-form.model'; +export * from './developer-app-json-api.model'; export * from './developer-apps.models'; diff --git a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.html b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.html index 4d43c8cd8..4c7a14dd3 100644 --- a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.html +++ b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.html @@ -1,36 +1,34 @@ -
+

{{ 'settings.developerApps.list.description' | translate }}

-
+
+ @if (isLoading()) { + @for (_ of [1, 2, 3]; track $index) { + + + + } + } + @for (developerApp of developerApplications(); track $index) { -
- +
+

{{ developerApp.name }}

-
+ +
-
+

} - - @if (isLoading()) { - @for (_ of [1, 2, 3]; track $index) { - -
- - -
-
- } - }
diff --git a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.scss b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.scss index 13e3d1ff1..89734679f 100644 --- a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.scss +++ b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.scss @@ -1,49 +1,16 @@ -@use "assets/styles/variables" as var; @use "assets/styles/mixins" as mix; :host { @include mix.flex-column; flex: 1; +} - .content-container { - flex: 1; - padding: 1.7rem; - color: var.$dark-blue-1; - background-color: var.$white; - - &.mobile { - padding: 1rem; - } - - p { - margin-bottom: 1.7rem; - } - - .applications-container { - @include mix.flex-column; - gap: 0.85rem; - - p-card { - .card-body { - &.mobile { - @include mix.flex-column; - gap: 0.85rem; - - a { - align-self: flex-start; - } - - .button-container { - align-self: flex-end; - width: 50%; - } - } +.content-container { + background-color: var(--white); +} - &:not(.mobile) { - @include mix.flex-center-between; - } - } - } - } - } +.app-link { + color: var(--dark-blue-1); + font-size: 1.125rem; + font-weight: 700; } diff --git a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts index 87c815d9b..7389509eb 100644 --- a/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts +++ b/src/app/features/settings/developer-apps/pages/developer-apps-list/developer-apps-list.component.ts @@ -6,12 +6,10 @@ import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; import { Skeleton } from 'primeng/skeleton'; -import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core'; -import { toSignal } from '@angular/core/rxjs-interop'; +import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; import { RouterLink } from '@angular/router'; import { CustomConfirmationService } from '@osf/shared/services'; -import { IS_XSMALL } from '@osf/shared/utils'; import { DeveloperApp } from '../../models'; import { DeleteDeveloperApp, DeveloperAppsSelectors, GetDeveloperApps } from '../../store'; @@ -30,22 +28,12 @@ export class DeveloperAppsListComponent implements OnInit { }); private readonly customConfirmationService = inject(CustomConfirmationService); - protected readonly isLoading = signal(false); - protected readonly isXSmall = toSignal(inject(IS_XSMALL)); + protected readonly isLoading = select(DeveloperAppsSelectors.isLoading); readonly developerApplications = select(DeveloperAppsSelectors.getDeveloperApps); ngOnInit(): void { if (!this.developerApplications().length) { - this.isLoading.set(true); - - this.actions.getDeveloperApps().subscribe({ - complete: () => { - this.isLoading.set(false); - }, - error: () => { - this.isLoading.set(false); - }, - }); + this.actions.getDeveloperApps(); } } diff --git a/src/app/features/settings/developer-apps/services/developer-apps.service.ts b/src/app/features/settings/developer-apps/services/developer-apps.service.ts index 59c902f4a..72931dd57 100644 --- a/src/app/features/settings/developer-apps/services/developer-apps.service.ts +++ b/src/app/features/settings/developer-apps/services/developer-apps.service.ts @@ -6,7 +6,7 @@ import { JsonApiResponse } from '@osf/core/models'; import { JsonApiService } from '@osf/core/services'; import { DeveloperAppMapper } from '../mappers'; -import { DeveloperApp, DeveloperAppCreateUpdate, DeveloperAppGetResponse } from '../models'; +import { DeveloperApp, DeveloperAppCreateUpdate, DeveloperAppGetResponseJsonApi } from '../models'; import { environment } from 'src/environments/environment'; @@ -18,7 +18,7 @@ export class DeveloperApplicationsService { baseUrl = `${environment.apiUrl}/applications/`; getApplications(): Observable { - return this.jsonApiService.get>(this.baseUrl).pipe( + return this.jsonApiService.get>(this.baseUrl).pipe( map((responses) => { return responses.data.map((response) => DeveloperAppMapper.fromGetResponse(response)); }) @@ -27,7 +27,7 @@ export class DeveloperApplicationsService { getApplicationDetails(clientId: string): Observable { return this.jsonApiService - .get>(this.baseUrl + clientId + '/') + .get>(this.baseUrl + clientId + '/') .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response.data))); } @@ -35,7 +35,7 @@ export class DeveloperApplicationsService { const request = DeveloperAppMapper.toCreateRequest(developerAppCreate); return this.jsonApiService - .post>(this.baseUrl, request) + .post>(this.baseUrl, request) .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response.data))); } @@ -43,7 +43,7 @@ export class DeveloperApplicationsService { const request = DeveloperAppMapper.toUpdateRequest(developerAppUpdate); return this.jsonApiService - .patch(this.baseUrl + clientId + '/', request) + .patch(this.baseUrl + clientId + '/', request) .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response))); } @@ -51,7 +51,7 @@ export class DeveloperApplicationsService { const request = DeveloperAppMapper.toResetSecretRequest(clientId); return this.jsonApiService - .patch(this.baseUrl + clientId + '/', request) + .patch(this.baseUrl + clientId + '/', request) .pipe(map((response) => DeveloperAppMapper.fromGetResponse(response))); } diff --git a/src/app/features/settings/developer-apps/store/developer-apps.selectors.ts b/src/app/features/settings/developer-apps/store/developer-apps.selectors.ts index 5e362539c..ea87caa9c 100644 --- a/src/app/features/settings/developer-apps/store/developer-apps.selectors.ts +++ b/src/app/features/settings/developer-apps/store/developer-apps.selectors.ts @@ -8,11 +8,16 @@ import { DeveloperAppsStateModel } from './developer-apps.state-model'; export class DeveloperAppsSelectors { @Selector([DeveloperAppsState]) static getDeveloperApps(state: DeveloperAppsStateModel): DeveloperApp[] { - return state.developerApps; + return state.data; + } + + @Selector([DeveloperAppsState]) + static isLoading(state: DeveloperAppsStateModel): boolean { + return state.isLoading; } @Selector([DeveloperAppsState]) static getDeveloperAppDetails(state: DeveloperAppsStateModel): (clientId: string) => DeveloperApp | undefined { - return (clientId: string) => state.developerApps.find((app) => app.clientId === clientId); + return (clientId: string) => state.data.find((app) => app.clientId === clientId); } } diff --git a/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts b/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts index 8dcd50595..1e8fa57b7 100644 --- a/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts +++ b/src/app/features/settings/developer-apps/store/developer-apps.state-model.ts @@ -1,5 +1,5 @@ +import { AsyncStateModel } from '@osf/shared/models'; + import { DeveloperApp } from '../models'; -export interface DeveloperAppsStateModel { - developerApps: DeveloperApp[]; -} +export type DeveloperAppsStateModel = AsyncStateModel; diff --git a/src/app/features/settings/developer-apps/store/developer-apps.state.ts b/src/app/features/settings/developer-apps/store/developer-apps.state.ts index 708a6e5b8..742b9883a 100644 --- a/src/app/features/settings/developer-apps/store/developer-apps.state.ts +++ b/src/app/features/settings/developer-apps/store/developer-apps.state.ts @@ -1,7 +1,7 @@ import { Action, State, StateContext } from '@ngxs/store'; import { insertItem, patch, removeItem, updateItem } from '@ngxs/store/operators'; -import { of, tap } from 'rxjs'; +import { catchError, of, tap, throwError } from 'rxjs'; import { inject, Injectable } from '@angular/core'; @@ -21,91 +21,121 @@ import { DeveloperAppsStateModel } from './developer-apps.state-model'; @State({ name: 'developerApps', defaults: { - developerApps: [], + data: [], + isLoading: false, + error: null, }, }) @Injectable() export class DeveloperAppsState { - #developerAppsService = inject(DeveloperApplicationsService); + private readonly developerAppsService = inject(DeveloperApplicationsService); @Action(GetDeveloperApps) getDeveloperApps(ctx: StateContext) { - return this.#developerAppsService.getApplications().pipe( + const state = ctx.getState(); + ctx.patchState({ ...state.data, isLoading: true, error: null }); + + return this.developerAppsService.getApplications().pipe( tap((developerApps) => { - ctx.setState(patch({ developerApps })); - }) + ctx.setState(patch({ data: developerApps, isLoading: false, error: null })); + }), + catchError((error) => this.handleError(ctx, error)) ); } @Action(GetDeveloperAppDetails) getDeveloperAppDetails(ctx: StateContext, action: GetDeveloperAppDetails) { const state = ctx.getState(); - const developerAppFromState = state.developerApps.find((app: DeveloperApp) => app.clientId === action.clientId); + const developerAppFromState = state.data.find((app: DeveloperApp) => app.clientId === action.clientId); if (developerAppFromState) { return of(developerAppFromState); } - return this.#developerAppsService.getApplicationDetails(action.clientId).pipe( + return this.developerAppsService.getApplicationDetails(action.clientId).pipe( tap((fetchedApp) => { ctx.setState( patch({ - developerApps: insertItem(fetchedApp), + data: insertItem(fetchedApp), + isLoading: false, + error: null, }) ); - }) + }), + catchError((error) => this.handleError(ctx, error)) ); } @Action(CreateDeveloperApp) createDeveloperApp(ctx: StateContext, action: CreateDeveloperApp) { - return this.#developerAppsService.createApplication(action.developerAppCreate).pipe( + return this.developerAppsService.createApplication(action.developerAppCreate).pipe( tap((newApp) => { ctx.setState( patch({ - developerApps: insertItem(newApp, 0), + data: insertItem(newApp, 0), + isLoading: false, + error: null, }) ); - }) + }), + catchError((error) => this.handleError(ctx, error)) ); } @Action(UpdateDeveloperApp) updateDeveloperApp(ctx: StateContext, action: UpdateDeveloperApp) { - return this.#developerAppsService.updateApp(action.clientId, action.developerAppUpdate).pipe( + return this.developerAppsService.updateApp(action.clientId, action.developerAppUpdate).pipe( tap((updatedApp) => { ctx.setState( patch({ - developerApps: updateItem((app) => app.clientId === action.clientId, updatedApp), + data: updateItem((app) => app.clientId === action.clientId, updatedApp), + isLoading: false, + error: null, }) ); - }) + }), + catchError((error) => this.handleError(ctx, error)) ); } @Action(ResetClientSecret) resetClientSecret(ctx: StateContext, action: ResetClientSecret) { - return this.#developerAppsService.resetClientSecret(action.clientId).pipe( + return this.developerAppsService.resetClientSecret(action.clientId).pipe( tap((updatedApp) => { ctx.setState( patch({ - developerApps: updateItem((app) => app.clientId === action.clientId, updatedApp), + data: updateItem((app) => app.clientId === action.clientId, updatedApp), + isLoading: false, + error: null, }) ); - }) + }), + catchError((error) => this.handleError(ctx, error)) ); } @Action(DeleteDeveloperApp) deleteDeveloperApp(ctx: StateContext, action: DeleteDeveloperApp) { - return this.#developerAppsService.deleteApplication(action.clientId).pipe( + return this.developerAppsService.deleteApplication(action.clientId).pipe( tap(() => { ctx.setState( patch({ - developerApps: removeItem((app) => app.clientId === action.clientId), + data: removeItem((app) => app.clientId === action.clientId), + isLoading: false, + error: null, }) ); - }) + }), + catchError((error) => this.handleError(ctx, error)) ); } + + private handleError(ctx: StateContext, error: Error) { + ctx.patchState({ + ...ctx.getState().data, + isLoading: false, + error: error.message, + }); + return throwError(() => error); + } } diff --git a/src/app/features/settings/settings-container.component.spec.ts b/src/app/features/settings/settings-container.component.spec.ts index c3281b709..5a0b6de71 100644 --- a/src/app/features/settings/settings-container.component.spec.ts +++ b/src/app/features/settings/settings-container.component.spec.ts @@ -1,12 +1,8 @@ -import { MockProvider } from 'ng-mocks'; - import { BehaviorSubject } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { IS_WEB } from '@osf/shared/utils'; - import { SettingsContainerComponent } from './settings-container.component'; describe('SettingsContainerComponent', () => { @@ -19,7 +15,7 @@ describe('SettingsContainerComponent', () => { await TestBed.configureTestingModule({ imports: [SettingsContainerComponent], - providers: [MockProvider(IS_WEB, isWebSubject)], + providers: [], }).compileComponents(); fixture = TestBed.createComponent(SettingsContainerComponent); diff --git a/src/app/features/settings/tokens/components/token-created-dialog/token-created-dialog.component.spec.ts b/src/app/features/settings/tokens/components/token-created-dialog/token-created-dialog.component.spec.ts index bb52e643a..01b5e594c 100644 --- a/src/app/features/settings/tokens/components/token-created-dialog/token-created-dialog.component.spec.ts +++ b/src/app/features/settings/tokens/components/token-created-dialog/token-created-dialog.component.spec.ts @@ -49,21 +49,6 @@ describe('TokenCreatedDialogComponent', () => { expect(tokenInput.value).toBe(mockTokenValue); }); - it('should show copy notification when token is copied', () => { - expect(fixture.componentInstance['tokenCopiedNotificationVisible']()).toBe(false); - fixture.componentInstance['tokenCopiedToClipboard'](); - expect(fixture.componentInstance['tokenCopiedNotificationVisible']()).toBe(true); - }); - - it('should hide copy notification after 2 seconds', () => { - jest.useFakeTimers(); - fixture.componentInstance['tokenCopiedToClipboard'](); - expect(fixture.componentInstance['tokenCopiedNotificationVisible']()).toBe(true); - jest.advanceTimersByTime(2000); - expect(fixture.componentInstance['tokenCopiedNotificationVisible']()).toBe(false); - jest.useRealTimers(); - }); - it('should set input selection range to 0 after render', () => { const input = fixture.debugElement.query(By.css('input')).nativeElement; expect(input.selectionStart).toBe(0); diff --git a/src/app/features/settings/tokens/pages/tokens-list/tokens-list.component.html b/src/app/features/settings/tokens/pages/tokens-list/tokens-list.component.html index 92fd4fdfa..10abcf014 100644 --- a/src/app/features/settings/tokens/pages/tokens-list/tokens-list.component.html +++ b/src/app/features/settings/tokens/pages/tokens-list/tokens-list.component.html @@ -7,10 +7,7 @@ @if (isLoading()) { @for (_ of [1, 2, 3]; track $index) { -
- - -
+
} } @else { diff --git a/src/app/features/support/support.component.html b/src/app/features/support/support.component.html deleted file mode 100644 index 921c7277b..000000000 --- a/src/app/features/support/support.component.html +++ /dev/null @@ -1 +0,0 @@ -

Support page

diff --git a/src/app/features/support/support.component.scss b/src/app/features/support/support.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/features/support/support.component.spec.ts b/src/app/features/support/support.component.spec.ts deleted file mode 100644 index 198ad46d2..000000000 --- a/src/app/features/support/support.component.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { TranslatePipe, TranslateService } from '@ngx-translate/core'; -import { MockPipe, MockProvider } from 'ng-mocks'; - -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SupportComponent } from './support.component'; - -describe('SupportComponent', () => { - let component: SupportComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [SupportComponent, MockPipe(TranslatePipe)], - providers: [MockProvider(TranslateService)], - }).compileComponents(); - - fixture = TestBed.createComponent(SupportComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/features/support/support.component.ts b/src/app/features/support/support.component.ts deleted file mode 100644 index b49ea2bf8..000000000 --- a/src/app/features/support/support.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'osf-support', - imports: [], - templateUrl: './support.component.html', - styleUrl: './support.component.scss', -}) -export class SupportComponent {} diff --git a/src/app/shared/constants/input-limits.const.ts b/src/app/shared/constants/input-limits.const.ts index 87842ad4d..257b41555 100644 --- a/src/app/shared/constants/input-limits.const.ts +++ b/src/app/shared/constants/input-limits.const.ts @@ -22,4 +22,10 @@ export const InputLimits = { requestAccessComment: { maxLength: 250, }, + link: { + maxLength: 200, + }, + description: { + maxLength: 250, + }, }; From e319c7815e566275cfa1e79c17324dc53e47755f Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 8 Jul 2025 19:13:27 +0300 Subject: [PATCH 6/7] fix(nav-menu): fixed --- .../nav-menu/nav-menu.component.html | 9 ++++---- .../components/nav-menu/nav-menu.component.ts | 6 +++++- src/app/core/constants/nav-items.constant.ts | 21 +++++++++++-------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/app/core/components/nav-menu/nav-menu.component.html b/src/app/core/components/nav-menu/nav-menu.component.html index 558eabef3..6a1540ea4 100644 --- a/src/app/core/components/nav-menu/nav-menu.component.html +++ b/src/app/core/components/nav-menu/nav-menu.component.html @@ -1,13 +1,12 @@