From e3c2b87c70558ecec54418a873dcd1bdf7b9a0bf Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 4 Nov 2025 09:33:19 -0800 Subject: [PATCH] Skeleton share bundle component --- .../share-bundle-dialog.component.html | 67 +++++++++++++ .../share-bundle-dialog.component.scss | 0 .../share-bundle-dialog.component.spec.ts | 7 ++ .../share-bundle-dialog.component.ts | 98 +++++++++++++++++++ .../share-links-dialog.component.ts | 13 ++- .../bulk-editor/bulk-editor.component.ts | 15 ++- src-ui/src/app/data/share-link.ts | 12 +++ 7 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html create mode 100644 src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.scss create mode 100644 src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts create mode 100644 src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.ts diff --git a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html new file mode 100644 index 000000000..4635f3bec --- /dev/null +++ b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html @@ -0,0 +1,67 @@ + + + diff --git a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.scss b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts new file mode 100644 index 000000000..baae428ec --- /dev/null +++ b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts @@ -0,0 +1,7 @@ +describe('ShareBundleDialogComponent', () => { + it('is pending implementation', () => { + pending( + 'ShareBundleDialogComponent tests will be implemented once the dialog logic is finalized.' + ) + }) +}) diff --git a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.ts b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.ts new file mode 100644 index 000000000..148e55041 --- /dev/null +++ b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.ts @@ -0,0 +1,98 @@ +import { CommonModule } from '@angular/common' +import { Component, Input, inject } from '@angular/core' +import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms' +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' +import { + FileVersion, + SHARE_LINK_EXPIRATION_OPTIONS, +} from 'src/app/data/share-link' + +@Component({ + selector: 'pngx-share-bundle-dialog', + templateUrl: './share-bundle-dialog.component.html', + standalone: true, + imports: [CommonModule, ReactiveFormsModule], +}) +export class ShareBundleDialogComponent { + private activeModal = inject(NgbActiveModal) + private formBuilder = inject(FormBuilder) + + private _documentIds: number[] = [] + private _documentsWithArchive = 0 + + selectionCount = 0 + documentPreview: number[] = [] + form: FormGroup = this.formBuilder.group({ + shareArchiveVersion: [true], + expirationDays: [7], + }) + + readonly expirationOptions = SHARE_LINK_EXPIRATION_OPTIONS + + @Input() + set documentIds(ids: number[]) { + this._documentIds = ids ?? [] + this.selectionCount = this._documentIds.length + this.documentPreview = this._documentIds.slice(0, 10) + this.syncArchiveOption() + } + + get documentIds(): number[] { + return this._documentIds + } + + @Input() + set documentsWithArchive(count: number) { + this._documentsWithArchive = count ?? 0 + this.syncArchiveOption() + } + + get documentsWithArchive(): number { + return this._documentsWithArchive + } + + get archiveOptionDisabled(): boolean { + return ( + this.selectionCount === 0 || + this._documentsWithArchive !== this.selectionCount + ) + } + + get missingArchiveCount(): number { + return Math.max(this.selectionCount - this._documentsWithArchive, 0) + } + + close() { + this.activeModal.close() + } + + submit() { + // Placeholder until the backend workflow is wired up. + this.activeModal.close({ + documentIds: this.documentIds, + options: { + fileVersion: this.form.value.shareArchiveVersion + ? FileVersion.Archive + : FileVersion.Original, + expirationDays: this.form.value.expirationDays, + }, + }) + } + + private syncArchiveOption() { + const control = this.form.get('shareArchiveVersion') + if (!control) return + + const canUseArchive = + this.selectionCount > 0 && + this._documentsWithArchive === this.selectionCount + + if (canUseArchive) { + control.enable({ emitEvent: false }) + control.patchValue(true, { emitEvent: false }) + } else { + control.disable({ emitEvent: false }) + control.patchValue(false, { emitEvent: false }) + } + } +} diff --git a/src-ui/src/app/components/common/share-links-dialog/share-links-dialog.component.ts b/src-ui/src/app/components/common/share-links-dialog/share-links-dialog.component.ts index ffe11808c..813dbd2cf 100644 --- a/src-ui/src/app/components/common/share-links-dialog/share-links-dialog.component.ts +++ b/src-ui/src/app/components/common/share-links-dialog/share-links-dialog.component.ts @@ -4,7 +4,11 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { first } from 'rxjs' -import { FileVersion, ShareLink } from 'src/app/data/share-link' +import { + FileVersion, + SHARE_LINK_EXPIRATION_OPTIONS, + ShareLink, +} from 'src/app/data/share-link' import { ShareLinkService } from 'src/app/services/rest/share-link.service' import { ToastService } from 'src/app/services/toast.service' import { environment } from 'src/environments/environment' @@ -21,12 +25,7 @@ export class ShareLinksDialogComponent implements OnInit { private toastService = inject(ToastService) private clipboard = inject(Clipboard) - EXPIRATION_OPTIONS = [ - { label: $localize`1 day`, value: 1 }, - { label: $localize`7 days`, value: 7 }, - { label: $localize`30 days`, value: 30 }, - { label: $localize`Never`, value: null }, - ] + EXPIRATION_OPTIONS = SHARE_LINK_EXPIRATION_OPTIONS @Input() title = $localize`Share Links` 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 f3b3f9b0e..0364ef006 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 @@ -54,6 +54,7 @@ import { } from '../../common/filterable-dropdown/filterable-dropdown.component' 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 { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' import { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component' @@ -909,9 +910,19 @@ export class BulkEditorComponent } shareSelected() { - this.toastService.showInfo( - $localize`Bulk share link creation is coming soon.` + const selectedDocuments = this.list.documents.filter((d) => + this.list.selected.has(d.id) ) + const documentsWithArchive = selectedDocuments.filter( + (doc) => !!doc.archived_file_name + ).length + + const modal = this.modalService.open(ShareBundleDialogComponent, { + backdrop: 'static', + size: 'lg', + }) + modal.componentInstance.documentIds = Array.from(this.list.selected) + modal.componentInstance.documentsWithArchive = documentsWithArchive } manageShareLinks() { diff --git a/src-ui/src/app/data/share-link.ts b/src-ui/src/app/data/share-link.ts index debc8c111..d9710bd47 100644 --- a/src-ui/src/app/data/share-link.ts +++ b/src-ui/src/app/data/share-link.ts @@ -5,6 +5,18 @@ export enum FileVersion { Original = 'original', } +export interface ShareLinkExpirationOption { + label: string + value: number | null +} + +export const SHARE_LINK_EXPIRATION_OPTIONS: ShareLinkExpirationOption[] = [ + { label: $localize`1 day`, value: 1 }, + { label: $localize`7 days`, value: 7 }, + { label: $localize`30 days`, value: 30 }, + { label: $localize`Never`, value: null }, +] + export interface ShareLink extends ObjectWithPermissions { created: string // Date