Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
[title]="'home.loggedIn.dashboard.title' | translate"
[icon]="'fas fa-home'"
[buttonLabel]="'home.loggedIn.dashboard.createProject' | translate"
[isButtonDisabled]="projectCreationDisabled()"
[buttonTooltip]="buttonTooltip() | translate"
(buttonClick)="createProject()"
/>
<osf-scheduled-banner />
Expand Down Expand Up @@ -69,11 +71,13 @@ <h1>{{ 'home.loggedIn.latestResearch.title' | translate }}</h1>
[title]="'home.loggedIn.dashboard.welcome' | translate"
[icon]="'home'"
[buttonLabel]="'home.loggedIn.dashboard.createProject' | translate"
[isButtonDisabled]="projectCreationDisabled()"
[buttonTooltip]="buttonTooltip() | translate"
(buttonClick)="createProject()"
/>
<div class="flex items-center justify-center min-h-screen bg-white pt-4">
<div class="text-center max-w-2xl px-6 w-full">
<p class="mb-4">{{ 'home.loggedIn.dashboard.noCreatedProject' | translate }}</p>
<p class="mb-4">{{ noProjectsMessage() | translate }}</p>

<p class="mb-6">{{ 'home.loggedIn.dashboard.watchVideoBelow' | translate }}</p>

Expand Down
11 changes: 11 additions & 0 deletions src/app/features/home/pages/dashboard/dashboard.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';

