Skip to content

Commit

Permalink
[ACS-8634] "Manage Searches" - a full page list of saved searches (#4181
Browse files Browse the repository at this point in the history
)

* [ACS-8751] Adapt search results to handle query encoding and state propagation

* Changes after CR, bug fixed

* Changes after CR, bug fixed

* [ACS-8634] "Manage Searches" - a full page list of saved searches

* Changes after Code Review

* [ACS-8634] Update package-lock

* [ACS-8634] Minor fixes

* [ACS-8634] Use latest ADF, fix unit tests

* [ACS-8634] Fix package lock

* [ACS-8634] Minor fixes

* [ACS-8634] CR fixes

* [ACS-8634] Sidenav state fixes

* [ACS-8634] Final fix for sidenav state

* [ACS-8634] CR fixes

* [ACS-8634] CR fix

---------

Co-authored-by: MichalKinas <[email protected]>
  • Loading branch information
dominikiwanekhyland and MichalKinas authored Oct 31, 2024
1 parent cacc414 commit c2d2f95
Show file tree
Hide file tree
Showing 37 changed files with 2,730 additions and 4,153 deletions.
5,522 changes: 1,410 additions & 4,112 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
},
"private": true,
"dependencies": {
"@alfresco/adf-content-services": "7.0.0-alpha.4",
"@alfresco/adf-core": "7.0.0-alpha.4",
"@alfresco/adf-extensions": "7.0.0-alpha.4",
"@alfresco/eslint-plugin-eslint-angular": "7.0.0-alpha.4",
"@alfresco/js-api": "8.0.0-alpha.4",
"@alfresco/adf-content-services": "7.0.0-alpha.5-11519897356",
"@alfresco/adf-core": "7.0.0-alpha.5-11519897356",
"@alfresco/adf-extensions": "7.0.0-alpha.5-11519897356",
"@alfresco/eslint-plugin-eslint-angular": "7.0.0-alpha.5-11519897356",
"@alfresco/js-api": "8.0.0-alpha.5-11519897356",
"@angular/animations": "16.2.9",
"@angular/cdk": "16.2.9",
"@angular/common": "16.2.9",
Expand Down Expand Up @@ -62,8 +62,8 @@
"zone.js": "0.13.3"
},
"devDependencies": {
"@alfresco/adf-cli": "7.0.0-alpha.4",
"@angular-devkit/build-angular": "16.2.16",
"@alfresco/adf-cli": "7.0.0-alpha.5-11519897356",
"@angular-devkit/build-angular": "16.2.9",
"@angular-devkit/core": "16.2.9",
"@angular-devkit/schematics": "16.2.9",
"@angular-eslint/builder": "17.0.0",
Expand Down
26 changes: 24 additions & 2 deletions projects/aca-content/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,31 @@
"NAME_REQUIRED_ERROR": "This field is required",
"DESCRIPTION_LABEL": "Description",
"SAVE_SUCCESS": "Search Saved",
"SAVE_ERROR": "Error occured. Search could not be saved.",
"SAVE_ERROR": "Error occurred. Search could not be saved.",
"NAVBAR": {
"TITLE": "Saved Searches ({{ number }})"
"TITLE": "Saved Searches ({{ number }})",
"MANAGE_BUTTON": "Manage searches"
},
"EDIT_DIALOG": {
"CONTEXT_OPTION": "Edit Search",
"TITLE": "Edit Saved Search",
"SUCCESS_MESSAGE": "Saved Search edited successfully",
"ERROR_MESSAGE": "Error occurred. Saved Search could not be edited."
},
"DELETE_DIALOG": {
"CONTEXT_OPTION": "Delete Search",
"TITLE": "Delete Saved Search",
"CONTENT": "Are you sure you want to delete this search",
"SUCCESS_MESSAGE": "Saved Search deleted successfully",
"ERROR_MESSAGE": "Error occurred. Saved Search could not be deleted."
},
"LIST": {
"TITLE": "Saved Searches",
"NAME": "Name",
"DESCRIPTION": "Description",
"EMPTY_LIST": "No saved searches",
"COPY_TO_CLIPBOARD": "Copy to clipboard",
"COPY_TO_CLIPBOARD_SUCCESS": "Search copied to clipboard"
}
},
"FOUND_RESULTS": "{{ number }} results found",
Expand Down
8 changes: 4 additions & 4 deletions projects/aca-content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"peerDependencies": {
"@angular/common": ">=15.2",
"@angular/core": ">=15.2",
"@alfresco/adf-core": ">=7.0.0-alpha.4",
"@alfresco/adf-content-services": ">=7.0.0-alpha.4",
"@alfresco/adf-extensions": ">=7.0.0-alpha.4",
"@alfresco/js-api": ">=8.0.0-alpha.4",
"@alfresco/adf-core": ">=7.0.0-alpha.5-0",
"@alfresco/adf-content-services": ">=7.0.0-alpha.5-0",
"@alfresco/adf-extensions": ">=7.0.0-alpha.5-0",
"@alfresco/js-api": ">=8.0.0-alpha.5-0",
"@angular/animations": ">=15.2",
"@angular/cdk": ">=15.2",
"@angular/forms": ">=15.2",
Expand Down
12 changes: 11 additions & 1 deletion projects/aca-content/src/lib/aca-content.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { LibrariesComponent } from './components/libraries/libraries.component';
import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component';
import { SearchResultsComponent } from './components/search/search-results/search-results.component';
import { SearchLibrariesResultsComponent } from './components/search/search-libraries-results/search-libraries-results.component';
import { AppSharedRuleGuard, GenericErrorComponent, ExtensionRoute, ExtensionsDataLoaderGuard, PluginEnabledGuard } from '@alfresco/aca-shared';
import { AppSharedRuleGuard, ExtensionRoute, ExtensionsDataLoaderGuard, GenericErrorComponent, PluginEnabledGuard } from '@alfresco/aca-shared';
import { AuthGuard, UnsavedChangesGuard } from '@alfresco/adf-core';
import { FavoritesComponent } from './components/favorites/favorites.component';
import { RecentFilesComponent } from './components/recent-files/recent-files.component';
Expand All @@ -41,6 +41,7 @@ import { SharedLinkViewComponent } from './components/shared-link-view/shared-li
import { TrashcanComponent } from './components/trashcan/trashcan.component';
import { ShellLayoutComponent } from '@alfresco/adf-core/shell';
import { SearchAiResultsComponent } from './components/knowledge-retrieval/search-ai/search-ai-results/search-ai-results.component';
import { SavedSearchesSmartListComponent } from './components/search/search-save/list/smart-list/saved-searches-smart-list.component';

export const CONTENT_ROUTES: ExtensionRoute[] = [
{
Expand Down Expand Up @@ -354,6 +355,15 @@ export const CONTENT_LAYOUT_ROUTES: Route = {
...createViewRoutes('knowledge-retrieval')
]
},
{
path: 'saved-searches',
children: [
{
path: '',
component: SavedSearchesSmartListComponent
}
]
},
{
path: '**',
component: GenericErrorComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ export class SearchResultsComponent extends PageComponent implements OnInit {

this.subscriptions.push(
this.queryBuilder.updated.subscribe((query) => {
this.isLoading = true;
if (query) {
this.sorting = this.getSorting();
this.isLoading = true;
this.changeDetectorRef.detectChanges();
}
}),
Expand All @@ -203,6 +203,9 @@ export class SearchResultsComponent extends PageComponent implements OnInit {

if (this.route) {
this.route.queryParams.pipe(takeUntil(this.onDestroy$)).subscribe((params: Params) => {
if (params[this.queryParamName]) {
this.isLoading = true;
}
this.loadedFilters$.next();
this.encodedQuery = params[this.queryParamName] || null;
this.searchedWord = extractSearchedWordFromEncodedQuery(this.encodedQuery);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<div class="aca-saved-search-delete-dialog__header">
<h2 class="aca-saved-search-delete-dialog__title">{{"APP.BROWSE.SEARCH.SAVE_SEARCH.DELETE_DIALOG.TITLE" | translate}}</h2>
<button
mat-icon-button
mat-dialog-close
[attr.aria-label]="'CLOSE' | translate"
[attr.title]="'CLOSE' | translate">
<mat-icon>close</mat-icon>
</button>
</div>
<mat-dialog-content>
<p>{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.DELETE_DIALOG.CONTENT' | translate }}</p>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button
mat-dialog-close
id="aca-save-search-delete-dialog-cancel-button">{{ 'CANCEL' | titlecase | translate }}</button>
<button mat-flat-button
id="aca-save-search-delete-dialog-submit-button"
color="primary"
[disabled]="isLoading"
(click)="onSubmit()">{{ 'DELETE' | titlecase | translate }}</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.aca-saved-search-delete-dialog {
.aca-saved-search-delete-dialog__header {
display: flex;
align-items: center;
padding-left: 20px;
justify-content: space-between;
}

.aca-saved-search-delete-dialog__title {
font-size: large;
font-weight: 200;
margin: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*!
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/

import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { of } from 'rxjs';
import { SavedSearchDeleteDialogComponent } from './saved-search-delete-dialog.component';
import { ContentTestingModule, SavedSearch, SavedSearchesService } from '@alfresco/adf-content-services';
import { provideMockStore } from '@ngrx/store/testing';
import { Store } from '@ngrx/store';
import { SnackbarErrorAction, SnackbarInfoAction } from '@alfresco/aca-shared/store';
import { AppTestingModule } from '../../../../../testing/app-testing.module';

describe('SaveSearchDeleteDialogComponent', () => {
let fixture: ComponentFixture<SavedSearchDeleteDialogComponent>;
let savedSearchesService: SavedSearchesService;
let store: Store;
let submitButton: HTMLButtonElement;
let cancelButton: HTMLButtonElement;

const savedSearchToDelete: SavedSearch = {
name: 'test',
encodedUrl: '1234',
order: 0
};

const dialogRef = {
close: jasmine.createSpy('close')
};

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule, AppTestingModule],
providers: [
{ provide: MatDialogRef, useValue: dialogRef },
provideMockStore(),
{ provide: SavedSearchesService, useValue: { deleteSavedSearch: () => of() } },
{ provide: MAT_DIALOG_DATA, useValue: savedSearchToDelete }
]
});
dialogRef.close.calls.reset();
fixture = TestBed.createComponent(SavedSearchDeleteDialogComponent);
savedSearchesService = TestBed.inject(SavedSearchesService);
store = TestBed.inject(Store);

submitButton = fixture.nativeElement.querySelector('#aca-save-search-delete-dialog-submit-button');
cancelButton = fixture.nativeElement.querySelector('#aca-save-search-delete-dialog-cancel-button');
});

afterEach(() => {
fixture.destroy();
});

it('should not delete and close dialog window if cancel button clicked', () => {
spyOn(savedSearchesService, 'deleteSavedSearch').and.callThrough();
cancelButton.click();
expect(savedSearchesService.deleteSavedSearch).not.toHaveBeenCalled();
expect(dialogRef.close).toHaveBeenCalled();
});

it('should delete search, show snackbar message and close modal if submit button clicked', fakeAsync(() => () => {
spyOn(savedSearchesService, 'deleteSavedSearch').and.callThrough();
clickSubmitButton();
expect(store.dispatch).toHaveBeenCalledWith(new SnackbarInfoAction('APP.BROWSE.SEARCH.SAVE_SEARCH.DELETE_DIALOG.SUCCESS_MESSAGE'));
expect(dialogRef.close).toHaveBeenCalled();
}));

it('should show snackbar error if there is delete error', fakeAsync(() => () => {
spyOn(savedSearchesService, 'deleteSavedSearch').and.throwError('');
clickSubmitButton();
expect(store.dispatch).toHaveBeenCalledWith(new SnackbarErrorAction('APP.BROWSE.SEARCH.SAVE_SEARCH.DELETE_DIALOG.SUCCESS_MESSAGE'));
expect(dialogRef.close).not.toHaveBeenCalled();
}));

function clickSubmitButton() {
submitButton.click();
tick();
expect(savedSearchesService.deleteSavedSearch).toHaveBeenCalledWith(savedSearchToDelete);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*!
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/

import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { SavedSearch, SavedSearchesService } from '@alfresco/adf-content-services';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { take } from 'rxjs/operators';
import { AppStore, SnackbarErrorAction, SnackbarInfoAction } from '@alfresco/aca-shared/store';
import { Store } from '@ngrx/store';
import { CoreModule } from '@alfresco/adf-core';

@Component({
standalone: true,
imports: [CoreModule],
selector: 'aca-saved-search-delete-dialog',
templateUrl: './saved-search-delete-dialog.component.html',
styleUrls: ['./saved-search-delete-dialog.component.scss'],
encapsulation: ViewEncapsulation.None,
host: { class: 'aca-saved-search-delete-dialog' }
})
export class SavedSearchDeleteDialogComponent {
isLoading = false;

constructor(
private readonly dialog: MatDialogRef<SavedSearchDeleteDialogComponent>,
private readonly savedSearchesService: SavedSearchesService,
private readonly store: Store<AppStore>,
@Inject(MAT_DIALOG_DATA) private readonly data: SavedSearch
) {}

onSubmit() {
if (this.isLoading) {
return;
}
this.isLoading = true;
this.savedSearchesService
.deleteSavedSearch(this.data)
.pipe(take(1))
.subscribe({
next: () => {
this.dialog.close(this.data);
this.store.dispatch(new SnackbarInfoAction('APP.BROWSE.SEARCH.SAVE_SEARCH.DELETE_DIALOG.SUCCESS_MESSAGE'));
this.isLoading = false;
},
error: () => {
this.store.dispatch(new SnackbarErrorAction('APP.BROWSE.SEARCH.SAVE_SEARCH.DELETE_DIALOG.ERROR_MESSAGE'));
this.isLoading = false;
}
});
}
}
Loading

0 comments on commit c2d2f95

Please sign in to comment.