diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts
index 66864a0d5..57ca25876 100644
--- a/src-ui/src/app/app-routing.module.ts
+++ b/src-ui/src/app/app-routing.module.ts
@@ -14,6 +14,7 @@ import { DocumentListComponent } from './components/document-list/document-list.
import { DocumentAttributesComponent } from './components/manage/document-attributes/document-attributes.component'
import { MailComponent } from './components/manage/mail/mail.component'
import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component'
+import { ShareLinksComponent } from './components/manage/share-links/share-links.component'
import { WorkflowsComponent } from './components/manage/workflows/workflows.component'
import { NotFoundComponent } from './components/not-found/not-found.component'
import { DirtyDocGuard } from './guards/dirty-doc.guard'
@@ -310,6 +311,24 @@ export const routes: Routes = [
componentName: 'SavedViewsComponent',
},
},
+ {
+ path: 'share-links',
+ component: ShareLinksComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ requiredPermissionAny: [
+ {
+ action: PermissionAction.View,
+ type: PermissionType.ShareLink,
+ },
+ {
+ action: PermissionAction.View,
+ type: PermissionType.ShareLinkBundle,
+ },
+ ],
+ componentName: 'ShareLinksComponent',
+ },
+ },
],
},
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html
index 6faddbb8a..a0c10c94e 100644
--- a/src-ui/src/app/components/app-frame/app-frame.component.html
+++ b/src-ui/src/app/components/app-frame/app-frame.component.html
@@ -244,6 +244,15 @@
Saved Views
+ @if (canManageShareLinks) {
+
+
+ Share links
+
+
+ }
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts
index 59a2ba18b..aad2b1511 100644
--- a/src-ui/src/app/components/app-frame/app-frame.component.ts
+++ b/src-ui/src/app/components/app-frame/app-frame.component.ts
@@ -221,6 +221,19 @@ export class AppFrameComponent
return this.appTitleSetting() || environment.appTitle
}
+ get canManageShareLinks(): boolean {
+ return (
+ this.permissionsService.currentUserCan(
+ PermissionAction.View,
+ PermissionType.ShareLink
+ ) ||
+ this.permissionsService.currentUserCan(
+ PermissionAction.View,
+ PermissionType.ShareLinkBundle
+ )
+ )
+ }
+
get customAppTitle(): string {
return this.appTitleSetting()
}
diff --git a/src-ui/src/app/components/manage/share-links/share-links.component.html b/src-ui/src/app/components/manage/share-links/share-links.component.html
new file mode 100644
index 000000000..812138e5d
--- /dev/null
+++ b/src-ui/src/app/components/manage/share-links/share-links.component.html
@@ -0,0 +1,37 @@
+
+
+
+ @if (canViewDocumentLinks) {
+ -
+
+
+
+ Manage links to individual documents here.
+
+
+
+ }
+ @if (canViewBundles) {
+ -
+
+
+
+ Manage share link bundles here.
+
+
+
+ }
+
+
+
diff --git a/src-ui/src/app/components/manage/share-links/share-links.component.ts b/src-ui/src/app/components/manage/share-links/share-links.component.ts
new file mode 100644
index 000000000..4fb54f95b
--- /dev/null
+++ b/src-ui/src/app/components/manage/share-links/share-links.component.ts
@@ -0,0 +1,62 @@
+import { Component, inject, signal } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
+import {
+ PermissionAction,
+ PermissionsService,
+ PermissionType,
+} from 'src/app/services/permissions.service'
+import { PageHeaderComponent } from '../../common/page-header/page-header.component'
+
+export enum ShareLinksNavIDs {
+ DocumentLinks = 'documents',
+ Bundles = 'bundles',
+}
+
+@Component({
+ selector: 'pngx-share-links',
+ templateUrl: './share-links.component.html',
+ imports: [NgbNavModule, PageHeaderComponent],
+})
+export class ShareLinksComponent {
+ private readonly route = inject(ActivatedRoute)
+ private readonly router = inject(Router)
+ private readonly permissionsService = inject(PermissionsService)
+
+ readonly ShareLinksNavIDs = ShareLinksNavIDs
+ readonly activeNavID = signal(this.getInitialNavID())
+
+ get canViewDocumentLinks(): boolean {
+ return this.permissionsService.currentUserCan(
+ PermissionAction.View,
+ PermissionType.ShareLink
+ )
+ }
+
+ get canViewBundles(): boolean {
+ return this.permissionsService.currentUserCan(
+ PermissionAction.View,
+ PermissionType.ShareLinkBundle
+ )
+ }
+
+ selectTab(tab: ShareLinksNavIDs): void {
+ this.activeNavID.set(tab)
+ void this.router.navigate([], {
+ relativeTo: this.route,
+ queryParams: { type: tab },
+ queryParamsHandling: 'merge',
+ })
+ }
+
+ private getInitialNavID(): ShareLinksNavIDs {
+ const requestedTab = this.route.snapshot.queryParamMap.get('type')
+ if (requestedTab === ShareLinksNavIDs.Bundles && this.canViewBundles) {
+ return ShareLinksNavIDs.Bundles
+ }
+ if (this.canViewDocumentLinks) {
+ return ShareLinksNavIDs.DocumentLinks
+ }
+ return ShareLinksNavIDs.Bundles
+ }
+}