diff --git a/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.html b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.html
new file mode 100644
index 000000000..297d4d0b5
--- /dev/null
+++ b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.html
@@ -0,0 +1,86 @@
+
+
+
+ @if (loading) {
+
+
+
Loading bulk share links…
+
+ }
+ @if (!loading && error) {
+
+ {{ error }}
+
+ }
+ @if (!loading && !error) {
+ @if (bundles.length === 0) {
+
No bulk share links currently exist.
+ }
+ @if (bundles.length > 0) {
+
+
+
+
+ | Created |
+ Status |
+ Expires |
+ Documents |
+ Actions |
+
+
+
+ @for (bundle of bundles; track bundle.id) {
+
+ | {{ bundle.created | date: 'short' }} |
+
+ {{ bundle.status }}
+ |
+
+ @if (bundle.expiration) {
+ {{ bundle.expiration | date: 'short' }}
+ }
+ @if (!bundle.expiration) {
+ Never
+ }
+ |
+ {{ bundle.document_count }} |
+
+
+
+
+
+ |
+
+ }
+
+
+
+ }
+ }
+
+
+
diff --git a/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts
new file mode 100644
index 000000000..3f39da1b5
--- /dev/null
+++ b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts
@@ -0,0 +1,7 @@
+describe('ShareBundleManageDialogComponent', () => {
+ it('is pending implementation', () => {
+ pending(
+ 'ShareBundleManageDialogComponent tests will be implemented once the dialog logic is finalized.'
+ )
+ })
+})
diff --git a/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.ts b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.ts
new file mode 100644
index 000000000..e552ffeb6
--- /dev/null
+++ b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.ts
@@ -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()
+ }
+}
diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts
index 7748b7676..8f47ac020 100644
--- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts
+++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts
@@ -56,6 +56,7 @@ import {
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-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 { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component'
@@ -955,9 +956,10 @@ export class BulkEditorComponent
}
manageShareLinks() {
- this.toastService.showInfo(
- $localize`Bulk share link management is coming soon.`
- )
+ const modal = this.modalService.open(ShareBundleManageDialogComponent, {
+ backdrop: 'static',
+ size: 'lg',
+ })
}
emailSelected() {
diff --git a/src-ui/src/app/services/rest/share-bundle.service.ts b/src-ui/src/app/services/rest/share-bundle.service.ts
index 0b66da77f..7fcb9fc4e 100644
--- a/src-ui/src/app/services/rest/share-bundle.service.ts
+++ b/src-ui/src/app/services/rest/share-bundle.service.ts
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
+import { map } from 'rxjs/operators'
import {
ShareBundleCreatePayload,
ShareBundleSummary,
@@ -22,12 +23,9 @@ export class ShareBundleService extends AbstractNameFilterService