Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
eee8c05
fix(filter-options): updated filter option models
nsemets Apr 29, 2026
bbf24ff
fix(search-help-tutorial): simplified logic for step position
nsemets Apr 29, 2026
55b99d7
Merge remote-tracking branch 'upstream/feature/pbs-26-6' into fix/sea…
nsemets Apr 29, 2026
3f1126f
Merge remote-tracking branch 'origin/feature/pbs-26-9' into fix/searc…
nsemets May 12, 2026
5ec7b73
Merge remote-tracking branch 'upstream/feature/pbs-26-9' into fix/sea…
nsemets May 27, 2026
6dc182d
fix(tutorial-step): fixed interfaces
nsemets Jun 26, 2026
bbe5d88
Merge pull request #986 from nsemets/fix/search-improvements
omar-cos Jun 29, 2026
c466daf
fix(registration): fixed registration link for file with view only li…
nsemets Jun 29, 2026
d03e6d9
Merge branch 'develop' into feature/pbs-26-12
brianjgeiger Jul 2, 2026
91a69d9
feat(ENG-9833): cleanup and delete dead code
Vlad0n20 May 6, 2026
3573882
Merge pull request #981 from Vlad0n20/feat/ENG-9833
adlius Jul 7, 2026
6bd7c65
fix(activity-logs): added foreign user value and updated unit tests (…
nsemets Jul 10, 2026
7383794
[ENG-11532] AVOL Contributors Exposed on Files on Embargoed Registrat…
nsemets Jul 15, 2026
a4c700a
Merge pull request #1031 from CenterForOpenScience/feature/pbs-26-12
adlius Jul 16, 2026
39ea1ef
chore(release): Bump version no. Add CHANGELOG
adlius Jul 16, 2026
3a3d9bb
Merge branch 'release/26.13.0'
adlius Jul 16, 2026
f45357b
Merge tag '26.13.0' into develop
adlius Jul 16, 2026
e455e31
[ENG-11260] My Projects Page Improvements (#1033)
nsemets Jul 20, 2026
2f5c579
Update changelog and bump version
cslzchen Jul 20, 2026
487a9e8
Merge branch 'release/26.14.0'
cslzchen Jul 20, 2026
97dd7cc
Merge tag '26.14.0' into develop
cslzchen Jul 20, 2026
cd8f7af
Merge remote-tracking branch 'upstream/develop' into feature/download…
sh-andriy Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO.

26.14.0 (2026-07-20)
====================

* My Projects Page Improvements & DAZ Improvements

26.13.0 (2026-06-23)
====================

* PB&S and ES2 code cleanup

26.12.0 (2026-06-23)
====================

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "osf",
"version": "26.12.0",
"version": "26.14.0",
"scripts": {
"ng": "ng",
"analyze-bundle": "ng build --configuration=analyze-bundle && source-map-explorer dist/**/*.js --no-border-checks",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { ChangeDetectionStrategy, Component, model } from '@angular/core';

import { FilterChipsComponent } from '@osf/shared/components/filter-chips/filter-chips.component';
import { SearchFiltersComponent } from '@osf/shared/components/search-filters/search-filters.component';
import { DiscoverableFilter, FilterOption } from '@osf/shared/models/search/discoverable-filter.model';
import { DiscoverableFilter } from '@osf/shared/models/search/discoverable-filter.model';
import { FilterOptionRemoved } from '@osf/shared/models/search/filter-option-removed';
import { FilterOptionSelected } from '@osf/shared/models/search/filter-option-selected.model';
import { FilterOptionsSearchText } from '@osf/shared/models/search/filter-options-search-text.model';
import {
ClearFilterSearchResults,
FetchResources,
Expand Down Expand Up @@ -44,7 +47,7 @@ export class FiltersSectionComponent {

filtersVisible = model<boolean>();

onSelectedFilterOptionsChanged(event: { filter: DiscoverableFilter; filterOption: FilterOption[] }): void {
onSelectedFilterOptionsChanged(event: FilterOptionSelected): void {
this.actions.updateSelectedFilterOption(event.filter.key, event.filterOption);
this.actions.fetchResources();
}
Expand All @@ -57,15 +60,15 @@ export class FiltersSectionComponent {
this.actions.loadMoreFilterOptions(filter.key);
}

onSearchFilterOptions(event: { searchText: string; filter: DiscoverableFilter }): void {
onSearchFilterOptions(event: FilterOptionsSearchText): void {
if (event.searchText.trim()) {
this.actions.loadFilterOptionsWithSearch(event.filter.key, event.searchText);
} else {
this.actions.clearFilterSearchResults(event.filter.key);
}
}

onFilterChipRemoved(event: { filterKey: string; optionRemoved: FilterOption }): void {
onFilterChipRemoved(event: FilterOptionRemoved): void {
const updatedOptions = this.selectedFilterOptions()[event.filterKey].filter(
(option) => option.value !== event.optionRemoved.value
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,8 @@ <h1 class="collections-heading flex align-items-center">{{ collectionProvider()?
[stepperActiveValue]="stepperActiveValue()"
[targetStepValue]="AddToCollectionSteps.CollectionMetadata"
[isDisabled]="isCollectionMetadataDisabled()"
[primaryCollectionId]="primaryCollectionId()"
[isCedarMode]="isCedarMode()"
[cedarTemplate]="requiredMetadataTemplate()"
[existingCedarRecord]="existingCedarRecord()"
(metadataSaved)="handleCollectionMetadataSaved($event)"
(cedarDataSaved)="handleCedarDataSaved($event)"
(stepChange)="handleChangeStep($event)"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { MockComponents, MockProvider } from 'ng-mocks';
import { of, Subject, throwError } from 'rxjs';

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { UserSelectors } from '@core/store/user';
Expand Down Expand Up @@ -51,7 +50,6 @@ const DEFAULT_SIGNALS: SignalOverride[] = [
{ selector: CollectionsSelectors.getRequiredMetadataTemplate, value: null },
{ selector: ProjectsSelectors.getSelectedProject, value: MOCK_PROJECT },
{ selector: UserSelectors.getCurrentUser, value: MOCK_USER },
{ selector: UserSelectors.getActiveFlags, value: [] },
{ selector: MetadataSelectors.getCedarRecords, value: [] },
{ selector: AddToCollectionSelectors.getCurrentCollectionSubmission, value: null },
];
Expand Down Expand Up @@ -175,16 +173,6 @@ describe('AddToCollectionComponent', () => {
expect(component.projectContributorsSaved()).toBe(true);
});

it('should handle collection metadata saved', () => {
const { component } = setup();
const mockForm = new FormGroup({});
component.handleCollectionMetadataSaved(mockForm);

expect(component.collectionMetadataForm).toBe(mockForm);
expect(component.collectionMetadataSaved()).toBe(true);
expect(component.stepperActiveValue()).toBe(AddToCollectionSteps.Complete);
});

it('should handle cedar data saved', () => {
const { component } = setup();
const mockCedarData: CedarRecordDataBinding = {
Expand Down Expand Up @@ -283,11 +271,10 @@ describe('AddToCollectionComponent', () => {
);
});

it('should update submission and navigate on success in edit mode', () => {
it('should navigate on success in edit mode', () => {
const { component, mockToastService, mockLoaderService, mockRouter } = setup({
routeParams: { id: 'project-1', providerId: 'provider-1' },
});
vi.spyOn(component.actions, 'updateCollectionSubmission').mockReturnValue(of(void 0));

component.handleAddToCollection();

Expand All @@ -299,11 +286,20 @@ describe('AddToCollectionComponent', () => {
expect(mockRouter.navigate).toHaveBeenCalledWith(['project-1', 'overview']);
});

it('should show error toast when update fails in edit mode', () => {
it('should show error toast when saving cedar record fails in edit mode', () => {
const { component, mockToastService } = setup({
routeParams: { id: 'project-1', providerId: 'provider-1' },
selectorOverrides: [
{ selector: CollectionsSelectors.getRequiredMetadataTemplate, value: { id: 'template-123' } },
],
});
vi.spyOn(component.actions, 'updateCollectionSubmission').mockReturnValue(throwError(() => new Error('fail')));
const mockCedarData: CedarRecordDataBinding = {
data: {} as CedarRecordDataBinding['data'],
id: 'template-123',
isPublished: false,
};
component.handleCedarDataSaved(mockCedarData);
vi.spyOn(component.actions, 'createCedarRecord').mockReturnValue(throwError(() => new Error('fail')));

component.handleAddToCollection();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import {
signal,
} from '@angular/core';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { FormGroup } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';

import { ENVIRONMENT } from '@core/provider/environment.provider';
import { UserSelectors } from '@core/store/user';
import { CedarMetadataRecordData, CedarRecordDataBinding } from '@osf/features/metadata/models';
import {
Expand All @@ -43,7 +41,6 @@ import { LoaderService } from '@osf/shared/services/loader.service';
import { ToastService } from '@osf/shared/services/toast.service';
import { CollectionsSelectors, GetCollectionProvider } from '@osf/shared/stores/collections';
import { ProjectsSelectors, SetSelectedProject } from '@osf/shared/stores/projects';
import { COLLECTION_SUBMISSION_WITH_CEDAR } from '@shared/constants/feature-flags.const';

import { AddToCollectionSteps } from '../../enums';
import { RemoveCollectionSubmissionPayload } from '../../models/remove-collection-submission-payload.model';
Expand All @@ -53,7 +50,6 @@ import {
ClearAddToCollectionState,
GetCurrentCollectionSubmission,
RemoveCollectionSubmission,
UpdateCollectionSubmission,
} from '../../store/add-to-collection';

import { AddToCollectionConfirmationDialogComponent } from './add-to-collection-confirmation-dialog/add-to-collection-confirmation-dialog.component';
Expand Down Expand Up @@ -91,22 +87,18 @@ export class AddToCollectionComponent implements CanDeactivateComponent {
private readonly headerStyleHelper = inject(HeaderStyleService);
private readonly platformId = inject(PLATFORM_ID);
private readonly isBrowser = isPlatformBrowser(this.platformId);
private readonly environment = inject(ENVIRONMENT);

readonly selectedProjectId = toSignal<string | null>(
this.route.params.pipe(map((params) => params['id'])) ?? of(null)
);

readonly AddToCollectionSteps = AddToCollectionSteps;

collectionMetadataForm = new FormGroup({});

isProviderLoading = select(CollectionsSelectors.getCollectionProviderLoading);
collectionProvider = select(CollectionsSelectors.getCollectionProvider);
requiredMetadataTemplate = select(CollectionsSelectors.getRequiredMetadataTemplate);
selectedProject = select(ProjectsSelectors.getSelectedProject);
currentUser = select(UserSelectors.getCurrentUser);
activeFlags = select(UserSelectors.getActiveFlags);
currentCollectionSubmission = select(AddToCollectionSelectors.getCurrentCollectionSubmission);
cedarRecords = select(MetadataSelectors.getCedarRecords);

Expand All @@ -125,9 +117,6 @@ export class AddToCollectionComponent implements CanDeactivateComponent {
isCollectionMetadataDisabled = computed(
() => !this.selectedProject() || !this.projectMetadataSaved() || !this.projectContributorsSaved()
);
isCedarMode = computed(
() => this.activeFlags().includes(COLLECTION_SUBMISSION_WITH_CEDAR) && !!this.requiredMetadataTemplate()
);
existingCedarRecord = computed<CedarMetadataRecordData | null>(() => {
const records = this.cedarRecords();
const templateId = this.requiredMetadataTemplate()?.id;
Expand All @@ -138,7 +127,6 @@ export class AddToCollectionComponent implements CanDeactivateComponent {
readonly actions = createDispatchMap({
getCollectionProvider: GetCollectionProvider,
clearAddToCollectionState: ClearAddToCollectionState,
updateCollectionSubmission: UpdateCollectionSubmission,
deleteCollectionSubmission: RemoveCollectionSubmission,
setSelectedProject: SetSelectedProject,
getCurrentCollectionSubmission: GetCurrentCollectionSubmission,
Expand Down Expand Up @@ -195,12 +183,6 @@ export class AddToCollectionComponent implements CanDeactivateComponent {
this.projectContributorsSaved.set(true);
}

handleCollectionMetadataSaved(form: FormGroup): void {
this.collectionMetadataForm = form;
this.collectionMetadataSaved.set(true);
this.stepperActiveValue.set(AddToCollectionSteps.Complete);
}

handleCedarDataSaved(data: CedarRecordDataBinding): void {
this.pendingCedarData.set(data);
this.collectionMetadataSaved.set(true);
Expand All @@ -212,17 +194,13 @@ export class AddToCollectionComponent implements CanDeactivateComponent {
collectionId: this.primaryCollectionId() || '',
projectId: this.selectedProject()?.id || '',
userId: this.currentUser()?.id || '',
collectionMetadata: this.isCedarMode() ? {} : this.collectionMetadataForm.value || {},
};

const isEditMode = this.isEditMode();

if (isEditMode) {
if (this.isEditMode()) {
this.loaderService.show();

this.saveCedarRecordIfNeeded()
.pipe(
switchMap(() => this.actions.updateCollectionSubmission(payload)),
finalize(() => this.loaderService.hide()),
takeUntilDestroyed(this.destroyRef)
)
Expand Down Expand Up @@ -298,8 +276,6 @@ export class AddToCollectionComponent implements CanDeactivateComponent {
}

private saveCedarRecordIfNeeded(): Observable<unknown> {
if (!this.isCedarMode()) return of(null);

const cedarData = this.pendingCedarData();
const projectId = this.selectedProject()?.id;
const templateId = this.requiredMetadataTemplate()?.id;
Expand Down Expand Up @@ -352,8 +328,7 @@ export class AddToCollectionComponent implements CanDeactivateComponent {

effect(() => {
const projectId = this.selectedProjectId();
const isCedar = this.isCedarMode();
if (isCedar && projectId) {
if (projectId) {
this.actions.getCedarRecords(projectId, ResourceType.Project);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,15 @@ <h3>{{ 'collections.addToCollection.collectionMetadata' | translate }}</h3>

@if (!isDisabled() && !isStepActive()) {
@if (collectionMetadataSaved()) {
@if (isCedarMode()) {
@let cedarTpl = cedarTemplate();
@let cedarTpl = cedarTemplate();

@if (cedarTpl) {
<cedar-artifact-viewer
#cedarViewer
[config]="cedarViewerConfig"
[templateObject]="cedarTpl.attributes.template"
[instanceObject]="cedarFormData()"
></cedar-artifact-viewer>
}
} @else {
@for (filterEntry of availableFilterEntries(); track filterEntry.key) {
<div>
<p class="font-bold">{{ filterEntry.labelKey | translate }}</p>

<p class="mt-2">
{{ collectionMetadataForm().get(filterEntry.key)?.value }}
</p>
</div>
}
@if (cedarTpl) {
<cedar-artifact-viewer
#cedarViewer
[config]="cedarViewerConfig"
[templateObject]="cedarTpl.attributes.template"
[instanceObject]="cedarFormData()"
></cedar-artifact-viewer>
}
}

Expand All @@ -48,62 +36,32 @@ <h3>{{ 'collections.addToCollection.collectionMetadata' | translate }}</h3>

<p-step-panel [value]="targetStepValue()" class="p-0">
<ng-template #content>
@if (isCedarMode()) {
@let cedarTpl = cedarTemplate();

@if (cedarTpl) {
@if (isStepActive()) {
<div class="cedar-editor-container mt-3">
<cedar-embeddable-editor
#cedarEditor
[config]="cedarConfig"
[templateObject]="cedarTpl.attributes.template"
[metadata]="cedarFormData()"
(change)="onCedarChange($event)"
(keyup)="onCedarChange($event)"
></cedar-embeddable-editor>
</div>
}
@let cedarTpl = cedarTemplate();

<div class="flex justify-content-end gap-3 mt-4">
<p-button
severity="info"
[label]="'common.buttons.discardChanges' | translate"
(onClick)="handleDiscardChanges()"
/>
<p-button [label]="'common.buttons.saveAndContinue' | translate" (onClick)="handleSaveCedarMetadata()" />
@if (cedarTpl) {
@if (isStepActive()) {
<div class="cedar-editor-container mt-3">
<cedar-embeddable-editor
#cedarEditor
[config]="cedarConfig"
[templateObject]="cedarTpl.attributes.template"
[metadata]="cedarFormData()"
(change)="onCedarChange($event)"
(keyup)="onCedarChange($event)"
></cedar-embeddable-editor>
</div>
} @else {
<p class="mt-3">{{ 'collections.addToCollection.cedarFormNotAvailable' | translate }}</p>
}
} @else {
<form [formGroup]="collectionMetadataForm()" class="grid row-gap-2 mt-3">
@for (filterEntry of availableFilterEntries(); track filterEntry.key) {
<div class="flex flex-column gap-1 col-12 md:col-6">
<label [for]="filterEntry.key">{{ filterEntry.labelKey | translate }}</label>
<p-select
[id]="filterEntry.key"
[options]="filterEntry.options"
[formControlName]="filterEntry.key"
[placeholder]="filterEntry.labelKey | translate"
appendTo="body"
></p-select>
</div>
}
</form>

<div class="flex justify-content-end gap-3 mt-4">
<p-button
severity="info"
[label]="'common.buttons.discardChanges' | translate"
(onClick)="handleDiscardChanges()"
/>
<p-button
[label]="'common.buttons.saveAndContinue' | translate"
[disabled]="!collectionMetadataForm().valid"
(onClick)="handleSaveMetadata()"
/>
<p-button [label]="'common.buttons.saveAndContinue' | translate" (onClick)="handleSaveCedarMetadata()" />
</div>
} @else {
<p class="mt-3">{{ 'collections.addToCollection.cedarFormNotAvailable' | translate }}</p>
}
</ng-template>
</p-step-panel>
Expand Down
Loading
Loading