mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-12-04 00:31:12 -06:00
Basic wiring of existing bundles
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{ title }}</h4>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
@if (loading) {
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||||
|
<span i18n>Loading bulk share links…</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (!loading && error) {
|
||||||
|
<div class="alert alert-danger mb-0" role="alert">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (!loading && !error) {
|
||||||
|
@if (bundles.length === 0) {
|
||||||
|
<p class="mb-0 text-muted fst-italic" i18n>No bulk share links currently exist.</p>
|
||||||
|
}
|
||||||
|
@if (bundles.length > 0) {
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm align-middle mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" i18n>Created</th>
|
||||||
|
<th scope="col" i18n>Status</th>
|
||||||
|
<th scope="col" i18n>Expires</th>
|
||||||
|
<th scope="col" i18n>Documents</th>
|
||||||
|
<th scope="col" i18n>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (bundle of bundles; track bundle.id) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ bundle.created | date: 'short' }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge text-bg-secondary text-uppercase">{{ bundle.status }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@if (bundle.expiration) {
|
||||||
|
{{ bundle.expiration | date: 'short' }}
|
||||||
|
}
|
||||||
|
@if (!bundle.expiration) {
|
||||||
|
<span i18n>Never</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>{{ bundle.document_count }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="btn-group btn-group-sm">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-primary"
|
||||||
|
(click)="copy(bundle)"
|
||||||
|
>
|
||||||
|
@if (copiedSlug === bundle.slug) {
|
||||||
|
<i-bs name="clipboard-check"></i-bs>
|
||||||
|
}
|
||||||
|
@if (copiedSlug !== bundle.slug) {
|
||||||
|
<i-bs name="clipboard"></i-bs>
|
||||||
|
}
|
||||||
|
<span class="visually-hidden" i18n>Copy link</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-danger"
|
||||||
|
(click)="delete(bundle)"
|
||||||
|
>
|
||||||
|
<i-bs name="trash"></i-bs>
|
||||||
|
<span class="visually-hidden" i18n>Delete link</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" (click)="close()" i18n>Close</button>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
describe('ShareBundleManageDialogComponent', () => {
|
||||||
|
it('is pending implementation', () => {
|
||||||
|
pending(
|
||||||
|
'ShareBundleManageDialogComponent tests will be implemented once the dialog logic is finalized.'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { Clipboard } from '@angular/cdk/clipboard'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
|
import { first } from 'rxjs'
|
||||||
|
import { ShareBundleSummary } from 'src/app/data/share-bundle'
|
||||||
|
import { ShareBundleService } from 'src/app/services/rest/share-bundle.service'
|
||||||
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { environment } from 'src/environments/environment'
|
||||||
|
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pngx-share-bundle-manage-dialog',
|
||||||
|
templateUrl: './share-bundle-manage-dialog.component.html',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, NgxBootstrapIconsModule],
|
||||||
|
})
|
||||||
|
export class ShareBundleManageDialogComponent
|
||||||
|
extends LoadingComponentWithPermissions
|
||||||
|
implements OnInit
|
||||||
|
{
|
||||||
|
private activeModal = inject(NgbActiveModal)
|
||||||
|
private shareBundleService = inject(ShareBundleService)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
private clipboard = inject(Clipboard)
|
||||||
|
|
||||||
|
title = $localize`Bulk Share Links`
|
||||||
|
|
||||||
|
bundles: ShareBundleSummary[] = []
|
||||||
|
error: string
|
||||||
|
copiedSlug: string
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.fetchBundles()
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchBundles(): void {
|
||||||
|
this.loading = true
|
||||||
|
this.error = null
|
||||||
|
this.shareBundleService
|
||||||
|
.listAllBundles()
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe({
|
||||||
|
next: (results) => {
|
||||||
|
this.bundles = results
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.loading = false
|
||||||
|
this.error = $localize`Failed to load bulk share links.`
|
||||||
|
this.toastService.showError(
|
||||||
|
$localize`Error retrieving bulk share links.`,
|
||||||
|
e
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getShareUrl(bundle: ShareBundleSummary): string {
|
||||||
|
const apiURL = new URL(environment.apiBaseUrl)
|
||||||
|
return `${apiURL.origin}${apiURL.pathname.replace(/\/api\/$/, '/share/')}${
|
||||||
|
bundle.slug
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(bundle: ShareBundleSummary): void {
|
||||||
|
const success = this.clipboard.copy(this.getShareUrl(bundle))
|
||||||
|
if (success) {
|
||||||
|
this.copiedSlug = bundle.slug
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copiedSlug = null
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(bundle: ShareBundleSummary): void {
|
||||||
|
this.shareBundleService.delete(bundle).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toastService.showInfo($localize`Bulk share link deleted.`)
|
||||||
|
this.fetchBundles()
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.toastService.showError(
|
||||||
|
$localize`Error deleting bulk share link.`,
|
||||||
|
e
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.activeModal.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
|
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
|
||||||
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
|
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
|
||||||
import { ShareBundleDialogComponent } from '../../common/share-bundle-dialog/share-bundle-dialog.component'
|
import { ShareBundleDialogComponent } from '../../common/share-bundle-dialog/share-bundle-dialog.component'
|
||||||
|
import { ShareBundleManageDialogComponent } from '../../common/share-bundle-manage-dialog/share-bundle-manage-dialog.component'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
import { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component'
|
import { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component'
|
||||||
|
|
||||||
@@ -955,9 +956,10 @@ export class BulkEditorComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
manageShareLinks() {
|
manageShareLinks() {
|
||||||
this.toastService.showInfo(
|
const modal = this.modalService.open(ShareBundleManageDialogComponent, {
|
||||||
$localize`Bulk share link management is coming soon.`
|
backdrop: 'static',
|
||||||
)
|
size: 'lg',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
emailSelected() {
|
emailSelected() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
|
import { map } from 'rxjs/operators'
|
||||||
import {
|
import {
|
||||||
ShareBundleCreatePayload,
|
ShareBundleCreatePayload,
|
||||||
ShareBundleSummary,
|
ShareBundleSummary,
|
||||||
@@ -22,12 +23,9 @@ export class ShareBundleService extends AbstractNameFilterService<ShareBundleSum
|
|||||||
return this.http.post<ShareBundleSummary>(this.getResourceUrl(), payload)
|
return this.http.post<ShareBundleSummary>(this.getResourceUrl(), payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
listBundlesForDocuments(
|
listAllBundles(): Observable<ShareBundleSummary[]> {
|
||||||
documentIds: number[]
|
return this.list(1, 1000, 'created', true).pipe(
|
||||||
): Observable<ShareBundleSummary[]> {
|
map((response) => response.results)
|
||||||
const params = { documents: documentIds.join(',') }
|
)
|
||||||
return this.http.get<ShareBundleSummary[]>(this.getResourceUrl(), {
|
|
||||||
params,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user