diff --git a/src/app/core/interceptors/auth.interceptor.ts b/src/app/core/interceptors/auth.interceptor.ts
index 174408c69..92dbd5630 100644
--- a/src/app/core/interceptors/auth.interceptor.ts
+++ b/src/app/core/interceptors/auth.interceptor.ts
@@ -9,11 +9,12 @@ export const authInterceptor: HttpInterceptorFn = (
const authToken = 'UlO9O9GNKgVzJD7pUeY53jiQTKJ4U2znXVWNvh0KZQruoENuILx0IIYf9LoDz7Duq72EIm';
// UlO9O9GNKgVzJD7pUeY53jiQTKJ4U2znXVWNvh0KZQruoENuILx0IIYf9LoDz7Duq72EIm kyrylo
// 2rjFZwmdDG4rtKj7hGkEMO6XyHBM2lN7XBbsA1e8OqcFhOWu6Z7fQZiheu9RXtzSeVrgOt roman nastyuk
-
- if (authToken) {
+ const localStorageToken = localStorage.getItem('authToken');
+ const token = localStorageToken || authToken;
+ if (token) {
const authReq = req.clone({
setHeaders: {
- Authorization: `Bearer ${authToken}`,
+ Authorization: `Bearer ${token}`,
Accept: req.responseType === 'text' ? '*/*' : 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json',
},
diff --git a/src/app/features/project/registrations/registrations.component.ts b/src/app/features/project/registrations/registrations.component.ts
index 208142a8d..3cb69a5da 100644
--- a/src/app/features/project/registrations/registrations.component.ts
+++ b/src/app/features/project/registrations/registrations.component.ts
@@ -9,7 +9,7 @@ import { map, of } from 'rxjs';
import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
import { LoadingSpinnerComponent, SubHeaderComponent } from '@osf/shared/components';
import { RegistrationCardComponent } from '@osf/shared/components/registration-card/registration-card.component';
@@ -26,20 +26,20 @@ import { GetRegistrations, RegistrationsSelectors } from './store';
})
export class RegistrationsComponent implements OnInit {
private readonly route = inject(ActivatedRoute);
-
+ private readonly router = inject(Router);
readonly projectId = toSignal(this.route.parent?.params.pipe(map((params) => params['id'])) ?? of(undefined));
-
protected registrations = select(RegistrationsSelectors.getRegistrations);
protected isRegistrationsLoading = select(RegistrationsSelectors.isRegistrationsLoading);
-
protected actions = createDispatchMap({ getRegistrations: GetRegistrations });
+ private readonly OSF_PROVIDER_ID = 'osf';
ngOnInit(): void {
this.actions.getRegistrations(this.projectId());
}
addRegistration(): void {
- //TODO: Implement the logic to add a new registration.
- console.log('Add Registration clicked');
+ this.router.navigate([`registries/${this.OSF_PROVIDER_ID}/new`], {
+ queryParams: { projectId: this.projectId() },
+ });
}
}
diff --git a/src/app/features/registries/components/new-registration/new-registration.component.html b/src/app/features/registries/components/new-registration/new-registration.component.html
index 4fd357a08..b33354feb 100644
--- a/src/app/features/registries/components/new-registration/new-registration.component.html
+++ b/src/app/features/registries/components/new-registration/new-registration.component.html
@@ -41,6 +41,7 @@
{{ ('registries.new.steps.title' | translate) + '2' }}
[placeholder]="'registries.new.selectProject' | translate"
optionLabel="title"
optionValue="id"
+ [loading]="isProjectsLoading()"
(onChange)="onSelectProject($event.value)"
class="w-6"
/>
diff --git a/src/app/features/registries/components/new-registration/new-registration.component.ts b/src/app/features/registries/components/new-registration/new-registration.component.ts
index 49599a698..85c031005 100644
--- a/src/app/features/registries/components/new-registration/new-registration.component.ts
+++ b/src/app/features/registries/components/new-registration/new-registration.component.ts
@@ -32,6 +32,7 @@ export class NewRegistrationComponent {
protected readonly isDraftSubmitting = select(RegistriesSelectors.isDraftSubmitting);
protected readonly draftRegistration = select(RegistriesSelectors.getDraftRegistration);
protected readonly isProvidersLoading = select(RegistriesSelectors.isProvidersLoading);
+ protected readonly isProjectsLoading = select(RegistriesSelectors.isProjectsLoading);
protected actions = createDispatchMap({
getProjects: GetProjects,
getProviderSchemas: GetProviderSchemas,
@@ -39,12 +40,13 @@ export class NewRegistrationComponent {
});
protected readonly providerId = this.route.snapshot.params['providerId'];
+ protected readonly projectId = this.route.snapshot.queryParams['projectId'];
- fromProject = false;
+ fromProject = this.projectId !== undefined;
draftForm = this.fb.group({
providerSchema: ['', Validators.required],
- project: [''],
+ project: [this.projectId || ''],
});
constructor() {
diff --git a/src/app/features/registries/components/review/review.component.html b/src/app/features/registries/components/review/review.component.html
index f54c10636..e2bda9dca 100644
--- a/src/app/features/registries/components/review/review.component.html
+++ b/src/app/features/registries/components/review/review.component.html
@@ -37,10 +37,21 @@ {{ 'navigation.registration.contributors' | translate }}
}
-
+
+
{{ 'shared.license.title' | translate }}
-
{{ draftRegistration()?.license?.id }}
- @if (!draftRegistration()?.license) {
+ @if (draftRegistration()?.license && license()) {
+
+
+
+ {{ license()?.name }}
+
+
+ {{ license()!.text | interpolate: licenseOptionsRecord() }}
+
+
+
+ } @else {
{{ 'common.labels.noData' | translate }}
{{ INPUT_VALIDATION_MESSAGES.required | translate }}
diff --git a/src/app/features/registries/components/review/review.component.ts b/src/app/features/registries/components/review/review.component.ts
index 4c542628f..0b24465d8 100644
--- a/src/app/features/registries/components/review/review.component.ts
+++ b/src/app/features/registries/components/review/review.component.ts
@@ -2,6 +2,7 @@ import { createDispatchMap, select } from '@ngxs/store';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
+import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion';
import { Button } from 'primeng/button';
import { Card } from 'primeng/card';
import { DialogService } from 'primeng/dynamicdialog';
@@ -16,6 +17,7 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants';
import { ResourceType } from '@osf/shared/enums';
+import { InterpolatePipe } from '@osf/shared/pipes';
import { CustomConfirmationService, ToastService } from '@osf/shared/services';
import {
ContributorsSelectors,
@@ -25,13 +27,25 @@ import {
} from '@osf/shared/stores';
import { FieldType } from '../../enums';
-import { DeleteDraft, FetchProjectChildren, RegistriesSelectors } from '../../store';
+import { DeleteDraft, FetchLicenses, FetchProjectChildren, RegistriesSelectors } from '../../store';
import { ConfirmRegistrationDialogComponent } from '../confirm-registration-dialog/confirm-registration-dialog.component';
import { SelectComponentsDialogComponent } from '../select-components-dialog/select-components-dialog.component';
@Component({
selector: 'osf-review',
- imports: [TranslatePipe, Card, Message, RouterLink, Tag, Button],
+ imports: [
+ TranslatePipe,
+ Card,
+ Message,
+ RouterLink,
+ Tag,
+ Button,
+ Accordion,
+ AccordionContent,
+ AccordionHeader,
+ AccordionPanel,
+ InterpolatePipe,
+ ],
templateUrl: './review.component.html',
styleUrl: './review.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -54,6 +68,9 @@ export class ReviewComponent {
protected readonly contributors = select(ContributorsSelectors.getContributors);
protected readonly subjects = select(SubjectsSelectors.getSelectedSubjects);
protected readonly components = select(RegistriesSelectors.getRegistrationComponents);
+ protected readonly license = select(RegistriesSelectors.getRegistrationLicense);
+ private readonly OSF_PROVIDER_ID = 'osf';
+
protected readonly FieldType = FieldType;
protected actions = createDispatchMap({
@@ -61,6 +78,7 @@ export class ReviewComponent {
getSubjects: FetchSelectedSubjects,
deleteDraft: DeleteDraft,
getProjectsComponents: FetchProjectChildren,
+ fetchLicenses: FetchLicenses,
});
private readonly draftId = toSignal(this.route.params.pipe(map((params) => params['id'])) ?? of(undefined));
@@ -71,6 +89,10 @@ export class ReviewComponent {
return Object.values(this.stepsValidation()).some((step) => step.invalid);
});
+ licenseOptionsRecord = computed(() => {
+ return (this.draftRegistration()?.license.options ?? {}) as Record;
+ });
+
constructor() {
if (!this.contributors()?.length) {
this.actions.getContributors(this.draftId(), ResourceType.DraftRegistration);
@@ -79,6 +101,12 @@ export class ReviewComponent {
this.actions.getSubjects(this.draftId(), ResourceType.DraftRegistration);
}
+ effect(() => {
+ if (this.draftRegistration()) {
+ this.actions.fetchLicenses(this.draftRegistration()?.providerId ?? this.OSF_PROVIDER_ID);
+ }
+ });
+
let componentsLoaded = false;
effect(() => {
if (!this.isDraftSubmitting()) {
diff --git a/src/app/features/registries/store/registries.selectors.ts b/src/app/features/registries/store/registries.selectors.ts
index 8a4acbffe..52bf4eacf 100644
--- a/src/app/features/registries/store/registries.selectors.ts
+++ b/src/app/features/registries/store/registries.selectors.ts
@@ -23,6 +23,11 @@ export class RegistriesSelectors {
return state.projects.data;
}
+ @Selector([RegistriesState])
+ static isProjectsLoading(state: RegistriesStateModel): boolean {
+ return state.projects.isLoading;
+ }
+
@Selector([RegistriesState])
static isDraftSubmitting(state: RegistriesStateModel): boolean {
return state.draftRegistration.isSubmitting ?? false;
@@ -63,6 +68,11 @@ export class RegistriesSelectors {
return state.draftRegistration.data?.license || null;
}
+ @Selector([RegistriesState])
+ static getRegistrationLicense(state: RegistriesStateModel): License | null {
+ return state.licenses.data.find((l) => l.id === state.draftRegistration.data?.license.id) || null;
+ }
+
@Selector([RegistriesState])
static getPagesSchema(state: RegistriesStateModel): PageSchema[] {
return state.pagesSchema.data;
diff --git a/src/app/shared/components/files-tree/files-tree.component.html b/src/app/shared/components/files-tree/files-tree.component.html
index f02951db3..0e1119685 100644
--- a/src/app/shared/components/files-tree/files-tree.component.html
+++ b/src/app/shared/components/files-tree/files-tree.component.html
@@ -1,94 +1,102 @@
-@if (!viewOnly()) {
-
- @if (isDragOver()) {
-
-
-
{{ 'project.files.dropText' | translate }}
-
- }
-
-}
-
-@if (isLoading()) {
-
-
-
-} @else {
-
-
+ @if (!viewOnly()) {
+
-
- @if (file.previousFolder) {
-
-
-
-
-
- {{ file.name ?? '' }}
-
-
-
- } @else {
-
-
- @if (file.kind !== 'folder') {
-
-
- {{ file?.name ?? '' }}
-
- } @else {
-
-
- {{ file?.name ?? '' }}
-
- }
-
-
-
- @if (file.extra.downloads) {
- {{ file.kind === 'file' ? file.extra.downloads + ' ' + ('common.labels.downloads' | translate) : '' }}
- }
-
+ @if (isDragOver()) {
+
+
+
{{ 'project.files.dropText' | translate }}
+
+ }
+
+ }
-
- {{ file.size | fileSize }}
+ @if (isLoading()) {
+
+
+
+ } @else {
+
+
+
+ @if (file.previousFolder) {
+
+
+
+
+
+ {{ file.name ?? '' }}
+
+
+ } @else {
+
+
+ @if (file.kind !== 'folder') {
+
+
+ {{ file?.name ?? '' }}
+
+ } @else {
+
+
+ {{ file?.name ?? '' }}
+
+ }
+
-
- {{ file.dateModified | date: 'MMM d, y hh:mm a' }}
-
+
+ @if (file.extra.downloads) {
+ {{ file.kind === 'file' ? file.extra.downloads + ' ' + ('common.labels.downloads' | translate) : '' }}
+ }
+
- @if (!viewOnly() && !viewOnlyDownloadable()) {
-
-
+
+ {{ file.size | fileSize }}
- } @else if (viewOnly() && viewOnlyDownloadable()) {
+
-
+ {{ file.dateModified | date: 'MMM d, y hh:mm a' }}
- }
-
- }
-
-
- @if (!files().length) {
-
-
{{ 'project.files.emptyState' | translate }}
-
- }
-
-}
+ @if (!viewOnly() && !viewOnlyDownloadable()) {
+
+
+
+ } @else if (viewOnly() && viewOnlyDownloadable()) {
+
+
+
+ }
+
+ }
+
+
+
+ @if (!files().length) {
+
+
{{ 'project.files.emptyState' | translate }}
+
+ }
+
+ }
+
diff --git a/src/app/shared/components/files-tree/files-tree.component.scss b/src/app/shared/components/files-tree/files-tree.component.scss
index 864001576..922feb710 100644
--- a/src/app/shared/components/files-tree/files-tree.component.scss
+++ b/src/app/shared/components/files-tree/files-tree.component.scss
@@ -1,6 +1,10 @@
@use "assets/styles/variables" as var;
@use "assets/styles/mixins" as mix;
+:host {
+ min-height: 200px;
+}
+
.files-table {
display: flex;
flex-direction: column;
@@ -68,7 +72,7 @@
}
.drop-zone {
- position: fixed;
+ position: absolute;
top: 0;
left: 0;
width: 100%;
diff --git a/src/app/shared/components/files-tree/files-tree.component.ts b/src/app/shared/components/files-tree/files-tree.component.ts
index 5ce87dfb2..4f4debb9d 100644
--- a/src/app/shared/components/files-tree/files-tree.component.ts
+++ b/src/app/shared/components/files-tree/files-tree.component.ts
@@ -9,17 +9,19 @@ import { catchError } from 'rxjs/operators';
import { DatePipe } from '@angular/common';
import {
+ AfterViewInit,
ChangeDetectionStrategy,
Component,
computed,
effect,
+ ElementRef,
HostBinding,
inject,
input,
OnDestroy,
- OnInit,
output,
signal,
+ viewChild,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
@@ -38,8 +40,9 @@ import { CustomConfirmationService, FilesService, ToastService } from '@shared/s
styleUrl: './files-tree.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class FilesTreeComponent implements OnInit, OnDestroy {
+export class FilesTreeComponent implements OnDestroy, AfterViewInit {
@HostBinding('class') classes = 'relative';
+ private dropZoneContainerRef = viewChild('dropZoneContainer');
readonly filesService = inject(FilesService);
readonly router = inject(Router);
readonly toastService = inject(ToastService);
@@ -78,15 +81,15 @@ export class FilesTreeComponent implements OnInit, OnDestroy {
}
});
- ngOnInit(): void {
- window.addEventListener('dragenter', this.onGlobalDragEnter);
+ ngAfterViewInit(): void {
+ this.dropZoneContainerRef()!.nativeElement.addEventListener('dragenter', this.dragEnterHandler);
}
ngOnDestroy(): void {
- window.removeEventListener('dragenter', this.onGlobalDragEnter);
+ this.dropZoneContainerRef()!.nativeElement.removeEventListener('dragenter', this.dragEnterHandler);
}
- onGlobalDragEnter = (event: DragEvent) => {
+ private dragEnterHandler = (event: DragEvent) => {
if (event.dataTransfer?.types?.includes('Files')) {
this.isDragOver.set(true);
}
@@ -94,12 +97,20 @@ export class FilesTreeComponent implements OnInit, OnDestroy {
onDragOver(event: DragEvent) {
event.preventDefault();
+ event.stopPropagation();
event.dataTransfer!.dropEffect = 'copy';
this.isDragOver.set(true);
}
+ onDragLeave(event: Event) {
+ event.preventDefault();
+ event.stopPropagation();
+ this.isDragOver.set(false);
+ }
+
onDrop(event: DragEvent) {
event.preventDefault();
+ event.stopPropagation();
this.isDragOver.set(false);
const files = event.dataTransfer?.files;