mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Add frontend owner filtering
Add owner to doc cards, table Frontend testing for owner filtering
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<div class="dropdown-menu date-dropdown shadow pt-0" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}">
|
||||
<div class="list-group list-group-flush">
|
||||
<button *ngFor="let rd of relativeDates" class="list-group-item small list-goup list-group-item-action d-flex p-2" role="menuitem" (click)="setRelativeDate(rd.date)">
|
||||
<div _ngcontent-hga-c166="" class="selected-icon me-1">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="relativeDate === rd.date" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
||||
</svg>
|
||||
|
@@ -0,0 +1,81 @@
|
||||
<div class="btn-group w-100" ngbDropdown role="group">
|
||||
<button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="isActive ? 'btn-primary' : 'btn-outline-primary'">
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
|
||||
</svg> {{title}}
|
||||
<app-clearable-badge [selected]="isActive" (cleared)="reset()"></app-clearable-badge><span class="visually-hidden">selected</span>
|
||||
</button>
|
||||
<div class="dropdown-menu permission-filter-dropdown shadow py-0 w-2" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}">
|
||||
<div class="list-group list-group-flush">
|
||||
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" (click)="setFilter(OwnerFilterType.NONE)" [disabled]="disabled">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="selectionModel.ownerFilter === OwnerFilterType.NONE" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="me-1">
|
||||
<small i18n>All</small>
|
||||
</div>
|
||||
</button>
|
||||
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" (click)="setFilter(OwnerFilterType.SELF)" [disabled]="disabled">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="selectionModel.ownerFilter === OwnerFilterType.SELF" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="me-1">
|
||||
<small i18n>My documents</small>
|
||||
</div>
|
||||
</button>
|
||||
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" (click)="setFilter(OwnerFilterType.NOT_SELF)" [disabled]="disabled">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="selectionModel.ownerFilter === OwnerFilterType.NOT_SELF" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="me-1">
|
||||
<small i18n>Shared with me</small>
|
||||
</div>
|
||||
</button>
|
||||
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" (click)="setFilter(OwnerFilterType.UNOWNED)" [disabled]="disabled">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="selectionModel.ownerFilter === OwnerFilterType.UNOWNED" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="me-1">
|
||||
<small i18n>Unowned</small>
|
||||
</div>
|
||||
</button>
|
||||
<button *appIfPermissions="{ action: PermissionAction.Add, type: PermissionType.User }" class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" [disabled]="disabled">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="selectionModel.ownerFilter === OwnerFilterType.OTHERS" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
|
||||
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="me-1 w-100">
|
||||
<ng-select
|
||||
name="user"
|
||||
class="user-select small"
|
||||
[(ngModel)]="selectionModel.includeUsers"
|
||||
[disabled]="disabled"
|
||||
[clearable]="false"
|
||||
[items]="users"
|
||||
bindLabel="username"
|
||||
multiple="true"
|
||||
bindValue="id"
|
||||
placeholder="Users"
|
||||
i18n-placeholder
|
||||
(change)="onUserSelect()">
|
||||
</ng-select>
|
||||
</div>
|
||||
</button>
|
||||
<div *ngIf="selectionModel.ownerFilter === OwnerFilterType.NONE || selectionModel.ownerFilter === OwnerFilterType.NOT_SELF" class="list-group-item list-group-item-action d-flex align-items-center p-2 ps-3 border-bottom-0 border-start-0 border-end-0">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" class="form-check-input" id="hideUnowned" [(ngModel)]="this.selectionModel.hideUnowned" (change)="onChange()" [disabled]="disabled">
|
||||
<label class="form-check-label" for="hideUnowned"><small i18n>Hide unowned</small></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,8 @@
|
||||
.user-select {
|
||||
min-width: 15rem;
|
||||
}
|
||||
|
||||
.selected-icon {
|
||||
min-width: 1em;
|
||||
min-height: 1em;
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||
import { first } from 'rxjs'
|
||||
import { PaperlessUser } from 'src/app/data/paperless-user'
|
||||
import {
|
||||
PermissionAction,
|
||||
PermissionType,
|
||||
PermissionsService,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
|
||||
export class PermissionsSelectionModel {
|
||||
ownerFilter: OwnerFilterType
|
||||
hideUnowned: boolean
|
||||
userID: number
|
||||
includeUsers: number[]
|
||||
excludeUsers: number[]
|
||||
|
||||
clear() {
|
||||
this.ownerFilter = OwnerFilterType.NONE
|
||||
this.userID = null
|
||||
this.hideUnowned = false
|
||||
this.includeUsers = []
|
||||
this.excludeUsers = []
|
||||
}
|
||||
}
|
||||
|
||||
export enum OwnerFilterType {
|
||||
NONE = 0,
|
||||
SELF = 1,
|
||||
NOT_SELF = 2,
|
||||
OTHERS = 3,
|
||||
UNOWNED = 4,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-permissions-filter-dropdown',
|
||||
templateUrl: './permissions-filter-dropdown.component.html',
|
||||
styleUrls: ['./permissions-filter-dropdown.component.scss'],
|
||||
})
|
||||
export class PermissionsFilterDropdownComponent {
|
||||
public PermissionAction = PermissionAction
|
||||
public PermissionType = PermissionType
|
||||
public OwnerFilterType = OwnerFilterType
|
||||
|
||||
@Input()
|
||||
title: string
|
||||
|
||||
@Input()
|
||||
disabled = false
|
||||
|
||||
@Input()
|
||||
selectionModel: PermissionsSelectionModel
|
||||
|
||||
@Output()
|
||||
ownerFilterSet = new EventEmitter<PermissionsSelectionModel>()
|
||||
|
||||
users: PaperlessUser[]
|
||||
|
||||
hideUnowned: boolean
|
||||
|
||||
get isActive(): boolean {
|
||||
return (
|
||||
this.selectionModel.ownerFilter !== OwnerFilterType.NONE ||
|
||||
this.selectionModel.hideUnowned
|
||||
)
|
||||
}
|
||||
|
||||
constructor(
|
||||
permissionsService: PermissionsService,
|
||||
userService: UserService,
|
||||
private settingsService: SettingsService
|
||||
) {
|
||||
if (
|
||||
permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.User
|
||||
)
|
||||
) {
|
||||
userService
|
||||
.listAll()
|
||||
.pipe(first())
|
||||
.subscribe({
|
||||
next: (result) => (this.users = result.results),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.selectionModel.clear()
|
||||
this.onChange()
|
||||
}
|
||||
|
||||
setFilter(type: OwnerFilterType) {
|
||||
this.selectionModel.ownerFilter = type
|
||||
if (this.selectionModel.ownerFilter === OwnerFilterType.SELF) {
|
||||
this.selectionModel.includeUsers = []
|
||||
this.selectionModel.excludeUsers = []
|
||||
this.selectionModel.userID = this.settingsService.currentUser.id
|
||||
this.selectionModel.hideUnowned = false
|
||||
} else if (this.selectionModel.ownerFilter === OwnerFilterType.NOT_SELF) {
|
||||
this.selectionModel.userID = null
|
||||
this.selectionModel.includeUsers = []
|
||||
this.selectionModel.excludeUsers = [this.settingsService.currentUser.id]
|
||||
this.selectionModel.hideUnowned = false
|
||||
} else if (this.selectionModel.ownerFilter === OwnerFilterType.NONE) {
|
||||
this.selectionModel.userID = null
|
||||
this.selectionModel.includeUsers = []
|
||||
this.selectionModel.excludeUsers = []
|
||||
this.selectionModel.hideUnowned = false
|
||||
} else if (this.selectionModel.ownerFilter === OwnerFilterType.UNOWNED) {
|
||||
this.selectionModel.userID = null
|
||||
this.selectionModel.includeUsers = []
|
||||
this.selectionModel.excludeUsers = []
|
||||
this.selectionModel.hideUnowned = false
|
||||
}
|
||||
this.onChange()
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.ownerFilterSet.emit(this.selectionModel)
|
||||
}
|
||||
|
||||
onUserSelect() {
|
||||
if (this.selectionModel.includeUsers?.length) {
|
||||
this.selectionModel.ownerFilter = OwnerFilterType.OTHERS
|
||||
} else {
|
||||
this.selectionModel.ownerFilter = OwnerFilterType.NONE
|
||||
}
|
||||
this.onChange()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user