From 0a84659c3b39cd34b70402b63d7bf172381fd963 Mon Sep 17 00:00:00 2001 From: nsemets Date: Fri, 3 Oct 2025 15:36:35 +0300 Subject: [PATCH] fix(ui): fixed some ui bugs --- .../rename-file-dialog.component.html | 4 ++-- .../rename-file-dialog.component.ts | 11 +++++---- .../files/pages/files/files.component.ts | 1 + .../license-dialog.component.html | 1 + .../developer-app-add-edit-form.component.ts | 1 + .../components/name/name.component.ts | 24 +++++++++++++++---- .../files-tree/files-tree.component.ts | 13 ++++++++-- .../components/license/license.component.html | 5 ++-- .../components/license/license.component.ts | 1 + .../wiki/wiki-list/wiki-list.component.html | 22 ++++++++++------- .../shared/constants/input-limits.const.ts | 3 +++ .../helpers/custom-form-validators.helper.ts | 12 ++++++++++ src/assets/i18n/en.json | 21 ++++++++++------ 13 files changed, 89 insertions(+), 30 deletions(-) diff --git a/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html index 2abb1b505..b052e04d4 100644 --- a/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html +++ b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.html @@ -4,7 +4,7 @@ [control]="renameForm.controls['name']" [label]="'files.dialogs.renameFile.newName'" [placeholder]="'files.dialogs.renameFile.enterNewName'" - [maxLength]="nameLimit" + [maxLength]="nameMaxLength" [minLength]="nameMinLength" > @@ -14,7 +14,7 @@ type="button" severity="secondary" [label]="'common.buttons.cancel' | translate" - (click)="onCancel()" + (onClick)="onCancel()" > diff --git a/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts index b9b4e5c48..01a04057b 100644 --- a/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts +++ b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts @@ -7,7 +7,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { TextInputComponent } from '@osf/shared/components'; -import { InputLimits } from '@osf/shared/constants'; +import { forbiddenFileNameCharacters, InputLimits } from '@osf/shared/constants'; import { CustomValidators } from '@osf/shared/helpers'; @Component({ @@ -20,13 +20,16 @@ export class RenameFileDialogComponent { private readonly dialogRef = inject(DynamicDialogRef); private readonly config = inject(DynamicDialogConfig); - readonly nameLimit = InputLimits.name.maxLength; - readonly nameMinLength = InputLimits.name.minLength; + readonly nameMaxLength = InputLimits.title.maxLength; + readonly nameMinLength = InputLimits.title.minLength; readonly renameForm = new FormGroup({ name: new FormControl(this.config.data?.currentName ?? '', { nonNullable: true, - validators: [CustomValidators.requiredTrimmed()], + validators: [ + CustomValidators.requiredTrimmed(), + CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + ], }), }); diff --git a/src/app/features/files/pages/files/files.component.ts b/src/app/features/files/pages/files/files.component.ts index 4fccfa5b4..21fe23a36 100644 --- a/src/app/features/files/pages/files/files.component.ts +++ b/src/app/features/files/pages/files/files.component.ts @@ -457,6 +457,7 @@ export class FilesComponent { finalize(() => { this.updateFilesList(); this.fileIsUploading.set(false); + this.toastService.showSuccess('files.dialogs.createFolder.success'); }) ) .subscribe(); diff --git a/src/app/features/metadata/dialogs/license-dialog/license-dialog.component.html b/src/app/features/metadata/dialogs/license-dialog/license-dialog.component.html index d36bed309..019867cbc 100644 --- a/src/app/features/metadata/dialogs/license-dialog/license-dialog.component.html +++ b/src/app/features/metadata/dialogs/license-dialog/license-dialog.component.html @@ -13,6 +13,7 @@

{{ 'project.metadata.license.dialog.chooseLicense.label' | tran [isSubmitting]="isSubmitting()" [showInternalButtons]="false" [fullWidthSelect]="true" + [appendTo]="'body'" (selectLicense)="onSelectLicense($event)" (createLicense)="onCreateLicense($event)" /> diff --git a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts index 99932c866..5f57de536 100644 --- a/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts +++ b/src/app/features/settings/developer-apps/components/developer-app-add-edit-form/developer-app-add-edit-form.component.ts @@ -23,6 +23,7 @@ import { CreateDeveloperApp, DeveloperAppsSelectors, UpdateDeveloperApp } from ' templateUrl: './developer-app-add-edit-form.component.html', styleUrl: './developer-app-add-edit-form.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, + providers: [DynamicDialogRef], }) export class DeveloperAppAddEditFormComponent implements OnInit { readonly isEditMode = input(false); diff --git a/src/app/features/settings/profile-settings/components/name/name.component.ts b/src/app/features/settings/profile-settings/components/name/name.component.ts index 5f299caef..17a114cce 100644 --- a/src/app/features/settings/profile-settings/components/name/name.component.ts +++ b/src/app/features/settings/profile-settings/components/name/name.component.ts @@ -9,6 +9,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormBuilder } from '@angular/forms'; import { UpdateProfileSettingsUser, UserSelectors } from '@osf/core/store/user'; +import { forbiddenFileNameCharacters } from '@osf/shared/constants'; import { CustomValidators } from '@osf/shared/helpers'; import { UserModel } from '@osf/shared/models'; import { CustomConfirmationService, LoaderService, ToastService } from '@osf/shared/services'; @@ -39,10 +40,25 @@ export class NameComponent { readonly fb = inject(FormBuilder); readonly form = this.fb.group({ - fullName: this.fb.control('', { nonNullable: true, validators: [CustomValidators.requiredTrimmed()] }), - givenName: this.fb.control('', { nonNullable: true }), - middleNames: this.fb.control('', { nonNullable: true }), - familyName: this.fb.control('', { nonNullable: true }), + fullName: this.fb.control('', { + nonNullable: true, + validators: [ + CustomValidators.requiredTrimmed(), + CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + ], + }), + givenName: this.fb.control('', { + nonNullable: true, + validators: CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + }), + middleNames: this.fb.control('', { + nonNullable: true, + validators: CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + }), + familyName: this.fb.control('', { + nonNullable: true, + validators: CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + }), suffix: this.fb.control('', { nonNullable: true }), }); 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 38d025481..f3e99c3ef 100644 --- a/src/app/shared/components/files-tree/files-tree.component.ts +++ b/src/app/shared/components/files-tree/files-tree.component.ts @@ -316,7 +316,9 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit { deleteEntry(link: string): void { this.actions().setFilesIsLoading?.(true); - this.actions().deleteEntry?.(this.resourceId(), link); + this.actions() + .deleteEntry?.(this.resourceId(), link) + .subscribe(() => this.toastService.showSuccess('files.dialogs.deleteFile.success')); } confirmRename(file: OsfFile): void { @@ -338,7 +340,10 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit { renameEntry(newName: string, file: OsfFile): void { if (newName.trim() && file.links.upload) { this.actions().setFilesIsLoading?.(true); - this.actions().renameEntry?.(this.resourceId(), file.links.upload, newName); + + this.actions() + .renameEntry?.(this.resourceId(), file.links.upload, newName) + .subscribe(() => this.toastService.showSuccess('files.dialogs.renameFile.success')); } } @@ -385,6 +390,10 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit { this.resetPagination(); if (foldersStack) { this.foldersStack = [...foldersStack]; + + if (action === 'copy') { + this.toastService.showSuccess('files.dialogs.copyFile.success'); + } } }); }); diff --git a/src/app/shared/components/license/license.component.html b/src/app/shared/components/license/license.component.html index ecd763e4d..0f2ea1718 100644 --- a/src/app/shared/components/license/license.component.html +++ b/src/app/shared/components/license/license.component.html @@ -1,12 +1,13 @@ @if (selectedLicense()) { diff --git a/src/app/shared/components/license/license.component.ts b/src/app/shared/components/license/license.component.ts index 730a105c6..f99a0a552 100644 --- a/src/app/shared/components/license/license.component.ts +++ b/src/app/shared/components/license/license.component.ts @@ -42,6 +42,7 @@ export class LicenseComponent { isSubmitting = input(false); showInternalButtons = input(true); fullWidthSelect = input(false); + appendTo = input(null); selectedLicense = model(null); createLicense = output<{ id: string; licenseOptions: LicenseOptions }>(); selectLicense = output(); diff --git a/src/app/shared/components/wiki/wiki-list/wiki-list.component.html b/src/app/shared/components/wiki/wiki-list/wiki-list.component.html index dd63ab679..565ba9352 100644 --- a/src/app/shared/components/wiki/wiki-list/wiki-list.component.html +++ b/src/app/shared/components/wiki/wiki-list/wiki-list.component.html @@ -11,15 +11,19 @@ } @else { @if (expanded()) { -
- +
+ @if (canEdit()) { + + } ~!@$&*:;,"'\\|/?]/; diff --git a/src/app/shared/helpers/custom-form-validators.helper.ts b/src/app/shared/helpers/custom-form-validators.helper.ts index 5991990c1..b4f665435 100644 --- a/src/app/shared/helpers/custom-form-validators.helper.ts +++ b/src/app/shared/helpers/custom-form-validators.helper.ts @@ -98,4 +98,16 @@ export class CustomValidators { return null; }; } + + static forbiddenCharactersValidator(pattern: RegExp): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; + if (!value) { + return null; + } + + const hasForbiddenCharacters = pattern.test(value); + return hasForbiddenCharacters ? { forbiddenCharacters: true } : null; + }; + } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index ea966de53..1dac3fde8 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -195,7 +195,7 @@ "donate": "Donate", "profileSettings": "Profile Settings", "accountSettings": "Account Settings", - "configureAddonAccounts": "Configure Addon Accounts", + "configureAddonAccounts": "Configure add-on & link service accounts", "notifications": "Notifications", "developerApps": "Developer Apps", "personalAccessTokens": "Personal Access Tokens", @@ -1081,32 +1081,39 @@ "createFolder": { "title": "Create folder", "folderName": "New folder name", - "folderNamePlaceholder": "Please enter a folder name" + "folderNamePlaceholder": "Please enter a folder name", + "success": "Folder successfully created." }, "renameFile": { "title": "Rename file", "newName": "New name", "enterNewName": "Enter new name", - "renameLabel": "Please rename the file" + "renameLabel": "Please rename the file", + "success": "File successfully renamed." }, "moveFile": { "cannotMove": "Cannot move to the same folder", "title": "Move file", "message": "Are you sure you want to move {{dragNodeName}} to {{dropNodeName}} ?", "storage": "OSF Storage", - "pathError": "Path is not specified!" + "pathError": "Path is not specified!", + "success": "File successfully moved." }, "copyFile": { - "title": "Select location to copy file to" + "title": "Select location to copy file to", + "success": "File successfully copied." }, "deleteFile": { "title": "Delete File", - "message": "Are you sure you want to delete {{name}}?" + "message": "Are you sure you want to delete {{name}}?", + "success": "File successfully deleted." }, "replaceFile": { "single": "Replace file", "multiple": "Replace files", - "message": "Are you sure you want to replace {{name}}?" + "message": "Are you sure you want to replace {{name}}?", + "successOne": "File successfully replaced.", + "successMultiple": "File successfully replaced." } }, "filesBrowserDialog": {