diff --git a/apps/red-ui/src/app/app-routing.module.ts b/apps/red-ui/src/app/app-routing.module.ts index 7de4f8021..2ca32ea3e 100644 --- a/apps/red-ui/src/app/app-routing.module.ts +++ b/apps/red-ui/src/app/app-routing.module.ts @@ -147,7 +147,7 @@ const mainRoutes: IqserRoutes = [ }, { path: 'search', - loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule), + loadComponent: () => import('./modules/search/search-screen/search-screen.component'), canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()], data: { routeGuards: [IqserAuthGuard, RedRoleGuard], @@ -159,7 +159,7 @@ const mainRoutes: IqserRoutes = [ }, { path: 'trash', - loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule), + loadChildren: () => import('./modules/trash/trash.routes'), canActivate: [CompositeRouteGuard, IqserPermissionsGuard], data: { routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard], diff --git a/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts b/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts index fff7c1e2f..a586590e2 100644 --- a/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts +++ b/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts @@ -3,11 +3,31 @@ import { workflowFileStatusTranslations } from '@translations/file-status-transl import { ISearchListItem } from '@red/domain'; import { escapeHtml } from '@iqser/common-ui/lib/utils'; import { getDossierRouterLink } from '@utils/router-links'; +import { MatTooltip } from '@angular/material/tooltip'; +import { NgForOf, NgIf } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { StopPropagationDirective } from '@iqser/common-ui'; +import { InitialsAvatarComponent } from '@common-ui/users'; +import { StatusBarComponent } from '@common-ui/shared'; +import { MatIcon } from '@angular/material/icon'; +import { RouterLink } from '@angular/router'; @Component({ selector: 'redaction-search-item-template', templateUrl: './search-item-template.component.html', styleUrls: ['./search-item-template.component.scss'], + standalone: true, + imports: [ + MatTooltip, + NgIf, + TranslateModule, + NgForOf, + StopPropagationDirective, + InitialsAvatarComponent, + StatusBarComponent, + MatIcon, + RouterLink, + ], }) export class SearchItemTemplateComponent implements OnChanges { @Input() item: ISearchListItem; diff --git a/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts index 017076a13..75cb6d922 100644 --- a/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts +++ b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts @@ -1,11 +1,18 @@ import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core'; -import { ListingComponent, listingProvidersFactory, LoadingService, SearchPositions, TableColumnConfig } from '@iqser/common-ui'; +import { + IqserListingModule, + ListingComponent, + listingProvidersFactory, + LoadingService, + SearchPositions, + TableColumnConfig, +} from '@iqser/common-ui'; import { combineLatest, Observable, of } from 'rxjs'; import { debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { workflowFileStatusTranslations } from '@translations/file-status-translations'; -import { TranslateService } from '@ngx-translate/core'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { RouterHistoryService } from '@services/router-history.service'; import { Dossier, @@ -25,14 +32,18 @@ import { DossierTemplatesService } from '@services/dossier-templates/dossier-tem import { UserService } from '@users/user.service'; import { IFilterGroup, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering'; import { SortingOrders } from '@iqser/common-ui/lib/sorting'; +import { AsyncPipe, NgIf } from '@angular/common'; +import { SearchItemTemplateComponent } from '../search-item-template/search-item-template.component'; @Component({ templateUrl: './search-screen.component.html', styleUrls: ['./search-screen.component.scss'], providers: listingProvidersFactory(SearchScreenComponent), changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [IqserListingModule, TranslateModule, AsyncPipe, NgIf, SearchItemTemplateComponent], }) -export class SearchScreenComponent extends ListingComponent implements OnDestroy { +export default class SearchScreenComponent extends ListingComponent implements OnDestroy { readonly searchPositions = SearchPositions; readonly tableHeaderLabel = _('search-screen.table-header'); @@ -46,7 +57,7 @@ export class SearchScreenComponent extends ListingComponent imp readonly searchResults$ = new Observable(); - readonly dossierTemplates$ = this._dossierTemplateService.loadAll().pipe(tap(templates => this._addTemplateFilter(templates))); + readonly dossierTemplates$ = this._dossierTemplateService.loadAll().pipe(tap(templates => this.#addTemplateFilter(templates))); constructor( private readonly _router: Router, @@ -65,10 +76,10 @@ export class SearchScreenComponent extends ListingComponent imp if (!Object.keys(this._activatedRoute.snapshot.queryParams).length) { this._router.navigate(['main']).then(); } - this.searchResults$ = combineLatest([this._queryChanged$, this._filtersChanged$]).pipe( + this.searchResults$ = combineLatest([this.#queryChanged$, this.#filtersChanged$]).pipe( tap(() => this._loadingService.start()), tap(([query, [dossierIds, workflowStatus, assignee, dossierTemplateIds, onlyActive]]) => - this._updateNavigation(query, dossierIds, workflowStatus, assignee, dossierTemplateIds, onlyActive), + this.#updateNavigation(query, dossierIds, workflowStatus, assignee, dossierTemplateIds, onlyActive), ), switchMap(([query, [dossierIds, workflowStatus, assignee, dossierTemplateIds, onlyActive]]) => this._platformSearchService.search({ @@ -81,43 +92,43 @@ export class SearchScreenComponent extends ListingComponent imp includeArchivedDossiers: !onlyActive, }), ), - map(searchResult => this._toMatchedDocuments(searchResult)), - map(docs => this._toListItems(docs)), + map(searchResult => this.#toMatchedDocuments(searchResult)), + map(docs => this.#toListItems(docs)), tap(result => this.entitiesService.setEntities(result)), tap(() => this._loadingService.stop()), ); this.searchService.skip = true; this.sortingService.setSortingOption({ column: 'searchKey', order: SortingOrders.desc }); - this._initFilters(); + this.#initFilters(); } - private get _routeDossierIds(): string[] { + get #routeDossierIds(): string[] { return this._activatedRoute.snapshot.queryParamMap.get('dossierIds').split(','); } - private get _routeDossierTemplateIds(): string[] { + get #routeDossierTemplateIds(): string[] { return this._activatedRoute.snapshot.queryParamMap.get('dossierTemplateIds')?.split(','); } - private get _routeStatus(): WorkflowFileStatus { + get #routeStatus(): WorkflowFileStatus { return this._activatedRoute.snapshot.queryParamMap.get('status') as WorkflowFileStatus; } - private get _routeAssignee(): string { + get #routeAssignee(): string { return this._activatedRoute.snapshot.queryParamMap.get('assignee'); } - private get _routeOnlyActive(): boolean { + get #routeOnlyActive(): boolean { return this._activatedRoute.snapshot.queryParamMap.get('onlyActive') === 'true'; } - private get _routeQuery(): string { + get #routeQuery(): string { return this._activatedRoute.snapshot.queryParamMap.get('query'); } - private get _queryChanged$(): Observable { + get #queryChanged$(): Observable { return this.searchService.valueChanges$.pipe( - startWith(this._routeQuery), + startWith(this.#routeQuery), tap(query => (this.searchService.searchValue = query)), debounceTime(300), ); @@ -127,7 +138,7 @@ export class SearchScreenComponent extends ListingComponent imp return this._featuresService.isEnabled(DOSSIERS_ARCHIVE); } - private get _filtersChanged$(): Observable<[string[], WorkflowFileStatus, string, string[], boolean]> { + get #filtersChanged$(): Observable<[string[], WorkflowFileStatus, string, string[], boolean]> { const onlyActiveDossiers$ = this.#enabledArchive ? this.filterService.getSingleFilter('onlyActiveDossiers').pipe(map(f => !!f.checked)) : of(true); @@ -141,11 +152,11 @@ export class SearchScreenComponent extends ListingComponent imp return [dossierIds, workflowStatus, assignee, dossierTemplateIds, onlyActive]; }), startWith<[string[], WorkflowFileStatus, string, string[], boolean]>([ - this._routeDossierIds, - this._routeStatus, - this._routeAssignee, - this._routeDossierTemplateIds, - this._routeOnlyActive, + this.#routeDossierIds, + this.#routeStatus, + this.#routeAssignee, + this.#routeDossierTemplateIds, + this.#routeOnlyActive, ]), ); } @@ -155,8 +166,8 @@ export class SearchScreenComponent extends ListingComponent imp this.searchService.searchValue = newQuery ?? ''; } - private _initFilters() { - const dossierIds = this._routeDossierIds; + #initFilters() { + const dossierIds = this.#routeDossierIds; const dossierToFilter = ({ dossierName, id }: Dossier) => { const checked = dossierIds.includes(id); return new NestedFilter({ id, label: dossierName, checked }); @@ -170,7 +181,7 @@ export class SearchScreenComponent extends ListingComponent imp checker: keyChecker('dossierId'), }; - const status = this._routeStatus; + const status = this.#routeStatus; const statusToFilter = (workflowStatus: WorkflowFileStatus) => { const checked = status === workflowStatus; return new NestedFilter({ @@ -188,7 +199,7 @@ export class SearchScreenComponent extends ListingComponent imp checker: keyChecker('status'), }; - const assignee = this._routeAssignee; + const assignee = this.#routeAssignee; const assigneeToFilter = (userId: string) => { const checked = assignee === userId; return new NestedFilter({ id: userId, label: this._userService.getName(userId), checked }); @@ -215,12 +226,12 @@ export class SearchScreenComponent extends ListingComponent imp this.filterService.addFilterGroups([dossierNameFilter, workflowStatusFilter, assigneeFilter]); const onlyActiveLabel = this._translateService.instant('search-screen.filters.only-active'); if (this.#enabledArchive) { - this.filterService.addSingleFilter({ id: 'onlyActiveDossiers', label: onlyActiveLabel, checked: this._routeOnlyActive }); + this.filterService.addSingleFilter({ id: 'onlyActiveDossiers', label: onlyActiveLabel, checked: this.#routeOnlyActive }); } } - private _addTemplateFilter(templates: DossierTemplate[]) { - const templatesIds = this._routeDossierTemplateIds; + #addTemplateFilter(templates: DossierTemplate[]) { + const templatesIds = this.#routeDossierTemplateIds; const templateToFilter = ({ name, id }: DossierTemplate) => { const checked = templatesIds?.includes(id); return new NestedFilter({ id, label: name, checked }); @@ -236,7 +247,7 @@ export class SearchScreenComponent extends ListingComponent imp this.filterService.addFilterGroups([templateNameFilter]); } - private _updateNavigation( + #updateNavigation( query?: string, dossierIds?: string[], workflowStatus?: WorkflowFileStatus, @@ -255,15 +266,15 @@ export class SearchScreenComponent extends ListingComponent imp return this._router.navigate([], { queryParams, replaceUrl: true }); } - private _toMatchedDocuments({ matchedDocuments }: ISearchResponse): IMatchedDocument[] { + #toMatchedDocuments({ matchedDocuments }: ISearchResponse): IMatchedDocument[] { return matchedDocuments.filter(doc => doc.score > 0 && doc.matchedTerms.length > 0); } - private _toListItems(matchedDocuments: IMatchedDocument[]): ISearchListItem[] { - return matchedDocuments.map(document => this._toListItem(document)).filter(value => value); + #toListItems(matchedDocuments: IMatchedDocument[]): ISearchListItem[] { + return matchedDocuments.map(document => this.#toListItem(document)).filter(value => value); } - private _toListItem({ + #toListItem({ dossierId, fileId, unmatchedTerms, diff --git a/apps/red-ui/src/app/modules/search/search.module.ts b/apps/red-ui/src/app/modules/search/search.module.ts deleted file mode 100644 index dd0e66b1a..000000000 --- a/apps/red-ui/src/app/modules/search/search.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { SearchScreenComponent } from './search-screen/search-screen.component'; -import { RouterModule } from '@angular/router'; -import { IqserListingModule, StopPropagationDirective } from '@iqser/common-ui'; -import { SharedModule } from '@shared/shared.module'; -import { TranslateModule } from '@ngx-translate/core'; -import { SearchItemTemplateComponent } from './search-item-template/search-item-template.component'; -import { InitialsAvatarComponent, IqserUsersModule } from '@iqser/common-ui/lib/users'; -import { StatusBarComponent } from '@iqser/common-ui/lib/shared'; - -const routes = [{ path: '', component: SearchScreenComponent }]; - -@NgModule({ - declarations: [SearchScreenComponent, SearchItemTemplateComponent], - imports: [ - CommonModule, - RouterModule.forChild(routes), - SharedModule, - IqserUsersModule, - TranslateModule, - IqserListingModule, - StatusBarComponent, - StopPropagationDirective, - InitialsAvatarComponent, - ], -}) -export class SearchModule {} diff --git a/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts b/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts index a40d314c3..76263b639 100644 --- a/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts +++ b/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts @@ -1,6 +1,13 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { ListingComponent, listingProvidersFactory, LoadingService, TableColumnConfig } from '@iqser/common-ui'; +import { + CircleButtonComponent, + IqserListingModule, + ListingComponent, + listingProvidersFactory, + LoadingService, + TableColumnConfig, +} from '@iqser/common-ui'; import { SortingOrders } from '@iqser/common-ui/lib/sorting'; import { TrashItem } from '@red/domain'; import { TrashService } from '@services/entity-services/trash.service'; @@ -8,6 +15,9 @@ import { RouterHistoryService } from '@services/router-history.service'; import { firstValueFrom, Observable } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { TrashDialogService } from '../services/trash-dialog.service'; +import { TranslateModule } from '@ngx-translate/core'; +import { AsyncPipe, NgIf } from '@angular/common'; +import { TrashTableItemComponent } from './trash-table-item/trash-table-item.component'; @Component({ templateUrl: './trash-screen.component.html', @@ -16,11 +26,13 @@ import { TrashDialogService } from '../services/trash-dialog.service'; entitiesService: TrashService, component: TrashScreenComponent, }), + standalone: true, + imports: [IqserListingModule, TranslateModule, CircleButtonComponent, NgIf, TrashTableItemComponent, AsyncPipe], }) export class TrashScreenComponent extends ListingComponent implements OnInit { readonly tableHeaderLabel = _('trash.table-header.title'); - readonly canRestoreSelected$ = this._canRestoreSelected$; - readonly canHardDeleteSelected$ = this._canHardDeleteSelected$; + readonly canRestoreSelected$ = this.#canRestoreSelected$; + readonly canHardDeleteSelected$ = this.#canHardDeleteSelected$; readonly tableColumnConfigs: TableColumnConfig[] = [ { label: _('trash.table-col-names.name'), sortByKey: 'name' }, { label: _('trash.table-col-names.owner'), class: 'user-column', sortByKey: 'ownerName' }, @@ -43,14 +55,14 @@ export class TrashScreenComponent extends ListingComponent implements }); } - private get _canRestoreSelected$(): Observable { + get #canRestoreSelected$(): Observable { return this.listingService.selectedEntities$.pipe( map(entities => entities.length && !entities.find(dossier => !(dossier.canRestore && dossier.hasRestoreRights))), distinctUntilChanged(), ); } - private get _canHardDeleteSelected$(): Observable { + get #canHardDeleteSelected$(): Observable { return this.listingService.selectedEntities$.pipe( map(entities => entities.length && !entities.find(dossier => !dossier.hasHardDeleteRights)), distinctUntilChanged(), diff --git a/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.ts b/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.ts index 6f8e263e9..0c0cd3c0e 100644 --- a/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.ts +++ b/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.ts @@ -2,15 +2,34 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Out import { Dossier, DossierStats, TrashFile, TrashItem, User } from '@red/domain'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { DossierStatsService } from '@services/dossiers/dossier-stats.service'; -import { PartialDossier } from '@shared/components/dossier-name-column/dossier-name-column.component'; +import { DossierNameColumnComponent, PartialDossier } from '@shared/components/dossier-name-column/dossier-name-column.component'; import { Observable } from 'rxjs'; -import { getCurrentUser } from '@common-ui/users'; +import { getCurrentUser, InitialsAvatarComponent } from '@common-ui/users'; +import { MatIcon } from '@angular/material/icon'; +import { FileNameColumnComponent } from '@shared/components/file-name-column/file-name-column.component'; +import { AsyncPipe, DatePipe, NgIf } from '@angular/common'; +import { CircleButtonComponent } from '@iqser/common-ui'; +import { RouterLink } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'redaction-trash-table-item [item]', templateUrl: './trash-table-item.component.html', styleUrls: ['./trash-table-item.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + MatIcon, + FileNameColumnComponent, + DossierNameColumnComponent, + NgIf, + InitialsAvatarComponent, + CircleButtonComponent, + AsyncPipe, + RouterLink, + DatePipe, + TranslateModule, + ], }) export class TrashTableItemComponent implements OnChanges { @Input() item: TrashItem; diff --git a/apps/red-ui/src/app/modules/trash/trash.module.ts b/apps/red-ui/src/app/modules/trash/trash.module.ts deleted file mode 100644 index fb8596de3..000000000 --- a/apps/red-ui/src/app/modules/trash/trash.module.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { RouterModule } from '@angular/router'; -import { TrashScreenComponent } from './trash-screen/trash-screen.component'; -import { CircleButtonComponent, IqserListingModule } from '@iqser/common-ui'; -import { TrashTableItemComponent } from './trash-screen/trash-table-item/trash-table-item.component'; -import { SharedModule } from '@shared/shared.module'; -import { TrashDialogService } from './services/trash-dialog.service'; -import { TranslateModule } from '@ngx-translate/core'; -import { InitialsAvatarComponent, IqserUsersModule } from '@iqser/common-ui/lib/users'; -import { FileNameColumnComponent } from '@shared/components/file-name-column/file-name-column.component'; -import { DossierNameColumnComponent } from '@shared/components/dossier-name-column/dossier-name-column.component'; - -const routes = [{ path: '', component: TrashScreenComponent }]; - -@NgModule({ - declarations: [TrashScreenComponent, TrashTableItemComponent], - imports: [ - CommonModule, - RouterModule.forChild(routes), - SharedModule, - IqserUsersModule, - TranslateModule, - IqserListingModule, - CircleButtonComponent, - FileNameColumnComponent, - InitialsAvatarComponent, - DossierNameColumnComponent, - ], - providers: [TrashDialogService], -}) -export class TrashModule {} diff --git a/apps/red-ui/src/app/modules/trash/trash.routes.ts b/apps/red-ui/src/app/modules/trash/trash.routes.ts new file mode 100644 index 000000000..543ca99ed --- /dev/null +++ b/apps/red-ui/src/app/modules/trash/trash.routes.ts @@ -0,0 +1,5 @@ +import { TrashScreenComponent } from './trash-screen/trash-screen.component'; +import { TrashDialogService } from './services/trash-dialog.service'; +import { IqserRoutes } from '@iqser/common-ui'; + +export default [{ path: '', component: TrashScreenComponent, providers: [TrashDialogService] }] satisfies IqserRoutes;