@if (isWeb()) {
-
Sort by:
+
{{ 'collections.filters.sortBy' | translate }}:
-
+ [(selectedValue)]="selectedSort"
+ >
} @else {
@if (isAnyFilterOptions()) {
diff --git a/src/app/features/search/components/resources/resources.component.scss b/src/app/features/search/components/resources/resources.component.scss
index c637cba9b..728af69d1 100644
--- a/src/app/features/search/components/resources/resources.component.scss
+++ b/src/app/features/search/components/resources/resources.component.scss
@@ -1,67 +1,65 @@
@use "assets/styles/variables" as var;
-:host {
- h3 {
- color: var.$pr-blue-1;
- }
+h3 {
+ color: var.$pr-blue-1;
+}
- .sorting-container {
- display: flex;
- align-items: center;
+.sorting-container {
+ display: flex;
+ align-items: center;
- h3 {
- color: var.$dark-blue-1;
- font-weight: 400;
- text-wrap: nowrap;
- margin-right: 0.5rem;
- }
+ h3 {
+ color: var.$dark-blue-1;
+ font-weight: 400;
+ text-wrap: nowrap;
+ margin-right: 0.5rem;
}
+}
- .filter-full-size {
- flex: 1;
- }
+.filter-full-size {
+ flex: 1;
+}
- .sort-card {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 44px;
- border: 1px solid var.$grey-2;
- border-radius: 12px;
- padding: 0 1.7rem 0 1.7rem;
- cursor: pointer;
- }
+.sort-card {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 44px;
+ border: 1px solid var.$grey-2;
+ border-radius: 12px;
+ padding: 0 1.7rem 0 1.7rem;
+ cursor: pointer;
+}
- .card-selected {
- background: var.$bg-blue-2;
- }
+.card-selected {
+ background: var.$bg-blue-2;
+}
- .filters-resources-web {
- .resources-container {
- flex: 1;
+.filters-resources-web {
+ .resources-container {
+ flex: 1;
- .resources-list {
- width: 100%;
- display: flex;
- flex-direction: column;
- row-gap: 0.85rem;
- }
+ .resources-list {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ row-gap: 0.85rem;
+ }
- .switch-icon {
- &:hover {
- cursor: pointer;
- }
+ .switch-icon {
+ &:hover {
+ cursor: pointer;
}
+ }
- .icon-disabled {
- opacity: 0.5;
- cursor: none;
- }
+ .icon-disabled {
+ opacity: 0.5;
+ cursor: none;
+ }
- .icon-active {
- fill: var.$grey-1;
- }
+ .icon-active {
+ fill: var.$grey-1;
}
}
}
diff --git a/src/app/features/search/components/resources/resources.component.ts b/src/app/features/search/components/resources/resources.component.ts
index 8b6a8494a..25a91a79c 100644
--- a/src/app/features/search/components/resources/resources.component.ts
+++ b/src/app/features/search/components/resources/resources.component.ts
@@ -1,9 +1,10 @@
-import { Store } from '@ngxs/store';
+import { select, Store } from '@ngxs/store';
+
+import { TranslatePipe } from '@ngx-translate/core';
import { AccordionModule } from 'primeng/accordion';
import { Button } from 'primeng/button';
import { DataViewModule } from 'primeng/dataview';
-import { Select } from 'primeng/select';
import { TableModule } from 'primeng/table';
import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core';
@@ -13,8 +14,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FilterChipsComponent, ResourceFiltersComponent } from '@osf/features/search/components';
import { ResourceTab } from '@osf/shared/enums';
import { IS_WEB, IS_XSMALL } from '@osf/shared/utils';
-import { ResourceCardComponent } from '@shared/components';
-import { searchSortingOptions } from '@shared/constants';
+import { ResourceCardComponent, SelectComponent } from '@shared/components';
+import { SEARCH_TAB_OPTIONS, searchSortingOptions } from '@shared/constants';
import { GetResourcesByLink, SearchSelectors, SetResourceTab, SetSortBy } from '../../store';
import { ResourceFiltersOptionsSelectors } from '../filters/store';
@@ -30,34 +31,35 @@ import { ResourceFiltersSelectors } from '../resource-filters/store';
TableModule,
DataViewModule,
FilterChipsComponent,
- Select,
ResourceCardComponent,
Button,
+ TranslatePipe,
+ SelectComponent,
],
templateUrl: './resources.component.html',
styleUrl: './resources.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ResourcesComponent {
- readonly #store = inject(Store);
+ readonly store = inject(Store);
protected readonly searchSortingOptions = searchSortingOptions;
- selectedTabStore = this.#store.selectSignal(SearchSelectors.getResourceTab);
- searchCount = this.#store.selectSignal(SearchSelectors.getResourcesCount);
- resources = this.#store.selectSignal(SearchSelectors.getResources);
- sortBy = this.#store.selectSignal(SearchSelectors.getSortBy);
- first = this.#store.selectSignal(SearchSelectors.getFirst);
- next = this.#store.selectSignal(SearchSelectors.getNext);
- prev = this.#store.selectSignal(SearchSelectors.getPrevious);
- isMyProfilePage = this.#store.selectSignal(SearchSelectors.getIsMyProfile);
+ selectedTabStore = select(SearchSelectors.getResourceTab);
+ searchCount = select(SearchSelectors.getResourcesCount);
+ resources = select(SearchSelectors.getResources);
+ sortBy = select(SearchSelectors.getSortBy);
+ first = select(SearchSelectors.getFirst);
+ next = select(SearchSelectors.getNext);
+ prev = select(SearchSelectors.getPrevious);
+ isMyProfilePage = select(SearchSelectors.getIsMyProfile);
isWeb = toSignal(inject(IS_WEB));
isFiltersOpen = signal(false);
isSortingOpen = signal(false);
- protected filters = this.#store.selectSignal(ResourceFiltersSelectors.getAllFilters);
- protected filtersOptions = this.#store.selectSignal(ResourceFiltersOptionsSelectors.getAllOptions);
+ protected filters = select(ResourceFiltersSelectors.getAllFilters);
+ protected filtersOptions = select(ResourceFiltersOptionsSelectors.getAllOptions);
protected isAnyFilterSelected = computed(() => {
return (
this.filters().creator.value ||
@@ -91,17 +93,9 @@ export class ResourcesComponent {
protected selectedSort = signal('');
protected selectedTab = signal(ResourceTab.All);
- protected readonly tabsOptions = [
- { label: 'All', value: ResourceTab.All },
- { label: 'Projects', value: ResourceTab.Projects },
- { label: 'Registrations', value: ResourceTab.Registrations },
- { label: 'Preprints', value: ResourceTab.Preprints },
- { label: 'Files', value: ResourceTab.Files },
- { label: 'Users', value: ResourceTab.Users },
- ];
+ protected readonly tabsOptions = SEARCH_TAB_OPTIONS;
constructor() {
- // if new value for sorting in store, update value in dropdown
effect(() => {
const storeValue = this.sortBy();
const currentInput = untracked(() => this.selectedSort());
@@ -111,13 +105,12 @@ export class ResourcesComponent {
}
});
- // if the sorting was changed, set new value to store
effect(() => {
const chosenValue = this.selectedSort();
const storeValue = untracked(() => this.sortBy());
if (chosenValue !== storeValue) {
- this.#store.dispatch(new SetSortBy(chosenValue));
+ this.store.dispatch(new SetSortBy(chosenValue));
}
});
@@ -135,14 +128,13 @@ export class ResourcesComponent {
const storeValue = untracked(() => this.selectedTabStore());
if (chosenValue !== storeValue) {
- this.#store.dispatch(new SetResourceTab(chosenValue));
+ this.store.dispatch(new SetResourceTab(chosenValue));
}
});
}
- // pagination
switchPage(link: string) {
- this.#store.dispatch(new GetResourcesByLink(link));
+ this.store.dispatch(new GetResourcesByLink(link));
}
openFilters() {
diff --git a/src/app/features/search/search.component.scss b/src/app/features/search/search.component.scss
index 1ffdbbbe2..7fb5db331 100644
--- a/src/app/features/search/search.component.scss
+++ b/src/app/features/search/search.component.scss
@@ -1,5 +1,3 @@
-@use "assets/styles/variables" as var;
-
:host {
display: flex;
flex-direction: column;
@@ -9,5 +7,5 @@
.resources {
position: relative;
- background: var.$white;
+ background: var(--white);
}
diff --git a/src/app/features/search/search.component.ts b/src/app/features/search/search.component.ts
index 7741190a6..b7a4ee803 100644
--- a/src/app/features/search/search.component.ts
+++ b/src/app/features/search/search.component.ts
@@ -1,4 +1,4 @@
-import { Store } from '@ngxs/store';
+import { select, Store } from '@ngxs/store';
import { TranslatePipe } from '@ngx-translate/core';
@@ -53,25 +53,25 @@ import { GetResources, ResetSearchState, SearchSelectors, SetResourceTab, SetSea
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SearchComponent implements OnDestroy {
- readonly #store = inject(Store);
+ readonly store = inject(Store);
protected searchControl = new FormControl('');
protected readonly isSmall = toSignal(inject(IS_SMALL));
private readonly destroyRef = inject(DestroyRef);
- protected readonly creatorsFilter = this.#store.selectSignal(ResourceFiltersSelectors.getCreator);
- protected readonly dateCreatedFilter = this.#store.selectSignal(ResourceFiltersSelectors.getDateCreated);
- protected readonly funderFilter = this.#store.selectSignal(ResourceFiltersSelectors.getFunder);
- protected readonly subjectFilter = this.#store.selectSignal(ResourceFiltersSelectors.getSubject);
- protected readonly licenseFilter = this.#store.selectSignal(ResourceFiltersSelectors.getLicense);
- protected readonly resourceTypeFilter = this.#store.selectSignal(ResourceFiltersSelectors.getResourceType);
- protected readonly institutionFilter = this.#store.selectSignal(ResourceFiltersSelectors.getInstitution);
- protected readonly providerFilter = this.#store.selectSignal(ResourceFiltersSelectors.getProvider);
- protected readonly partOfCollectionFilter = this.#store.selectSignal(ResourceFiltersSelectors.getPartOfCollection);
- protected searchStoreValue = this.#store.selectSignal(SearchSelectors.getSearchText);
- protected resourcesTabStoreValue = this.#store.selectSignal(SearchSelectors.getResourceTab);
- protected sortByStoreValue = this.#store.selectSignal(SearchSelectors.getSortBy);
+ protected readonly creatorsFilter = select(ResourceFiltersSelectors.getCreator);
+ protected readonly dateCreatedFilter = select(ResourceFiltersSelectors.getDateCreated);
+ protected readonly funderFilter = select(ResourceFiltersSelectors.getFunder);
+ protected readonly subjectFilter = select(ResourceFiltersSelectors.getSubject);
+ protected readonly licenseFilter = select(ResourceFiltersSelectors.getLicense);
+ protected readonly resourceTypeFilter = select(ResourceFiltersSelectors.getResourceType);
+ protected readonly institutionFilter = select(ResourceFiltersSelectors.getInstitution);
+ protected readonly providerFilter = select(ResourceFiltersSelectors.getProvider);
+ protected readonly partOfCollectionFilter = select(ResourceFiltersSelectors.getPartOfCollection);
+ protected searchStoreValue = select(SearchSelectors.getSearchText);
+ protected resourcesTabStoreValue = select(SearchSelectors.getResourceTab);
+ protected sortByStoreValue = select(SearchSelectors.getSortBy);
protected readonly resourceTabOptions = SEARCH_TAB_OPTIONS;
protected selectedTab: ResourceTab = ResourceTab.All;
@@ -92,7 +92,7 @@ export class SearchComponent implements OnDestroy {
this.searchStoreValue();
this.resourcesTabStoreValue();
this.sortByStoreValue();
- this.#store.dispatch(GetResources);
+ this.store.dispatch(GetResources);
});
effect(() => {
@@ -114,14 +114,14 @@ export class SearchComponent implements OnDestroy {
}
ngOnDestroy(): void {
- this.#store.dispatch(ResetFiltersState);
- this.#store.dispatch(ResetSearchState);
+ this.store.dispatch(ResetFiltersState);
+ this.store.dispatch(ResetSearchState);
}
onTabChange(index: ResourceTab): void {
- this.#store.dispatch(new SetResourceTab(index));
+ this.store.dispatch(new SetResourceTab(index));
this.selectedTab = index;
- this.#store.dispatch(GetAllOptions);
+ this.store.dispatch(GetAllOptions);
}
showTutorial() {
@@ -132,8 +132,8 @@ export class SearchComponent implements OnDestroy {
this.searchControl.valueChanges
.pipe(skip(1), debounceTime(500), takeUntilDestroyed(this.destroyRef))
.subscribe((searchText) => {
- this.#store.dispatch(new SetSearchText(searchText ?? ''));
- this.#store.dispatch(GetAllOptions);
+ this.store.dispatch(new SetSearchText(searchText ?? ''));
+ this.store.dispatch(GetAllOptions);
});
}
}
diff --git a/src/app/features/search/services/resource-filters.service.ts b/src/app/features/search/services/resource-filters.service.ts
index 5f3de92ad..90bde577b 100644
--- a/src/app/features/search/services/resource-filters.service.ts
+++ b/src/app/features/search/services/resource-filters.service.ts
@@ -24,19 +24,19 @@ import { SearchSelectors } from '../store';
providedIn: 'root',
})
export class ResourceFiltersService {
- #store = inject(Store);
- #filtersOptions = inject(FiltersOptionsService);
+ store = inject(Store);
+ filtersOptions = inject(FiltersOptionsService);
- #getFilterParams(): Record
{
- return addFiltersParams(this.#store.selectSignal(ResourceFiltersSelectors.getAllFilters)());
+ getFilterParams(): Record {
+ return addFiltersParams(this.store.selectSignal(ResourceFiltersSelectors.getAllFilters)());
}
- #getParams(): Record {
+ getParams(): Record {
const params: Record = {};
- const resourceTab = this.#store.selectSnapshot(SearchSelectors.getResourceTab);
+ const resourceTab = this.store.selectSnapshot(SearchSelectors.getResourceTab);
const resourceTypes = getResourceTypes(resourceTab);
- const searchText = this.#store.selectSnapshot(SearchSelectors.getSearchText);
- const sort = this.#store.selectSnapshot(SearchSelectors.getSortBy);
+ const searchText = this.store.selectSnapshot(SearchSelectors.getSearchText);
+ const sort = this.store.selectSnapshot(SearchSelectors.getSortBy);
params['cardSearchFilter[resourceType]'] = resourceTypes;
params['cardSearchFilter[accessService]'] = 'https://staging4.osf.io/';
@@ -47,38 +47,38 @@ export class ResourceFiltersService {
}
getCreators(valueSearchText: string): Observable {
- return this.#filtersOptions.getCreators(valueSearchText, this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getCreators(valueSearchText, this.getParams(), this.getFilterParams());
}
getDates(): Observable {
- return this.#filtersOptions.getDates(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getDates(this.getParams(), this.getFilterParams());
}
getFunders(): Observable {
- return this.#filtersOptions.getFunders(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getFunders(this.getParams(), this.getFilterParams());
}
getSubjects(): Observable {
- return this.#filtersOptions.getSubjects(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getSubjects(this.getParams(), this.getFilterParams());
}
getLicenses(): Observable {
- return this.#filtersOptions.getLicenses(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getLicenses(this.getParams(), this.getFilterParams());
}
getResourceTypes(): Observable {
- return this.#filtersOptions.getResourceTypes(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getResourceTypes(this.getParams(), this.getFilterParams());
}
getInstitutions(): Observable {
- return this.#filtersOptions.getInstitutions(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getInstitutions(this.getParams(), this.getFilterParams());
}
getProviders(): Observable {
- return this.#filtersOptions.getProviders(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getProviders(this.getParams(), this.getFilterParams());
}
getPartOtCollections(): Observable {
- return this.#filtersOptions.getPartOtCollections(this.#getParams(), this.#getFilterParams());
+ return this.filtersOptions.getPartOtCollections(this.getParams(), this.getFilterParams());
}
}
diff --git a/src/app/features/settings/account-settings/account-settings.component.html b/src/app/features/settings/account-settings/account-settings.component.html
index f1fba9c86..b7832fd0d 100644
--- a/src/app/features/settings/account-settings/account-settings.component.html
+++ b/src/app/features/settings/account-settings/account-settings.component.html
@@ -1,7 +1,7 @@
@if (currentUser()?.id) {
-
+
diff --git a/src/app/features/settings/account-settings/account-settings.component.scss b/src/app/features/settings/account-settings/account-settings.component.scss
index 9eb88126f..da0c027b5 100644
--- a/src/app/features/settings/account-settings/account-settings.component.scss
+++ b/src/app/features/settings/account-settings/account-settings.component.scss
@@ -1,118 +1,5 @@
-@use "assets/styles/mixins" as mix;
-@use "assets/styles/variables" as var;
-
:host {
display: flex;
flex-direction: column;
flex: 1;
- color: var(--dark-blue-1);
-}
-
-.header {
- display: flex;
- flex: 1;
- padding: 7.14rem 1.71rem 3.43rem 1.71rem;
- background: var(--gradient-1);
-
- h1 {
- margin-left: 0.85rem;
- }
-
- p-button {
- margin-left: auto;
- }
-
- i {
- color: var(--dark-blue-1);
- font-size: 2.6rem;
- }
-}
-
-.content {
- display: flex;
- flex-direction: column;
- gap: 1.7rem;
- padding: 1.7rem;
- background: var.$white;
-}
-
-.account-setting {
- border: 1px solid var.$grey-2;
- padding: 1.7rem;
- display: flex;
- flex-direction: column;
- gap: 1.7rem;
- border-radius: 0.5rem;
- font-weight: 400;
- text-transform: none;
-
- &-link {
- font-weight: 600;
- }
-
- &-radio-group {
- display: flex;
- flex: 1;
- gap: 4rem;
- }
-
- &-radio-item {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- }
-
- &-content {
- display: flex;
- }
-
- &-description {
- line-height: 2rem;
- text-transform: none;
- font-weight: 400;
- }
-
- &-action {
- display: flex;
- justify-content: flex-end;
- }
-
- &-emails {
- display: flex;
- flex-direction: column;
- gap: 1.7rem;
- }
-
- &-select {
- width: 50%;
- }
-
- &-email {
- display: flex;
- gap: 2rem;
- align-items: start;
-
- &__title {
- min-width: 10rem;
- }
-
- &--readonly {
- display: flex;
- align-items: center;
- border: 1px solid var.$grey-2;
- padding: 0.285rem 0.85rem;
- border-radius: 0.285rem;
-
- i {
- color: var.$dark-blue-1;
- font-size: 0.7rem;
- margin-left: 0.7rem;
- }
- }
-
- &__value {
- display: flex;
- gap: 0.428rem;
- }
- }
}
diff --git a/src/app/features/settings/account-settings/account-settings.component.ts b/src/app/features/settings/account-settings/account-settings.component.ts
index 3e1bb48e9..dde2087b6 100644
--- a/src/app/features/settings/account-settings/account-settings.component.ts
+++ b/src/app/features/settings/account-settings/account-settings.component.ts
@@ -1,16 +1,14 @@
-import { Store } from '@ngxs/store';
+import { createDispatchMap, select } from '@ngxs/store';
import { TranslatePipe } from '@ngx-translate/core';
import { DialogService } from 'primeng/dynamicdialog';
-import { ChangeDetectionStrategy, Component, effect, inject } from '@angular/core';
-import { toSignal } from '@angular/core/rxjs-interop';
+import { ChangeDetectionStrategy, Component, effect } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { UserSelectors } from '@osf/core/store/user';
import { SubHeaderComponent } from '@osf/shared/components';
-import { IS_XSMALL } from '@osf/shared/utils';
import {
AffiliatedInstitutionsComponent,
@@ -45,18 +43,23 @@ import { GetAccountSettings, GetEmails, GetExternalIdentities, GetRegions, GetUs
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccountSettingsComponent {
- readonly #store = inject(Store);
- protected readonly isMobile = toSignal(inject(IS_XSMALL));
- protected readonly currentUser = this.#store.selectSignal(UserSelectors.getCurrentUser);
+ readonly actions = createDispatchMap({
+ getAccountSettings: GetAccountSettings,
+ getEmails: GetEmails,
+ getExternalIdentities: GetExternalIdentities,
+ getRegions: GetRegions,
+ getUserInstitutions: GetUserInstitutions,
+ });
+ protected readonly currentUser = select(UserSelectors.getCurrentUser);
constructor() {
effect(() => {
if (this.currentUser()) {
- this.#store.dispatch(GetAccountSettings);
- this.#store.dispatch(GetEmails);
- this.#store.dispatch(GetExternalIdentities);
- this.#store.dispatch(GetRegions);
- this.#store.dispatch(GetUserInstitutions);
+ this.actions.getAccountSettings();
+ this.actions.getEmails();
+ this.actions.getExternalIdentities();
+ this.actions.getRegions();
+ this.actions.getUserInstitutions();
}
});
}
diff --git a/src/app/features/settings/account-settings/components/add-email/add-email.component.html b/src/app/features/settings/account-settings/components/add-email/add-email.component.html
index 519bd27e4..c41989c1e 100644
--- a/src/app/features/settings/account-settings/components/add-email/add-email.component.html
+++ b/src/app/features/settings/account-settings/components/add-email/add-email.component.html
@@ -1,14 +1,10 @@
-
-
-
-
+
diff --git a/src/app/features/settings/account-settings/components/add-email/add-email.component.ts b/src/app/features/settings/account-settings/components/add-email/add-email.component.ts
index cf1737b04..42af6805b 100644
--- a/src/app/features/settings/account-settings/components/add-email/add-email.component.ts
+++ b/src/app/features/settings/account-settings/components/add-email/add-email.component.ts
@@ -1,33 +1,54 @@
-import { Store } from '@ngxs/store';
+import { createDispatchMap, select } from '@ngxs/store';
import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
import { DynamicDialogRef } from 'primeng/dynamicdialog';
-import { InputText } from 'primeng/inputtext';
-import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { ChangeDetectionStrategy, Component, effect, inject } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
-import { AddEmail } from '../../store';
+import { TextInputComponent } from '@osf/shared/components';
+import { InputLimits } from '@osf/shared/constants';
+import { ToastService } from '@osf/shared/services';
+import { CustomValidators } from '@osf/shared/utils';
+
+import { AccountSettingsSelectors, AddEmail } from '../../store';
@Component({
- selector: 'osf-add-email',
- imports: [InputText, ReactiveFormsModule, Button, TranslatePipe],
+ selector: 'osf-confirmation-sent-dialog',
+ imports: [TextInputComponent, ReactiveFormsModule, Button, TranslatePipe],
templateUrl: './add-email.component.html',
styleUrl: './add-email.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddEmailComponent {
- readonly #store = inject(Store);
readonly dialogRef = inject(DynamicDialogRef);
- protected readonly emailControl = new FormControl('', [Validators.email, Validators.required]);
+ private readonly action = createDispatchMap({ addEmail: AddEmail });
+ private readonly toastService = inject(ToastService);
+
+ isSubmitting = select(AccountSettingsSelectors.isEmailsSubmitting);
+
+ protected readonly emailControl = new FormControl('', {
+ nonNullable: true,
+ validators: [Validators.email, CustomValidators.requiredTrimmed()],
+ });
+
+ emailMaxLength = InputLimits.email.maxLength;
+
+ constructor() {
+ effect(() => (this.isSubmitting() ? this.emailControl.disable() : this.emailControl.enable()));
+ }
addEmail() {
- if (this.emailControl.value) {
- this.#store.dispatch(new AddEmail(this.emailControl.value));
+ if (this.emailControl.invalid) {
+ return;
}
- this.dialogRef.close();
+
+ this.action.addEmail(this.emailControl.value).subscribe(() => {
+ this.dialogRef.close(this.emailControl.value);
+ this.toastService.showSuccess('settings.accountSettings.connectedEmails.successAdd');
+ });
}
}
diff --git a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.html b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.html
index 69d6d3224..9ec09b40b 100644
--- a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.html
+++ b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.html
@@ -1,27 +1,19 @@
-
-
+
+ {{ 'settings.accountSettings.affiliatedInstitutions.title' | translate }}
-
+
{{ 'settings.accountSettings.affiliatedInstitutions.description' | translate }}
-
+
@if (institutions().length === 0) {
-
+
{{ 'settings.accountSettings.affiliatedInstitutions.noInstitutions' | translate }}
-
+
}
+
@for (institution of institutions(); track institution.id) {
-
-
-
{{ institution.name }}
-
-
-
+
+
}
-
+
diff --git a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.scss b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.scss
index 21819b894..e69de29bb 100644
--- a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.scss
+++ b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.scss
@@ -1,57 +0,0 @@
-@use "../../account-settings.component.scss" as account-settings;
-@use "assets/styles/variables" as var;
-
-:host {
- @extend .account-setting;
-
- h3,
- p {
- font-weight: 400;
- text-transform: none;
- }
-
- .account-setting {
- &-emails {
- display: flex;
- flex-direction: column;
- gap: 1.7rem;
- }
-
- &-email {
- display: flex;
- gap: 2rem;
- align-items: start;
-
- &--readonly {
- display: flex;
- align-items: center;
- border: 1px solid var.$grey-2;
- padding: 0.285rem 0.85rem;
- border-radius: 0.285rem;
- min-height: 2.8rem;
-
- &--address {
- max-width: 14rem;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- i {
- color: var.$dark-blue-1;
- font-size: 0.7rem;
- margin-left: 0.7rem;
- cursor: pointer;
- font-weight: 400;
- }
- }
-
- &__value {
- display: flex;
- align-items: center;
- gap: 0.428rem;
- min-height: 2.8rem;
- }
- }
- }
-}
diff --git a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts
index bba4653d7..fbd92366a 100644
--- a/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts
+++ b/src/app/features/settings/account-settings/components/affiliated-institutions/affiliated-institutions.component.ts
@@ -1,28 +1,51 @@
-import { select, Store } from '@ngxs/store';
+import { createDispatchMap, select } from '@ngxs/store';
import { TranslatePipe } from '@ngx-translate/core';
+import { Card } from 'primeng/card';
+
+import { finalize } from 'rxjs';
+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { UserSelectors } from '@osf/core/store/user';
+import { ReadonlyInputComponent } from '@osf/shared/components';
+import { Institution } from '@osf/shared/models';
+import { CustomConfirmationService, LoaderService, ToastService } from '@osf/shared/services';
import { AccountSettingsSelectors, DeleteUserInstitution } from '../../store';
@Component({
selector: 'osf-affiliated-institutions',
- imports: [TranslatePipe],
+ imports: [Card, TranslatePipe, ReadonlyInputComponent],
templateUrl: './affiliated-institutions.component.html',
styleUrl: './affiliated-institutions.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AffiliatedInstitutionsComponent {
- private readonly store = inject(Store);
+ private readonly customConfirmationService = inject(CustomConfirmationService);
+ private readonly toastService = inject(ToastService);
+ private readonly loaderService = inject(LoaderService);
+
+ private readonly actions = createDispatchMap({ deleteUserInstitution: DeleteUserInstitution });
protected institutions = select(AccountSettingsSelectors.getUserInstitutions);
protected currentUser = select(UserSelectors.getCurrentUser);
- deleteInstitution(id: string) {
- if (this.currentUser()?.id) {
- this.store.dispatch(new DeleteUserInstitution(id, this.currentUser()!.id));
- }
+ deleteInstitution(institution: Institution) {
+ this.customConfirmationService.confirmDelete({
+ headerKey: 'settings.accountSettings.affiliatedInstitutions.deleteDialog.header',
+ messageParams: { name: institution.name },
+ messageKey: 'settings.accountSettings.affiliatedInstitutions.deleteDialog.message',
+ onConfirm: () => {
+ if (this.currentUser()?.id) {
+ this.actions
+ .deleteUserInstitution(institution.id, this.currentUser()!.id)
+ .pipe(finalize(() => this.loaderService.hide()))
+ .subscribe(() =>
+ this.toastService.showSuccess('settings.accountSettings.affiliatedInstitutions.successDelete')
+ );
+ }
+ },
+ });
}
}
diff --git a/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.html b/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.html
index 0a9837686..d93192a0e 100644
--- a/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.html
+++ b/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.html
@@ -1,29 +1,22 @@
-
-
-
- {{ 'settings.accountSettings.deactivateAccount.cancelDeactivation.confirm' | translate }}
-
-
-
- {{ 'settings.accountSettings.deactivateAccount.cancelDeactivation.description' | translate }}
-
-
+
+
+ {{ 'settings.accountSettings.deactivateAccount.dialog.undo.message' | translate }}
+
+
-
diff --git a/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.scss b/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.scss
index 3f1e57889..e69de29bb 100644
--- a/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.scss
+++ b/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.scss
@@ -1,5 +0,0 @@
-@use "../../account-settings.component.scss" as account-settings;
-
-:host {
- @extend .account-setting;
-}
diff --git a/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.ts b/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.ts
index bbe68c7ee..5791aa60e 100644
--- a/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.ts
+++ b/src/app/features/settings/account-settings/components/cancel-deactivation/cancel-deactivation.component.ts
@@ -1,5 +1,3 @@
-import { createDispatchMap } from '@ngxs/store';
-
import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';
@@ -7,8 +5,6 @@ import { DynamicDialogRef } from 'primeng/dynamicdialog';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
-import { CancelDeactivationRequest } from '../../store';
-
@Component({
selector: 'osf-cancel-deactivation',
imports: [Button, TranslatePipe],
@@ -17,11 +13,9 @@ import { CancelDeactivationRequest } from '../../store';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CancelDeactivationComponent {
- action = createDispatchMap({ cancelDeactivationRequest: CancelDeactivationRequest });
- dialogRef = inject(DynamicDialogRef);
+ readonly dialogRef = inject(DynamicDialogRef);
cancelDeactivation(): void {
- this.action.cancelDeactivationRequest();
- this.dialogRef.close();
+ this.dialogRef.close(true);
}
}
diff --git a/src/app/features/settings/account-settings/components/change-password/change-password.component.html b/src/app/features/settings/account-settings/components/change-password/change-password.component.html
index 8991fe606..13e1747d8 100644
--- a/src/app/features/settings/account-settings/components/change-password/change-password.component.html
+++ b/src/app/features/settings/account-settings/components/change-password/change-password.component.html
@@ -1,11 +1,12 @@
-
-
+
+ {{ 'settings.accountSettings.changePassword.title' | translate }}
-