<pngx-page-header [title]="getTitle()"> <div ngbDropdown class="btn-group flex-fill"> <button class="btn btn-sm btn-outline-primary" id="dropdownSelect" ngbDropdownToggle> <i-bs name="text-indent-left"></i-bs> <div class="d-none d-sm-inline"> <ng-container i18n>Select</ng-container></div> </button> <div ngbDropdownMenu aria-labelledby="dropdownSelect" class="shadow"> <button ngbDropdownItem (click)="list.selectNone()" i18n>Select none</button> <button ngbDropdownItem (click)="list.selectPage()" i18n>Select page</button> <button ngbDropdownItem (click)="list.selectAll()" i18n>Select all</button> </div> </div> <div ngbDropdown class="btn-group flex-fill"> <button class="btn btn-sm btn-outline-primary" id="dropdownDisplayFields" ngbDropdownToggle> <i-bs name="card-heading"></i-bs> <div class="d-none d-sm-inline"> <ng-container i18n>Show</ng-container></div> </button> <div ngbDropdownMenu aria-labelledby="dropdownDisplayFields" class="shadow"> <div class="px-3"> @for (field of settingsService.allDisplayFields; track field.id) { <div class="form-check my-1"> <input class="form-check-input mt-1" type="checkbox" id="displayField{{field.id}}" [checked]="activeDisplayFields.includes(field.id)" (change)="toggleDisplayField(field.id)"> <label class="form-check-label" for="displayField{{field.id}}">{{field.name}}</label> </div> } </div> </div> </div> <div class="btn-group flex-fill" role="group"> <input type="radio" class="btn-check" [(ngModel)]="list.displayMode" value="table" id="displayModeDetails" name="displayModeDetails"> <label for="displayModeDetails" class="btn btn-outline-primary btn-sm"> <i-bs name="list-ul"></i-bs> </label> <input type="radio" class="btn-check" [(ngModel)]="list.displayMode" value="smallCards" id="displayModeSmall" name="displayModeSmall"> <label for="displayModeSmall" class="btn btn-outline-primary btn-sm"> <i-bs name="grid"></i-bs> </label> <input type="radio" class="btn-check" [(ngModel)]="list.displayMode" value="largeCards" id="displayModeLarge" name="displayModeLarge"> <label for="displayModeLarge" class="btn btn-outline-primary btn-sm"> <i-bs name="hdd-stack"></i-bs> </label> </div> <div ngbDropdown class="btn-group flex-fill"> <button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle> <i-bs name="arrow-down-up"></i-bs> <div class="d-none d-sm-inline"> <ng-container i18n>Sort</ng-container></div> </button> <div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="shadow dropdown-menu-right"> <div class="w-100 d-flex pb-2 mb-1 border-bottom"> <input type="radio" class="btn-check" [value]="false" [(ngModel)]="listSortReverse" id="listSortReverseFalse"> <label class="btn btn-outline-primary btn-sm mx-2 flex-fill" for="listSortReverseFalse"> <i-bs name="sort-alpha-down"></i-bs> </label> <input type="radio" class="btn-check" [value]="true" [(ngModel)]="listSortReverse" id="listSortReverseTrue"> <label class="btn btn-outline-primary btn-sm me-2 flex-fill" for="listSortReverseTrue"> <i-bs name="sort-alpha-up-alt"></i-bs> </label> </div> <div> @for (f of getSortFields(); track f) { <button ngbDropdownItem (click)="list.sortField = f.field" [class.active]="list.sortField === f.field">{{f.name}} </button> } </div> </div> </div> <div class="btn-group flex-fill" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }" ngbDropdown role="group"> <button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle> <i-bs class="me-1" name="window-stack"></i-bs> <div class="d-none d-sm-inline"> <ng-container i18n>Views</ng-container></div> @if (savedViewIsModified) { <div class="position-absolute top-0 start-100 p-2 translate-middle badge bg-secondary border border-light rounded-circle"> <span class="visually-hidden">selected</span> </div> } </button> <div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu> @if (!list.activeSavedViewId) { @for (view of savedViewService.allViews; track view) { <button ngbDropdownItem (click)="loadViewConfig(view.id)">{{view.name}}</button> } @if (savedViewService.allViews.length > 0) { <div class="dropdown-divider"></div> } } <div *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.SavedView }"> @if (list.activeSavedViewId) { <button ngbDropdownItem (click)="saveViewConfig()" [disabled]="!savedViewIsModified" i18n>Save "{{list.activeSavedViewTitle}}"</button> } </div> <button ngbDropdownItem (click)="saveViewConfigAs()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.SavedView }" i18n>Save as...</button> <a ngbDropdownItem routerLink="/savedviews" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }" i18n>All saved views</a> </div> </div> </pngx-page-header> <div class="row sticky-top py-3 mt-n2 mt-md-n3 bg-body"> <pngx-filter-editor [hidden]="isBulkEditing" [disabled]="isBulkEditing" [(filterRules)]="list.filterRules" [unmodifiedFilterRules]="unmodifiedFilterRules" [selectionData]="list.selectionData" #filterEditor></pngx-filter-editor> <pngx-bulk-editor [hidden]="!isBulkEditing" [disabled]="!isBulkEditing"></pngx-bulk-editor> </div> <ng-template #pagination> <div class="d-flex flex-wrap gap-3 justify-content-between align-items-center mb-3"> <div class="d-flex align-items-center"> @if (list.isReloading) { <div class="spinner-border spinner-border-sm me-2" role="status"></div> <ng-container i18n>Loading...</ng-container> } @if (list.selected.size > 0) { <span i18n>{list.collectionSize, plural, =1 {Selected {{list.selected.size}} of one document} other {Selected {{list.selected.size}} of {{list.collectionSize || 0}} documents}}</span> } @if (!list.isReloading) { @if (list.selected.size === 0) { <span i18n>{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span> } @if (isFiltered) { <span i18n>(filtered)</span> } } @if (!list.isReloading && isFiltered) { <button class="btn btn-link py-0" (click)="resetFilters()"> <i-bs width="1em" height="1em" name="x"></i-bs><small i18n>Reset filters</small> </button> } </div> @if (list.collectionSize) { <ngb-pagination [pageSize]="list.pageSize" [collectionSize]="list.collectionSize" [(page)]="list.currentPage" [maxSize]="5" [rotate]="true" aria-label="Default pagination" size="sm"></ngb-pagination> } </div> </ng-template> <div tourAnchor="tour.documents"> <ng-container *ngTemplateOutlet="pagination"></ng-container> </div> @if (list.error ) { <div class="alert alert-danger" role="alert"><ng-container i18n>Error while loading documents</ng-container>: {{list.error}}</div> } @else { @if (list.displayMode === DisplayMode.LARGE_CARDS) { <div> @for (d of list.documents; track d.id) { <pngx-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" (dblClickDocument)="openDocumentDetail(d)" [document]="d" [displayFields]="activeDisplayFields" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)" (clickStoragePath)="clickStoragePath($event)" (clickMoreLike)="clickMoreLike(d.id)"> </pngx-document-card-large> } </div> } @if (list.displayMode === DisplayMode.TABLE) { <div class="table-responsive"> <table class="table table-sm align-middle border shadow-sm"> <thead> <tr> <th></th> @if (activeDisplayFields.includes(DisplayField.ASN)) { <th class="cursor-pointer" pngxSortable="archive_serial_number" title="Sort by ASN" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>ASN</th> } @if (activeDisplayFields.includes(DisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { <th class="cursor-pointer" pngxSortable="correspondent__name" title="Sort by correspondent" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Correspondent</th> } @if (activeDisplayFields.includes(DisplayField.TITLE)) { <th class="cursor-pointer" pngxSortable="title" title="Sort by title" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" style="min-width: 150px;" i18n>Title</th> } @if (activeDisplayFields.includes(DisplayField.TAGS) && !activeDisplayFields.includes(DisplayField.TITLE)) { <th i18n>Tags</th> } @if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) { <th class="cursor-pointer" pngxSortable="owner" title="Sort by owner" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Owner</th> } @if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) { <th class="cursor-pointer" pngxSortable="num_notes" title="Sort by notes" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Notes</th> } @if (activeDisplayFields.includes(DisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { <th class="cursor-pointer" pngxSortable="document_type__name" title="Sort by document type" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Document type</th> } @if (activeDisplayFields.includes(DisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { <th class="cursor-pointer" pngxSortable="storage_path__name" title="Sort by storage path" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Storage path</th> } @if (activeDisplayFields.includes(DisplayField.CREATED)) { <th class="cursor-pointer" pngxSortable="created" title="Sort by created date" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Created</th> } @if (activeDisplayFields.includes(DisplayField.ADDED)) { <th class="cursor-pointer" pngxSortable="added" title="Sort by added date" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Added</th> } @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { <th class="cursor-pointer" pngxSortable="page_count" title="Sort by number of pages" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)" i18n>Pages</th> } @if (activeDisplayFields.includes(DisplayField.SHARED)) { <th i18n> Shared </th> } @for (field_id of activeDisplayCustomFields; track field_id) { <th class="cursor-pointer" pngxSortable="{{field_id}}" title="Sort by {{getDisplayCustomFieldTitle(field_id)}}" i18n-title [currentSortField]="list.sortField" [currentSortReverse]="list.sortReverse" (sort)="onSort($event)"> {{getDisplayCustomFieldTitle(field_id)}} </th> } </tr> </thead> <tbody> @for (d of list.documents; track d.id) { <tr (click)="toggleSelected(d, $event); $event.stopPropagation();" (dblclick)="openDocumentDetail(d)" [ngClass]="list.isSelected(d) ? 'table-row-selected' : ''"> <td> <div class="form-check"> <input type="checkbox" class="form-check-input" id="docCheck{{d.id}}" [checked]="list.isSelected(d)" (click)="toggleSelected(d, $event); $event.stopPropagation();"> <label class="form-check-label" for="docCheck{{d.id}}"></label> </div> </td> @if (activeDisplayFields.includes(DisplayField.ASN)) { <td class=""> {{d.archive_serial_number}} </td> } @if (activeDisplayFields.includes(DisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { <td class=""> @if (d.correspondent) { <a (click)="clickCorrespondent(d.correspondent);$event.stopPropagation()" title="Filter by correspondent" i18n-title>{{d.correspondent | correspondentName | async}}</a> } </td> } @if (activeDisplayFields.includes(DisplayField.TITLE) || activeDisplayFields.includes(DisplayField.TAGS)) { <td width="30%"> @if (activeDisplayFields.includes(DisplayField.TITLE)) { <div class="d-inline-block" (mouseleave)="popupPreview.close()"> <a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a> <pngx-preview-popup [document]="d" linkClasses="btn btn-sm btn-link text-secondary" linkTitle="Preview document" (click)="$event.stopPropagation()" i18n-linkTitle #popupPreview> <i-bs name="eye"></i-bs> </pngx-preview-popup> </div> } @if (activeDisplayFields.includes(DisplayField.TAGS)) { @for (tagID of d.tags; track tagID) { <pngx-tag [tagID]="tagID" class="ms-1 fs-6" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(tagID);$event.stopPropagation()"></pngx-tag> } } </td> } @if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) { <td> {{d.owner | username | async}} </td> } @if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) { <td class=""> @if (d.notes.length) { <a routerLink="/documents/{{d.id}}/notes" class="btn btn-sm p-0"> <span class="badge rounded-pill bg-light border text-primary"> <i-bs width="1.2em" height="1.2em" class="ms-1 me-1" name="chat-left-text"></i-bs> {{d.notes.length}}</span> </a> } </td> } @if (activeDisplayFields.includes(DisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { <td class=""> @if (d.document_type) { <a (click)="clickDocumentType(d.document_type);$event.stopPropagation()" title="Filter by document type" i18n-title>{{d.document_type | documentTypeName | async}}</a> } </td> } @if (activeDisplayFields.includes(DisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { <td class=""> @if (d.storage_path) { <a (click)="clickStoragePath(d.storage_path);$event.stopPropagation()" title="Filter by storage path" i18n-title>{{d.storage_path | storagePathName | async}}</a> } </td> } @if (activeDisplayFields.includes(DisplayField.CREATED)) { <td> {{d.created_date | customDate}} </td> } @if (activeDisplayFields.includes(DisplayField.ADDED)) { <td> {{d.added | customDate}} </td> } @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { <td> {{ d.page_count }} </td> } @if (activeDisplayFields.includes(DisplayField.SHARED)) { <td> @if (d.is_shared_by_requester) { <ng-container i18n>Yes</ng-container> } @else { <ng-container i18n>No</ng-container> } </td> } @for (field of activeDisplayCustomFields; track field) { <td class=""> <pngx-custom-field-display [document]="d" [fieldDisplayKey]="field"></pngx-custom-field-display> </td> } </tr> } </tbody> </table> </div> } @if (list.displayMode === DisplayMode.SMALL_CARDS) { <div class="row row-cols-paperless-cards"> @for (d of list.documents; track d.id) { <pngx-document-card-small class="p-0" [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" (dblClickDocument)="openDocumentDetail(d)" [document]="d" (clickTag)="clickTag($event)" [displayFields]="activeDisplayFields" (clickCorrespondent)="clickCorrespondent($event)" (clickStoragePath)="clickStoragePath($event)" (clickDocumentType)="clickDocumentType($event)"> </pngx-document-card-small> } </div> } @if (list.documents?.length > 15) { <div class="mt-3"> <ng-container *ngTemplateOutlet="pagination"></ng-container> </div> } }