import { ScheduledBannerComponent } from '@core/components/osf-banners/scheduled-banner/scheduled-banner.component';
import { UserSelectors } from '@osf/core/store/user';
import { CreateProjectDialogComponent } from '@osf/features/my-projects/components';
import { IconComponent } from '@osf/shared/components/icon/icon.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
Expand Down Expand Up @@ -49,6 +50,7 @@ describe('DashboardComponent', () => {
{ selector: MyResourcesSelectors.getProjects, value: [] },
{ selector: MyResourcesSelectors.getTotalProjects, value: 0 },
{ selector: MyResourcesSelectors.getProjectsLoading, value: false },
{ selector: UserSelectors.getActiveFlags, value: [] },
];

interface SetupOverrides extends BaseSetupOverrides {
Expand Down Expand Up @@ -98,6 +100,15 @@ describe('DashboardComponent', () => {
expect(component).toBeTruthy();
});

it('should disable project creation and show tooltip when prevent_project_creation flag is active', () => {
setup({
selectorOverrides: [{ selector: UserSelectors.getActiveFlags, value: ['prevent_project_creation'] }],
});

expect(component.projectCreationDisabled()).toBe(true);
expect(component.buttonTooltip()).toBe('myProjects.header.createProjectDisabledTooltip');
});

it('should read query params and fetch projects on init', () => {
setup({
routeQueryParams: {
Expand Down
14 changes: 13 additions & 1 deletion src/app/features/home/pages/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { FormControl } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';

import { ScheduledBannerComponent } from '@core/components/osf-banners/scheduled-banner/scheduled-banner.component';
import { UserSelectors } from '@osf/core/store/user';
import { CreateProjectDialogComponent } from '@osf/features/my-projects/components';
import { IconComponent } from '@osf/shared/components/icon/icon.component';
import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
Expand All @@ -29,7 +30,6 @@ import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'
import { ProjectRedirectDialogService } from '@osf/shared/services/project-redirect-dialog.service';
import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores/my-resources';
import { TableParameters } from '@shared/models/table-parameters.model';

@Component({
selector: 'osf-dashboard',
imports: [
Expand All @@ -54,6 +54,7 @@ export class DashboardComponent implements OnInit {
private readonly projectRedirectDialogService = inject(ProjectRedirectDialogService);
private readonly platformId = inject(PLATFORM_ID);
private readonly isBrowser = isPlatformBrowser(this.platformId);
private readonly activeFlags = select(UserSelectors.getActiveFlags);

readonly searchControl = new FormControl<string>('');
readonly activeProject = signal<MyResourcesItem | null>(null);
Expand All @@ -72,7 +73,18 @@ export class DashboardComponent implements OnInit {
return this.projects().filter((project) => project.title.toLowerCase().includes(search));
});

readonly projectCreationDisabled = computed(() => this.activeFlags().includes('prevent_project_creation'));
readonly buttonTooltip = computed(() =>
this.projectCreationDisabled() ? 'myProjects.header.createProjectDisabledTooltip' : ''
);

readonly existsProjects = computed(() => this.projects().length || !!this.searchControl.value?.length);
readonly noProjectsMessage = computed(() => {
if (this.projectCreationDisabled()) {
return 'home.loggedIn.dashboard.noCreatedProjectAndCreateProjectDisabled';
}
return 'home.loggedIn.dashboard.noCreatedProject';
});

constructor() {
this.setupSearchSubscription();
Expand Down
2 changes: 2 additions & 0 deletions src/app/features/my-projects/my-projects.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
[showButton]="true"
[buttonLabel]="'myProjects.header.createProject' | translate"
[title]="'myProjects.header.title' | translate"
[isButtonDisabled]="projectCreationDisabled()"
[buttonTooltip]="buttonTooltip() | translate"
[icon]="'custom-icon-projects-dark'"
(buttonClick)="createProject()"
/>
Expand Down
9 changes: 9 additions & 0 deletions src/app/features/my-projects/my-projects.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Mock } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';

import { UserSelectors } from '@osf/core/store/user/user.selectors';
import { MyProjectsTableComponent } from '@osf/shared/components/my-projects-table/my-projects-table.component';
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
import { SelectComponent } from '@osf/shared/components/select/select.component';
Expand Down Expand Up @@ -77,6 +78,7 @@ describe('MyProjectsComponent', () => {
{ selector: BookmarksSelectors.getBookmarks, value: [] },
{ selector: BookmarksSelectors.getBookmarksCollectionId, value: 'bookmark-collection-id' },
{ selector: BookmarksSelectors.getBookmarksTotalCount, value: 0 },
{ selector: UserSelectors.getActiveFlags, value: [] },
];

function setup(selectorOverrides?: SignalOverride[]) {
Expand Down Expand Up @@ -132,6 +134,13 @@ describe('MyProjectsComponent', () => {
expect(component).toBeTruthy();
});

it('should disable project creation and show tooltip when prevent_project_creation flag is active', () => {
setup([{ selector: UserSelectors.getActiveFlags, value: ['prevent_project_creation'] }]);

expect(component.projectCreationDisabled()).toBe(true);
expect(component.buttonTooltip()).toBe('myProjects.header.createProjectDisabledTooltip');
});

it('should dispatch get bookmarks collection id on init', () => {
setup();
expect(store.dispatch).toHaveBeenCalledWith(new GetBookmarksCollectionId());
Expand Down
6 changes: 6 additions & 0 deletions src/app/features/my-projects/my-projects.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { FormControl, FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { UserSelectors } from '@osf/core/store/user';
import { MyProjectsTableComponent } from '@osf/shared/components/my-projects-table/my-projects-table.component';
import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component';
import { SelectComponent } from '@osf/shared/components/select/select.component';
Expand Down Expand Up @@ -83,6 +84,7 @@ export class MyProjectsComponent implements OnInit {
readonly tableParamsService = inject(MyProjectsTableParamsService);
readonly platformId = inject(PLATFORM_ID);
readonly isBrowser = isPlatformBrowser(this.platformId);
readonly activeFlags = select(UserSelectors.getActiveFlags);

readonly isLoading = signal(false);
readonly isMedium = toSignal(inject(IS_MEDIUM));
Expand Down Expand Up @@ -113,6 +115,10 @@ export class MyProjectsComponent implements OnInit {
readonly bookmarksCollectionId = select(BookmarksSelectors.getBookmarksCollectionId);
readonly totalBookmarksCount = select(BookmarksSelectors.getBookmarksTotalCount);
readonly isBookmarks = computed(() => this.selectedTab() === MyProjectsTab.Bookmarks);
readonly projectCreationDisabled = computed(() => this.activeFlags().includes('prevent_project_creation'));
readonly buttonTooltip = computed(() =>
this.projectCreationDisabled() ? 'myProjects.header.createProjectDisabledTooltip' : ''
);

readonly actions = createDispatchMap({
getBookmarksCollectionId: GetBookmarksCollectionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ <h1 data-test-page-heading class="flex align-items-center word-break-word">
[loading]="isSubmitting()"
[disabled]="isButtonDisabled()"
data-test-sub-header-button
[pTooltip]="buttonTooltip()"
></p-button>
</div>
}
Expand Down
11 changes: 11 additions & 0 deletions src/app/shared/components/sub-header/sub-header.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ describe('SubHeaderComponent', () => {
expect(component.isButtonDisabled()).toBe(true);
});

it('should set buttonTooltip input correctly', () => {
fixture.componentRef.setInput('buttonTooltip', 'Test button tooltip');
expect(component.buttonTooltip()).toBe('Test button tooltip');
});

it('should emit buttonClick event', () => {
const emitSpy = vi.spyOn(component.buttonClick, 'emit');

Expand Down Expand Up @@ -155,12 +160,14 @@ describe('SubHeaderComponent', () => {
fixture.componentRef.setInput('description', 'Description with special chars: <>&"\'');
fixture.componentRef.setInput('buttonLabel', 'Button with special chars: !@#$%');
fixture.componentRef.setInput('tooltip', 'Tooltip with special chars: [{}]|\\');
fixture.componentRef.setInput('buttonTooltip', 'Button tooltip with special chars: @#$%()<>');
fixture.componentRef.setInput('icon', 'pi-icon-with-special-chars');

expect(component.title()).toBe('Title with special chars: @#$%^&*()');
expect(component.description()).toBe('Description with special chars: <>&"\'');
expect(component.buttonLabel()).toBe('Button with special chars: !@#$%');
expect(component.tooltip()).toBe('Tooltip with special chars: [{}]|\\');
expect(component.buttonTooltip()).toBe('Button tooltip with special chars: @#$%()<>');
expect(component.icon()).toBe('pi-icon-with-special-chars');
});

Expand All @@ -169,12 +176,14 @@ describe('SubHeaderComponent', () => {
fixture.componentRef.setInput('description', '');
fixture.componentRef.setInput('buttonLabel', '');
fixture.componentRef.setInput('tooltip', '');
fixture.componentRef.setInput('buttonTooltip', '');
fixture.componentRef.setInput('icon', '');

expect(component.title()).toBe('');
expect(component.description()).toBe('');
expect(component.buttonLabel()).toBe('');
expect(component.tooltip()).toBe('');
expect(component.buttonTooltip()).toBe('');
expect(component.icon()).toBe('');
});

Expand All @@ -193,9 +202,11 @@ describe('SubHeaderComponent', () => {
fixture.componentRef.setInput('showButton', true);
fixture.componentRef.setInput('isButtonDisabled', true);
fixture.componentRef.setInput('buttonLabel', 'Disabled Button');
fixture.componentRef.setInput('buttonTooltip', 'Disabled Button Tooltip');

expect(component.showButton()).toBe(true);
expect(component.isButtonDisabled()).toBe(true);
expect(component.buttonLabel()).toBe('Disabled Button');
expect(component.buttonTooltip()).toBe('Disabled Button Tooltip');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ export class SubHeaderComponent {
isLoading = input<boolean>(false);
isSubmitting = input<boolean>(false);
isButtonDisabled = input<boolean>(false);
buttonTooltip = input<string>('');
buttonClick = output<void>();
}
4 changes: 4 additions & 0 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,8 @@
"loggedIn": {
"dashboard": {
"createProject": "Create New Project",
"createProjectDisabledTooltip": "Projects can no longer be created.",

"getStartedHelp": "Visit Get Started Help Guides",
"images": {
"osfCollectionsImageAltText": "OSF Collections",
Expand All @@ -804,6 +806,7 @@
"osfRegistriesImageAltTest": "OSF Registries"
},
"noCreatedProject": "You haven’t created a project yet. Click the \"Create New Project\" button above to get started.",
"noCreatedProjectAndCreateProjectDisabled": "You haven’t created a project yet.",
"quickSearch": {
"goTo": "Go to",
"myProjects": "My Projects",
Expand Down Expand Up @@ -1095,6 +1098,7 @@
},
"header": {
"createProject": "Create Project",
"createProjectDisabledTooltip": "Projects can no longer be created.",
"title": "My Projects"
},
"redirectDialog": {
Expand Down
Loading