@dignite/vault-extract 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +17 -0
  2. package/fesm2022/dignite-vault-extract-config.mjs +82 -0
  3. package/fesm2022/dignite-vault-extract-config.mjs.map +1 -0
  4. package/fesm2022/dignite-vault-extract-documents-cabinet-list.component-Ch0gpSCc.mjs +184 -0
  5. package/fesm2022/dignite-vault-extract-documents-cabinet-list.component-Ch0gpSCc.mjs.map +1 -0
  6. package/fesm2022/dignite-vault-extract-documents-content-type-DjCs-s4E.mjs +115 -0
  7. package/fesm2022/dignite-vault-extract-documents-content-type-DjCs-s4E.mjs.map +1 -0
  8. package/fesm2022/dignite-vault-extract-documents-document-detail.component-DHs42DWJ.mjs +1146 -0
  9. package/fesm2022/dignite-vault-extract-documents-document-detail.component-DHs42DWJ.mjs.map +1 -0
  10. package/fesm2022/dignite-vault-extract-documents-document-file-preview.component-CStXf8v9.mjs +72 -0
  11. package/fesm2022/dignite-vault-extract-documents-document-file-preview.component-CStXf8v9.mjs.map +1 -0
  12. package/fesm2022/dignite-vault-extract-documents-document-list.component-jThR5cct.mjs +642 -0
  13. package/fesm2022/dignite-vault-extract-documents-document-list.component-jThR5cct.mjs.map +1 -0
  14. package/fesm2022/dignite-vault-extract-documents-document-overview.component-BHUUUIVr.mjs +318 -0
  15. package/fesm2022/dignite-vault-extract-documents-document-overview.component-BHUUUIVr.mjs.map +1 -0
  16. package/fesm2022/dignite-vault-extract-documents-document-recycle-bin.component-dqeBrw22.mjs +178 -0
  17. package/fesm2022/dignite-vault-extract-documents-document-recycle-bin.component-dqeBrw22.mjs.map +1 -0
  18. package/fesm2022/dignite-vault-extract-documents-document-type-list.component-C8kXFJGb.mjs +464 -0
  19. package/fesm2022/dignite-vault-extract-documents-document-type-list.component-C8kXFJGb.mjs.map +1 -0
  20. package/fesm2022/dignite-vault-extract-documents-export-template-list.component-DlmZFFF1.mjs +361 -0
  21. package/fesm2022/dignite-vault-extract-documents-export-template-list.component-DlmZFFF1.mjs.map +1 -0
  22. package/fesm2022/dignite-vault-extract-documents-extensible-table-DkLXuoWo.mjs +53 -0
  23. package/fesm2022/dignite-vault-extract-documents-extensible-table-DkLXuoWo.mjs.map +1 -0
  24. package/fesm2022/dignite-vault-extract-documents-field-definition-list.component-ClmWkRun.mjs +530 -0
  25. package/fesm2022/dignite-vault-extract-documents-field-definition-list.component-ClmWkRun.mjs.map +1 -0
  26. package/fesm2022/dignite-vault-extract-documents-field-reextraction-modal.component-D7OOycv9.mjs +163 -0
  27. package/fesm2022/dignite-vault-extract-documents-field-reextraction-modal.component-D7OOycv9.mjs.map +1 -0
  28. package/fesm2022/dignite-vault-extract-documents-format-bytes-Cd3QwfQZ.mjs +19 -0
  29. package/fesm2022/dignite-vault-extract-documents-format-bytes-Cd3QwfQZ.mjs.map +1 -0
  30. package/fesm2022/dignite-vault-extract-documents-format-field-value-Xjb8lwzA.mjs +22 -0
  31. package/fesm2022/dignite-vault-extract-documents-format-field-value-Xjb8lwzA.mjs.map +1 -0
  32. package/fesm2022/dignite-vault-extract-documents.mjs +71 -0
  33. package/fesm2022/dignite-vault-extract-documents.mjs.map +1 -0
  34. package/fesm2022/dignite-vault-extract.mjs +522 -0
  35. package/fesm2022/dignite-vault-extract.mjs.map +1 -0
  36. package/package.json +38 -0
  37. package/types/dignite-vault-extract-config.d.ts +5 -0
  38. package/types/dignite-vault-extract-documents.d.ts +5 -0
  39. package/types/dignite-vault-extract.d.ts +521 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-vault-extract-documents-document-overview.component-BHUUUIVr.mjs","sources":["../../../packages/vault-extract/documents/src/lib/documents/upload-constraints.ts","../../../packages/vault-extract/documents/src/lib/documents/document-upload/document-upload.component.ts","../../../packages/vault-extract/documents/src/lib/documents/document-upload/document-upload.component.html","../../../packages/vault-extract/documents/src/lib/documents/document-overview/document-overview.component.ts","../../../packages/vault-extract/documents/src/lib/documents/document-overview/document-overview.component.html"],"sourcesContent":["// Client-side upload constraints — MUST mirror the backend DocumentConsts\n// (core/src/Dignite.Vault.Extract.Domain.Shared/Documents/DocumentConsts.cs:\n// MaxUploadFileBytes / AllowedUploadContentTypes / AllowedUploadExtensions, #221).\n// These are a UX nicety (instant feedback + file-picker filtering); the backend\n// is the authoritative fail-closed gate. If the backend defaults change, change\n// these to match — single source of truth on the Angular side lives here.\n\n/** Maximum upload size in bytes (20 MiB). Mirrors DocumentConsts.MaxUploadFileBytes. */\nexport const MAX_UPLOAD_FILE_BYTES = 20 * 1024 * 1024;\n\n/** Allowed MIME types. Mirrors DocumentConsts.AllowedUploadContentTypes. */\nexport const ALLOWED_UPLOAD_CONTENT_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'application/pdf',\n] as const;\n\n/** Allowed file extensions (lowercase, leading dot). Mirrors DocumentConsts.AllowedUploadExtensions. */\nexport const ALLOWED_UPLOAD_EXTENSIONS = [\n '.jpg',\n '.jpeg',\n '.png',\n '.gif',\n '.webp',\n '.pdf',\n] as const;\n\n/**\n * Value for a file input's `accept` attribute, derived from the whitelists so the\n * native picker filters to exactly the allowed set (no `image/*` over-reach).\n */\nexport const UPLOAD_ACCEPT_ATTRIBUTE = [\n ...ALLOWED_UPLOAD_CONTENT_TYPES,\n ...ALLOWED_UPLOAD_EXTENSIONS,\n].join(',');\n\n/** True when both the MIME type and the extension are in their respective whitelists (mirrors the backend dual check). */\nexport function isAllowedUpload(file: File): boolean {\n const typeOk = (ALLOWED_UPLOAD_CONTENT_TYPES as readonly string[]).includes(file.type);\n const dot = file.name.lastIndexOf('.');\n const ext = dot >= 0 ? file.name.slice(dot).toLowerCase() : '';\n const extOk = (ALLOWED_UPLOAD_EXTENSIONS as readonly string[]).includes(ext);\n return typeOk && extOk;\n}\n","import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, Injector, OnInit, afterNextRender, computed, inject, signal, viewChild } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { RouterModule } from '@angular/router';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { LocalizationPipe, PermissionService } from '@abp/ng.core';\nimport { ToasterService } from '@abp/ng.theme.shared';\nimport { CabinetDto, CabinetService, DocumentUploadService, EXTRACT_PERMISSIONS } from '@dignite/vault-extract';\nimport { from, of } from 'rxjs';\nimport { catchError, map, mergeMap } from 'rxjs/operators';\nimport {\n MAX_UPLOAD_FILE_BYTES,\n UPLOAD_ACCEPT_ATTRIBUTE,\n isAllowedUpload,\n} from '../upload-constraints';\n\n// Limits the number of concurrent /api/documents/upload requests to avoid\n// exhausting the browser's per-origin connection pool and overloading the\n// server when the user drops dozens of files at once.\nconst MAX_CONCURRENT_UPLOADS = 3;\n\ninterface FileUploadState {\n key: string;\n name: string;\n done: boolean;\n error: boolean;\n documentId?: string;\n errorMessage?: string;\n}\n\n@Component({\n selector: 'lib-document-upload',\n templateUrl: './document-upload.component.html',\n styleUrls: ['./document-upload.component.scss'],\n imports: [CommonModule, FormsModule, LocalizationPipe, RouterModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DocumentUploadComponent implements OnInit {\n private readonly documentUploadService = inject(DocumentUploadService);\n private readonly cabinetService = inject(CabinetService);\n private readonly toaster = inject(ToasterService);\n private readonly permissionService = inject(PermissionService);\n private readonly destroyRef = inject(DestroyRef);\n private readonly injector = inject(Injector);\n\n // Primary file-picker trigger; used to restore focus after the result queue is\n // cleared, because the button the user just clicked is removed from the DOM.\n private readonly browseButton = viewChild<ElementRef<HTMLButtonElement>>('browseButton');\n\n // Cabinet selection requires Cabinets.Default permission because backend getList is [Authorize].\n // Without permission, hide the dropdown and upload as unclassified.\n readonly canViewCabinets = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Cabinets.Default,\n );\n cabinets = signal<CabinetDto[]>([]);\n selectedCabinetId = signal<string>('');\n\n // Picker `accept` filter, derived from the shared whitelist (mirrors backend, #221).\n readonly acceptAttribute = UPLOAD_ACCEPT_ATTRIBUTE;\n\n isDragOver = signal(false);\n isUploading = signal(false);\n uploadingFiles = signal<FileUploadState[]>([]);\n\n readonly hasUploadResults = computed(\n () =>\n this.uploadingFiles().length > 0 &&\n !this.isUploading() &&\n this.uploadingFiles().every(file => file.done || file.error),\n );\n readonly hasUploadErrors = computed(\n () => this.hasUploadResults() && this.uploadingFiles().some(file => file.error),\n );\n readonly successfulUploadCount = computed(\n () => this.uploadingFiles().filter(file => file.done).length,\n );\n readonly canAcceptFiles = computed(() => !this.isUploading() && !this.hasUploadResults());\n\n ngOnInit(): void {\n if (this.canViewCabinets) {\n this.cabinetService.getList()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: list => this.cabinets.set(list),\n error: () => this.cabinets.set([]),\n });\n }\n }\n\n onDragOver(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n if (!this.canAcceptFiles()) return;\n\n this.isDragOver.set(true);\n }\n\n onDragLeave(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.isDragOver.set(false);\n }\n\n onDrop(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.isDragOver.set(false);\n if (!this.canAcceptFiles()) return;\n\n const files = event.dataTransfer?.files;\n if (files && files.length > 0) {\n this.uploadFiles(Array.from(files));\n }\n }\n\n onFileSelected(event: Event): void {\n const input = event.target as HTMLInputElement;\n if (input.files && input.files.length > 0) {\n this.uploadFiles(Array.from(input.files));\n input.value = '';\n }\n }\n\n openFilePicker(input: HTMLInputElement): void {\n if (!this.canAcceptFiles()) return;\n\n input.click();\n }\n\n resetQueue(): void {\n this.uploadingFiles.set([]);\n this.isDragOver.set(false);\n // Restore focus to the primary trigger once the empty state re-renders;\n // the button the user just clicked is removed when the queue is cleared.\n afterNextRender(() => this.browseButton()?.nativeElement.focus(), {\n injector: this.injector,\n });\n }\n\n private uploadFiles(files: File[]): void {\n if (!this.canAcceptFiles()) return;\n\n // Mirror the backend fail-closed gate (#221): MIME + extension whitelist, then size.\n const valid = files.filter(f => {\n if (!isAllowedUpload(f)) {\n this.toaster.error('::Document:UnsupportedFileType', '::Error');\n return false;\n }\n if (f.size > MAX_UPLOAD_FILE_BYTES) {\n this.toaster.error('::Document:FileTooLarge', '::Error');\n return false;\n }\n return true;\n });\n\n if (valid.length === 0) return;\n\n this.isUploading.set(true);\n this.uploadingFiles.set(\n valid.map((file, index) => ({\n key: `${file.name}-${file.lastModified}-${file.size}-${index}`,\n name: file.name,\n done: false,\n error: false,\n })),\n );\n\n const indexed = valid.map((file, idx) => ({ file, idx }));\n from(indexed)\n .pipe(\n mergeMap(\n ({ file, idx }) =>\n this.documentUploadService.upload(file, this.selectedCabinetId() || undefined).pipe(\n map(document => ({\n idx,\n success: true,\n documentId: document.id,\n errorMessage: undefined as string | undefined,\n })),\n catchError(err => {\n const errorMessage: string | undefined = err?.error?.error?.message;\n return of({ idx, success: false, documentId: undefined, errorMessage });\n }),\n ),\n MAX_CONCURRENT_UPLOADS,\n ),\n takeUntilDestroyed(this.destroyRef),\n )\n .subscribe({\n next: ({ idx, success, documentId, errorMessage }) => {\n this.uploadingFiles.update(list =>\n list.map((item, j) =>\n j === idx\n ? {\n ...item,\n done: success,\n error: !success,\n documentId: success ? documentId : undefined,\n errorMessage,\n }\n : item,\n ),\n );\n },\n complete: () => {\n this.isUploading.set(false);\n const hasError = this.uploadingFiles().some(f => f.error);\n if (!hasError) {\n this.toaster.success('::Document:UploadedSuccessfully', '::Success');\n }\n },\n });\n }\n}\n","<div class=\"card document-upload-card shadow-sm\">\n <div class=\"card-body p-4 p-lg-5\">\n <div class=\"d-flex flex-wrap align-items-start justify-content-between gap-3 mb-4\">\n <div>\n <h2 class=\"h4 mb-2\">{{ '::Document:Home:UploadTitle' | abpLocalization }}</h2>\n <p class=\"text-muted mb-0\">{{ '::Document:Home:UploadSubtitle' | abpLocalization }}</p>\n </div>\n\n @if (canViewCabinets && cabinets().length > 0 && canAcceptFiles()) {\n <div class=\"cabinet-select\">\n <label class=\"form-label fw-semibold\">{{ '::Document:SelectCabinet' | abpLocalization }}</label>\n <select\n class=\"form-select\"\n [ngModel]=\"selectedCabinetId()\"\n (ngModelChange)=\"selectedCabinetId.set($event)\"\n >\n <option value=\"\">{{ '::Document:Unfiled' | abpLocalization }}</option>\n @for (c of cabinets(); track c.id) {\n <option [value]=\"c.id\">{{ c.name }}</option>\n }\n </select>\n </div>\n }\n </div>\n\n <input\n #filePicker\n type=\"file\"\n [attr.accept]=\"acceptAttribute\"\n multiple\n class=\"d-none\"\n (change)=\"onFileSelected($event)\"\n />\n <input\n #cameraPicker\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"d-none\"\n (change)=\"onFileSelected($event)\"\n />\n\n <div\n class=\"drop-zone\"\n [class.drag-over]=\"isDragOver()\"\n [class.uploading]=\"isUploading()\"\n [class.has-results]=\"hasUploadResults()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n >\n @if (isUploading() || hasUploadResults()) {\n <div class=\"upload-results w-100\">\n <div class=\"d-flex flex-wrap align-items-center justify-content-between gap-2 mb-3\">\n <div>\n <p class=\"fw-semibold mb-1\">\n @if (isUploading()) {\n {{ '::Document:Uploading' | abpLocalization }}\n } @else if (hasUploadErrors()) {\n {{ '::Document:Upload:CompletedWithErrors' | abpLocalization }}\n } @else {\n {{ '::Document:Upload:Complete' | abpLocalization }}\n }\n </p>\n <small class=\"text-muted\">\n {{ successfulUploadCount() }} / {{ uploadingFiles().length }}\n {{ '::Document:Upload:Succeeded' | abpLocalization }}\n </small>\n </div>\n </div>\n\n <ul class=\"list-group upload-queue\">\n @for (f of uploadingFiles(); track f.key) {\n <li class=\"list-group-item py-3\">\n <div class=\"d-flex align-items-start gap-3\">\n @if (f.done) {\n <span class=\"status-icon text-bg-success\"><i class=\"fas fa-check\"></i></span>\n } @else if (f.error) {\n <span class=\"status-icon text-bg-danger\"><i class=\"fas fa-times\"></i></span>\n } @else {\n <span class=\"spinner-border spinner-border-sm text-primary mt-1\" role=\"status\"></span>\n }\n\n <div class=\"min-width-0 flex-grow-1\">\n <div class=\"fw-semibold text-truncate\">{{ f.name }}</div>\n @if (f.errorMessage) {\n <small class=\"text-danger d-block mt-1\">{{ f.errorMessage }}</small>\n } @else if (f.error) {\n <small class=\"text-danger\">{{ '::Document:UploadFailed' | abpLocalization }}</small>\n } @else if (f.done) {\n <small class=\"text-muted\">{{ '::Document:UploadedSuccessfully' | abpLocalization }}</small>\n } @else {\n <small class=\"text-muted\">{{ '::Document:Uploading' | abpLocalization }}</small>\n }\n </div>\n\n @if (hasUploadResults() && f.done && f.documentId) {\n <a\n class=\"btn btn-sm btn-outline-primary flex-shrink-0\"\n [routerLink]=\"['/documents', f.documentId]\"\n >\n <i class=\"fas fa-external-link-alt me-1\"></i>\n {{ '::Document:OpenDocument' | abpLocalization }}\n </a>\n }\n </div>\n </li>\n }\n </ul>\n\n @if (hasUploadResults()) {\n <div class=\"d-flex flex-wrap justify-content-end gap-2 mt-3\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"resetQueue()\">\n <i class=\"fas fa-plus me-1\"></i>\n {{ '::Document:ContinueUploading' | abpLocalization }}\n </button>\n <a class=\"btn btn-primary\" routerLink=\"/documents/list\">\n <i class=\"fas fa-list me-1\"></i>\n {{ '::Document:ContinueToDocuments' | abpLocalization }}\n </a>\n </div>\n }\n </div>\n } @else {\n <div class=\"text-center py-5\">\n <span class=\"upload-icon mb-3\">\n <i class=\"fas fa-cloud-upload-alt\"></i>\n </span>\n <h3 class=\"h5 mb-2\">{{ '::Document:DropOrClick' | abpLocalization }}</h3>\n <p class=\"text-muted mb-4\">{{ '::Document:SupportedFormats' | abpLocalization }}</p>\n\n <div class=\"d-flex flex-wrap justify-content-center gap-2\">\n <button #browseButton type=\"button\" class=\"btn btn-primary\" (click)=\"openFilePicker(filePicker)\">\n <i class=\"fas fa-folder-open me-1\"></i>\n {{ '::Document:BrowseFiles' | abpLocalization }}\n </button>\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"openFilePicker(cameraPicker)\">\n <i class=\"fas fa-camera me-1\"></i>\n {{ '::Document:TakePhoto' | abpLocalization }}\n </button>\n </div>\n </div>\n }\n </div>\n\n <div class=\"mt-3 text-center\">\n <small class=\"text-muted\">\n <i class=\"fas fa-info-circle me-1\"></i>\n {{ '::Document:MaxFileSize' | abpLocalization }}\n </small>\n </div>\n </div>\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n OnInit,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CommonModule } from '@angular/common';\nimport { RouterModule } from '@angular/router';\nimport { LocalizationPipe, PermissionService } from '@abp/ng.core';\nimport {\n CabinetDto,\n CabinetService,\n DocumentStatisticsDto,\n DocumentStatisticsService,\n DocumentTypeDto,\n DocumentTypeService,\n EXTRACT_PERMISSIONS,\n} from '@dignite/vault-extract';\nimport { EMPTY, Subject } from 'rxjs';\nimport { catchError, switchMap, tap } from 'rxjs/operators';\nimport { DocumentUploadComponent } from '../document-upload/document-upload.component';\nimport { formatBytes } from '../../shared/format-bytes';\n\n@Component({\n selector: 'lib-document-overview',\n templateUrl: './document-overview.component.html',\n styleUrls: ['./document-overview.component.scss'],\n imports: [CommonModule, RouterModule, LocalizationPipe, DocumentUploadComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DocumentOverviewComponent implements OnInit {\n private readonly permissionService = inject(PermissionService);\n private readonly statisticsService = inject(DocumentStatisticsService);\n private readonly cabinetService = inject(CabinetService);\n private readonly documentTypeService = inject(DocumentTypeService);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly canUpload = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Documents.Upload,\n );\n readonly canReview = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Documents.ConfirmClassification,\n );\n readonly canViewCabinets = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Cabinets.Default,\n );\n readonly canCreateCabinet = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Cabinets.Create,\n );\n readonly canManageTypes = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.DocumentTypes.Default,\n );\n readonly canCreateType = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.DocumentTypes.Create,\n );\n\n // Filtered-entry navigation (#335): cabinets and visible document types as quick links into the\n // document list. No per-entity counts — navigation, not a dashboard. Cabinet list is gated by\n // Cabinets.Default; document types are visible to any Documents.Default operator (GetVisible is\n // decoupled from DocumentTypes.Default, #223).\n readonly cabinets = signal<CabinetDto[]>([]);\n readonly documentTypes = signal<DocumentTypeDto[]>([]);\n // Loading starts true only when a fetch will actually run, so the empty state never flashes first.\n readonly cabinetsLoading = signal(this.canViewCabinets);\n readonly typesLoading = signal(true);\n\n // Show a section while it is still loading (avoids a layout pop), when it has items, or when the\n // user can create the first one (actionable empty state). A plain viewer with neither items nor\n // create rights sees nothing instead of a dead \"empty\" box.\n readonly showCabinetSection = computed(\n () =>\n this.canViewCabinets &&\n (this.cabinetsLoading() || this.cabinets().length > 0 || this.canCreateCabinet),\n );\n readonly showTypeSection = computed(\n () => this.typesLoading() || this.documentTypes().length > 0 || this.canCreateType,\n );\n\n readonly stats = signal<DocumentStatisticsDto | null>(null);\n readonly statsLoading = signal(true);\n readonly statsError = signal(false);\n\n // The loading skeleton must render the same number of tiles the data grid will, otherwise non-reviewers\n // (who don't see the needs-review tile) get a 6 -> 5 layout jump when stats resolve.\n readonly skeletonSlots = this.canReview ? [0, 1, 2, 3, 4, 5] : [0, 1, 2, 3, 4];\n\n // In-flight = stored-but-not-started (Uploaded) + actively processing. Composing the display bucket here\n // keeps the API contract a faithful per-status projection (#333 decision: granularity in the DTO, grouping in the UI).\n readonly processingCount = computed(() => {\n const s = this.stats();\n return (s?.uploadedCount ?? 0) + (s?.processingCount ?? 0);\n });\n\n readonly isEmpty = computed(() => (this.stats()?.totalCount ?? 0) === 0);\n\n // Exposed so the template can format the storage tile.\n readonly formatBytes = formatBytes;\n\n // A trigger drives loads through switchMap so a slower earlier request can never overwrite a newer one\n // (e.g. rapid Refresh clicks): each emission cancels the previous in-flight GET. catchError keeps the\n // stream alive across failures so later retries still work.\n private readonly reload$ = new Subject<void>();\n\n constructor() {\n this.reload$\n .pipe(\n tap(() => {\n this.statsLoading.set(true);\n this.statsError.set(false);\n }),\n switchMap(() =>\n this.statisticsService.get().pipe(\n catchError(() => {\n this.statsError.set(true);\n this.statsLoading.set(false);\n return EMPTY;\n }),\n ),\n ),\n takeUntilDestroyed(this.destroyRef),\n )\n .subscribe(stats => {\n this.stats.set(stats);\n this.statsLoading.set(false);\n });\n }\n\n ngOnInit(): void {\n this.loadStatistics();\n if (this.canViewCabinets) {\n this.loadCabinets();\n }\n this.loadDocumentTypes();\n }\n\n loadStatistics(): void {\n this.reload$.next();\n }\n\n private loadCabinets(): void {\n this.cabinetService\n .getList()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: list => {\n this.cabinets.set(list);\n this.cabinetsLoading.set(false);\n },\n error: () => {\n this.cabinets.set([]);\n this.cabinetsLoading.set(false);\n },\n });\n }\n\n private loadDocumentTypes(): void {\n this.documentTypeService\n .getVisible()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: list => {\n this.documentTypes.set(list);\n this.typesLoading.set(false);\n },\n error: () => {\n this.documentTypes.set([]);\n this.typesLoading.set(false);\n },\n });\n }\n}\n","<div class=\"document-overview container-fluid py-4\">\n <div class=\"document-overview-shell\">\n <div class=\"mb-4\">\n <h1 class=\"h3 mb-2\">{{ '::Menu:Documents' | abpLocalization }}</h1>\n <p class=\"text-muted mb-0\">{{ '::Document:Home:Subtitle' | abpLocalization }}</p>\n </div>\n\n <!-- Overview statistics (#333): loading / error / empty / data states. -->\n <section class=\"mb-4\">\n <h2 class=\"h5 mb-3\">{{ '::Document:Home:Statistics:Title' | abpLocalization }}</h2>\n\n @if (statsLoading()) {\n <div class=\"row g-3\">\n @for (slot of skeletonSlots; track slot) {\n <div class=\"col-6 col-md-4 col-xl-2\">\n <div class=\"card shadow-sm h-100 stat-card placeholder-glow\">\n <div class=\"card-body\">\n <span class=\"placeholder col-5 d-block mb-2\"></span>\n <span class=\"placeholder col-8\"></span>\n </div>\n </div>\n </div>\n }\n </div>\n } @else if (statsError()) {\n <div\n class=\"alert alert-warning d-flex flex-wrap align-items-center justify-content-between gap-2 mb-0\"\n role=\"alert\"\n >\n <span>\n <i class=\"fas fa-exclamation-triangle me-2\"></i>\n {{ '::Document:Home:Statistics:LoadFailed' | abpLocalization }}\n </span>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" (click)=\"loadStatistics()\">\n {{ '::Refresh' | abpLocalization }}\n </button>\n </div>\n } @else if (isEmpty()) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center text-muted py-4\">\n <i class=\"fas fa-chart-bar d-block mb-2 fs-4\"></i>\n {{ '::Document:Home:Statistics:Empty' | abpLocalization }}\n </div>\n </div>\n } @else {\n <div class=\"row g-3\">\n <div class=\"col-6 col-md-4 col-xl-2\">\n <div class=\"card shadow-sm h-100 stat-card\">\n <div class=\"card-body\">\n <div class=\"stat-value\">{{ stats()?.totalCount ?? 0 }}</div>\n <div class=\"stat-label\">{{ '::Document:Home:Statistics:Total' | abpLocalization }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"col-6 col-md-4 col-xl-2\">\n <div class=\"card shadow-sm h-100 stat-card\">\n <div class=\"card-body\">\n <div class=\"stat-value text-info\">{{ processingCount() }}</div>\n <div class=\"stat-label\">{{ '::Document:Home:Statistics:Processing' | abpLocalization }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"col-6 col-md-4 col-xl-2\">\n <div class=\"card shadow-sm h-100 stat-card\">\n <div class=\"card-body\">\n <div class=\"stat-value text-success\">{{ stats()?.readyCount ?? 0 }}</div>\n <div class=\"stat-label\">{{ '::Document:Home:Statistics:Ready' | abpLocalization }}</div>\n </div>\n </div>\n </div>\n\n @if (canReview) {\n <div class=\"col-6 col-md-4 col-xl-2\">\n <a\n class=\"card shadow-sm h-100 stat-card text-decoration-none text-reset\"\n routerLink=\"/documents/list\"\n [queryParams]=\"{ review: 1 }\"\n >\n <div class=\"card-body\">\n <div class=\"stat-value text-warning\">{{ stats()?.needsReviewCount ?? 0 }}</div>\n <div class=\"stat-label\">{{ '::Document:Home:Statistics:NeedsReview' | abpLocalization }}</div>\n </div>\n </a>\n </div>\n }\n\n <div class=\"col-6 col-md-4 col-xl-2\">\n <div class=\"card shadow-sm h-100 stat-card\">\n <div class=\"card-body\">\n <div class=\"stat-value\" [class.text-danger]=\"(stats()?.failedCount ?? 0) > 0\">\n {{ stats()?.failedCount ?? 0 }}\n </div>\n <div class=\"stat-label\">{{ '::Document:Home:Statistics:Failed' | abpLocalization }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"col-6 col-md-4 col-xl-2\">\n <div class=\"card shadow-sm h-100 stat-card\">\n <div class=\"card-body\">\n <div class=\"stat-value\">{{ formatBytes(stats()?.totalStorageBytes) }}</div>\n <div class=\"stat-label\">{{ '::Document:Home:Statistics:Storage' | abpLocalization }}</div>\n </div>\n </div>\n </div>\n </div>\n }\n </section>\n\n <div class=\"row g-4 align-items-stretch\">\n <div class=\"col-xl-8\">\n @if (canUpload) {\n <lib-document-upload />\n } @else {\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-body p-4 p-lg-5 d-flex flex-column justify-content-center text-center\">\n <span class=\"readonly-icon mx-auto mb-3\">\n <i class=\"fas fa-lock\"></i>\n </span>\n <h2 class=\"h4 mb-2\">{{ '::Document:Home:ReadOnlyTitle' | abpLocalization }}</h2>\n <p class=\"text-muted mb-4\">{{ '::Document:Home:ReadOnlyMessage' | abpLocalization }}</p>\n <div>\n <a class=\"btn btn-primary\" routerLink=\"/documents/list\">\n <i class=\"fas fa-list me-1\"></i>\n {{ '::Menu:DocumentList' | abpLocalization }}\n </a>\n </div>\n </div>\n </div>\n }\n </div>\n\n <div class=\"col-xl-4\">\n <div class=\"d-flex flex-column gap-3 h-100\">\n <div class=\"card shadow-sm\">\n <div class=\"card-body p-4\">\n <h2 class=\"h5 mb-3\">{{ '::Document:Home:NextActions' | abpLocalization }}</h2>\n\n <div class=\"d-grid gap-2\">\n <a class=\"home-action\" routerLink=\"/documents/list\">\n <span class=\"home-action-icon text-bg-primary\"><i class=\"fas fa-list\"></i></span>\n <span>\n <span class=\"fw-semibold d-block\">{{ '::Menu:DocumentList' | abpLocalization }}</span>\n <small class=\"text-muted\">{{ '::Document:Home:DocumentListHint' | abpLocalization }}</small>\n </span>\n </a>\n\n @if (canReview) {\n <a class=\"home-action\" routerLink=\"/documents/list\" [queryParams]=\"{ review: 1 }\">\n <span class=\"home-action-icon text-bg-warning\"><i class=\"fas fa-clipboard-check\"></i></span>\n <span>\n <span class=\"fw-semibold d-block\">{{ '::Document:NeedsReview' | abpLocalization }}</span>\n <small class=\"text-muted\">{{ '::Document:Home:ReviewQueueHint' | abpLocalization }}</small>\n </span>\n </a>\n }\n\n @if (canViewCabinets) {\n <a class=\"home-action\" routerLink=\"/documents/cabinets\">\n <span class=\"home-action-icon text-bg-secondary\"><i class=\"fas fa-folder\"></i></span>\n <span>\n <span class=\"fw-semibold d-block\">{{ '::Menu:Cabinets' | abpLocalization }}</span>\n <small class=\"text-muted\">{{ '::Document:Home:CabinetsHint' | abpLocalization }}</small>\n </span>\n </a>\n }\n </div>\n </div>\n </div>\n\n <div class=\"card shadow-sm flex-grow-1\">\n <div class=\"card-body p-4\">\n <h2 class=\"h5 mb-3\">{{ '::Document:Home:ProcessingTitle' | abpLocalization }}</h2>\n <ol class=\"processing-list mb-0\">\n <li>{{ '::Document:Home:ProcessingStep:Upload' | abpLocalization }}</li>\n <li>{{ '::Document:Home:ProcessingStep:Extract' | abpLocalization }}</li>\n <li>{{ '::Document:Home:ProcessingStep:Review' | abpLocalization }}</li>\n </ol>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Filtered-entry navigation (#335): jump into the document list by cabinet or by document type.\n Secondary to upload, so it sits below the main row. No per-entity counts. -->\n @if (showCabinetSection()) {\n <section class=\"mt-4\">\n <div class=\"d-flex align-items-center justify-content-between mb-3\">\n <h2 class=\"h5 mb-0\">{{ '::Document:Home:CabinetsSection' | abpLocalization }}</h2>\n @if (cabinets().length > 0) {\n <a class=\"small\" routerLink=\"/documents/cabinets\">\n {{ '::Document:Home:ManageCabinets' | abpLocalization }}\n </a>\n }\n </div>\n\n @if (cabinets().length > 0) {\n <div class=\"home-chip-grid\">\n @for (cabinet of cabinets(); track cabinet.id) {\n <a\n class=\"home-chip\"\n routerLink=\"/documents/list\"\n [queryParams]=\"{ cabinetId: cabinet.id }\"\n >\n <i class=\"fas fa-folder text-warning\"></i>\n <span class=\"text-truncate\">{{ cabinet.name }}</span>\n </a>\n }\n </div>\n } @else if (!cabinetsLoading()) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center text-muted py-4\">\n <i class=\"fas fa-folder-open d-block mb-2 fs-4\"></i>\n <div class=\"mb-2\">{{ '::Document:Home:CabinetsEmpty' | abpLocalization }}</div>\n @if (canCreateCabinet) {\n <a class=\"btn btn-sm btn-outline-primary\" routerLink=\"/documents/cabinets\">\n {{ '::Document:Home:CreateCabinet' | abpLocalization }}\n </a>\n }\n </div>\n </div>\n }\n </section>\n }\n\n @if (showTypeSection()) {\n <section class=\"mt-4\">\n <div class=\"d-flex align-items-center justify-content-between mb-3\">\n <h2 class=\"h5 mb-0\">{{ '::Document:Home:TypesSection' | abpLocalization }}</h2>\n @if (canManageTypes && documentTypes().length > 0) {\n <a class=\"small\" routerLink=\"/documents/types\">\n {{ '::Document:Home:ManageTypes' | abpLocalization }}\n </a>\n }\n </div>\n\n @if (documentTypes().length > 0) {\n <div class=\"home-chip-grid\">\n @for (type of documentTypes(); track type.id) {\n <a\n class=\"home-chip\"\n routerLink=\"/documents/list\"\n [queryParams]=\"{ documentTypeCode: type.typeCode }\"\n >\n <i class=\"fas fa-tags text-info\"></i>\n <span class=\"text-truncate\">{{ type.displayName }}</span>\n <code class=\"home-chip-code\">{{ type.typeCode }}</code>\n </a>\n }\n </div>\n } @else if (!typesLoading()) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center text-muted py-4\">\n <i class=\"fas fa-tags d-block mb-2 fs-4\"></i>\n <div class=\"mb-2\">{{ '::Document:Home:TypesEmpty' | abpLocalization }}</div>\n @if (canCreateType) {\n <a class=\"btn btn-sm btn-outline-primary\" routerLink=\"/documents/types\">\n {{ '::Document:Home:CreateType' | abpLocalization }}\n </a>\n }\n </div>\n </div>\n }\n </section>\n }\n </div>\n</div>\n"],"names":["i1"],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, DestroyRef, LOCALE_ID, signal, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { formatDate, CommonModule } from '@angular/common';
5
+ import { PermissionService, ListService, escapeHtmlChars, LocalizationPipe } from '@abp/ng.core';
6
+ import { ExtensionsService, EntityProp, ExtensibleTableComponent, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible';
7
+ import { ConfirmationService, ToasterService, Confirmation } from '@abp/ng.theme.shared';
8
+ import * as i2 from '@ng-bootstrap/ng-bootstrap';
9
+ import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
10
+ import { of } from 'rxjs';
11
+ import { DocumentService, EXTRACT_PERMISSIONS } from '@dignite/vault-extract';
12
+ import { c as configureEntityTable, E as EXTRACT_TABLES } from './dignite-vault-extract-documents-extensible-table-DkLXuoWo.mjs';
13
+ import { f as formatBytes } from './dignite-vault-extract-documents-format-bytes-Cd3QwfQZ.mjs';
14
+
15
+ class DocumentRecycleBinComponent {
16
+ constructor() {
17
+ this.documentService = inject(DocumentService);
18
+ this.confirmation = inject(ConfirmationService);
19
+ this.toaster = inject(ToasterService);
20
+ this.permissionService = inject(PermissionService);
21
+ this.destroyRef = inject(DestroyRef);
22
+ this.extensions = inject(ExtensionsService);
23
+ this.locale = inject(LOCALE_ID);
24
+ this.list = inject(ListService);
25
+ this.documents = signal({ totalCount: 0, items: [] }, ...(ngDevMode ? [{ debugName: "documents" }] : /* istanbul ignore next */ []));
26
+ this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
27
+ this.canRestore = this.permissionService.getGrantedPolicy(EXTRACT_PERMISSIONS.Documents.Restore);
28
+ this.canPermanentDelete = this.permissionService.getGrantedPolicy(EXTRACT_PERMISSIONS.Documents.PermanentDelete);
29
+ this.hasRecycleActions = this.canRestore || this.canPermanentDelete;
30
+ configureEntityTable(this.extensions, EXTRACT_TABLES.DocumentRecycleBin, [
31
+ EntityProp.create({
32
+ type: "string" /* ePropType.String */,
33
+ name: 'fileName',
34
+ displayName: '::Document:FileName',
35
+ sortable: false,
36
+ columnWidth: 340,
37
+ valueResolver: data => {
38
+ const doc = data.record;
39
+ const fileName = doc.title || doc.fileOrigin?.originalFileName || '-';
40
+ const iconClass = this.isImage(doc)
41
+ ? 'fas fa-file-image fa-lg text-muted'
42
+ : 'fas fa-file-pdf fa-lg text-muted';
43
+ return of(`<span class="recycle-file-cell"><i class="${iconClass} me-2"></i><span class="text-muted text-truncate">${escapeHtmlChars(fileName)}</span></span>`);
44
+ },
45
+ }),
46
+ EntityProp.create({
47
+ type: "string" /* ePropType.String */,
48
+ name: 'documentTypeCode',
49
+ displayName: '::Document:Type',
50
+ sortable: false,
51
+ columnWidth: 180,
52
+ valueResolver: data => {
53
+ const typeCode = data.record.documentTypeCode;
54
+ return of(typeCode
55
+ ? `<span class="badge bg-secondary">${escapeHtmlChars(typeCode)}</span>`
56
+ : '<span class="text-muted">-</span>');
57
+ },
58
+ }),
59
+ EntityProp.create({
60
+ type: "string" /* ePropType.String */,
61
+ name: 'fileSize',
62
+ displayName: '::Document:Size',
63
+ sortable: false,
64
+ columnWidth: 140,
65
+ valueResolver: data => of(`<span class="text-muted small">${escapeHtmlChars(formatBytes(data.record.fileOrigin?.fileSize))}</span>`),
66
+ }),
67
+ EntityProp.create({
68
+ type: "string" /* ePropType.String */,
69
+ name: 'deletionTime',
70
+ displayName: '::Document:DeletedAt',
71
+ sortable: false,
72
+ columnWidth: 180,
73
+ valueResolver: data => of(`<span class="text-muted small">${escapeHtmlChars(this.formatDateTime(data.record.deletionTime))}</span>`),
74
+ }),
75
+ ]);
76
+ }
77
+ ngOnInit() {
78
+ this.hookListQuery();
79
+ }
80
+ refresh() {
81
+ this.list.getWithoutPageReset();
82
+ }
83
+ hookListQuery() {
84
+ this.list.requestStatus$
85
+ .pipe(takeUntilDestroyed(this.destroyRef))
86
+ .subscribe(status => {
87
+ if (status === 'idle' && this.isLoading() && this.documents().items.length === 0)
88
+ return;
89
+ this.isLoading.set(status === 'loading');
90
+ });
91
+ this.list
92
+ .hookToQuery(query => this.documentService.getList({
93
+ isDeleted: true,
94
+ maxResultCount: query.maxResultCount,
95
+ skipCount: query.skipCount,
96
+ sorting: query.sorting || 'creationTime desc',
97
+ }))
98
+ .pipe(takeUntilDestroyed(this.destroyRef))
99
+ .subscribe(result => {
100
+ this.documents.set({
101
+ totalCount: result.totalCount ?? 0,
102
+ items: result.items ?? [],
103
+ });
104
+ });
105
+ }
106
+ restore(doc) {
107
+ this.confirmation
108
+ .warn('::Document:AreYouSureToRestore', '::AreYouSure')
109
+ .pipe(takeUntilDestroyed(this.destroyRef))
110
+ .subscribe(status => {
111
+ if (status !== Confirmation.Status.confirm)
112
+ return;
113
+ this.documentService.restore(doc.id)
114
+ .pipe(takeUntilDestroyed(this.destroyRef))
115
+ .subscribe({
116
+ next: () => {
117
+ this.toaster.success('::Document:RestoredSuccessfully', '::Success');
118
+ this.list.getWithoutPageReset();
119
+ },
120
+ error: () => this.toaster.error('::Document:RestoreFailed', '::Error'),
121
+ });
122
+ });
123
+ }
124
+ permanentDelete(doc) {
125
+ this.confirmation
126
+ .warn('::Document:AreYouSureToPermanentlyDelete', '::AreYouSure', {
127
+ yesText: '::Document:PermanentDelete',
128
+ })
129
+ .pipe(takeUntilDestroyed(this.destroyRef))
130
+ .subscribe(status => {
131
+ if (status !== Confirmation.Status.confirm)
132
+ return;
133
+ this.documentService.permanentDelete(doc.id)
134
+ .pipe(takeUntilDestroyed(this.destroyRef))
135
+ .subscribe({
136
+ next: () => {
137
+ this.toaster.success('::Document:PermanentlyDeletedSuccessfully', '::Success');
138
+ this.list.getWithoutPageReset();
139
+ },
140
+ error: () => this.toaster.error('::Document:PermanentDeleteFailed', '::Error'),
141
+ });
142
+ });
143
+ }
144
+ isImage(doc) {
145
+ return doc.fileOrigin?.contentType?.startsWith('image/') ?? false;
146
+ }
147
+ formatDateTime(value) {
148
+ if (!value)
149
+ return '-';
150
+ try {
151
+ return formatDate(value, 'yyyy-MM-dd HH:mm', this.locale);
152
+ }
153
+ catch {
154
+ return value;
155
+ }
156
+ }
157
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DocumentRecycleBinComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
158
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DocumentRecycleBinComponent, isStandalone: true, selector: "lib-document-recycle-bin", providers: [
159
+ ListService,
160
+ {
161
+ provide: EXTENSIONS_IDENTIFIER,
162
+ useValue: EXTRACT_TABLES.DocumentRecycleBin,
163
+ },
164
+ ], ngImport: i0, template: "<div class=\"container-fluid py-4\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h4 class=\"mb-0\">\n <i class=\"fas fa-trash-can me-2\"></i>\n {{ '::Document:RecycleBin' | abpLocalization }}\n </h4>\n <button\n class=\"btn btn-outline-secondary\"\n (click)=\"refresh()\"\n [disabled]=\"isLoading()\"\n title=\"{{ '::Refresh' | abpLocalization }}\"\n >\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"isLoading()\"></i>\n </button>\n </div>\n\n <!-- Loading spinner -->\n @if (isLoading() && documents().items.length === 0) {\n <div class=\"text-center py-5\">\n <div class=\"spinner-border text-primary\" role=\"status\"></div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!isLoading() && documents().totalCount === 0) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center py-5\">\n <i class=\"fas fa-trash-can fa-3x text-muted mb-3 d-block\"></i>\n <p class=\"text-muted mb-0\">{{ '::Document:RecycleBinEmpty' | abpLocalization }}</p>\n </div>\n </div>\n }\n\n <!-- List -->\n @if (documents().totalCount > 0) {\n <div class=\"card shadow-sm document-recycle-table\">\n <div class=\"card-body p-0\">\n <ng-template #actionsTemplate let-doc>\n @if (hasRecycleActions) {\n <div ngbDropdown container=\"body\" class=\"d-inline-block\">\n <button type=\"button\" class=\"btn btn-sm btn-primary\" ngbDropdownToggle>\n {{ 'AbpUi::Actions' | abpLocalization }}\n </button>\n <div ngbDropdownMenu>\n @if (canRestore) {\n <button type=\"button\" ngbDropdownItem (click)=\"restore(doc)\">\n <i class=\"fas fa-rotate-left me-2\"></i>\n {{ '::Document:Restore' | abpLocalization }}\n </button>\n }\n @if (canPermanentDelete) {\n <button type=\"button\" ngbDropdownItem (click)=\"permanentDelete(doc)\">\n <i class=\"fas fa-times me-2\"></i>\n {{ '::Document:PermanentDelete' | abpLocalization }}\n </button>\n }\n </div>\n </div>\n }\n </ng-template>\n\n @if (hasRecycleActions) {\n <abp-extensible-table\n [data]=\"documents().items\"\n [recordsTotal]=\"documents().totalCount\"\n [list]=\"list\"\n [actionsTemplate]=\"actionsTemplate\"\n actionsText=\"AbpUi::Actions\"\n [actionsColumnWidth]=\"150\"\n />\n } @else {\n <abp-extensible-table\n [data]=\"documents().items\"\n [recordsTotal]=\"documents().totalCount\"\n [list]=\"list\"\n />\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host ::ng-deep .recycle-file-cell{align-items:center;display:inline-flex;max-width:100%;min-width:0}:host ::ng-deep .recycle-file-cell .text-truncate{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ExtensibleTableComponent, selector: "abp-extensible-table", inputs: ["actionsText", "data", "list", "recordsTotal", "actionsColumnWidth", "actionsTemplate", "selectable", "selectionType", "selected", "infiniteScroll", "isLoading", "scrollThreshold", "tableHeight", "rowDetailTemplate", "rowDetailHeight"], outputs: ["tableActivate", "selectionChange", "loadMore", "rowDetailToggle"], exportAs: ["abpExtensibleTable"] }, { kind: "ngmodule", type: NgbDropdownModule }, { kind: "directive", type: i2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i2.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["tabindex", "disabled"] }, { kind: "directive", type: i2.NgbDropdownButtonItem, selector: "button[ngbDropdownItem]" }, { kind: "pipe", type: LocalizationPipe, name: "abpLocalization" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
165
+ }
166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DocumentRecycleBinComponent, decorators: [{
167
+ type: Component,
168
+ args: [{ selector: 'lib-document-recycle-bin', imports: [CommonModule, LocalizationPipe, ExtensibleTableComponent, NgbDropdownModule], providers: [
169
+ ListService,
170
+ {
171
+ provide: EXTENSIONS_IDENTIFIER,
172
+ useValue: EXTRACT_TABLES.DocumentRecycleBin,
173
+ },
174
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container-fluid py-4\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h4 class=\"mb-0\">\n <i class=\"fas fa-trash-can me-2\"></i>\n {{ '::Document:RecycleBin' | abpLocalization }}\n </h4>\n <button\n class=\"btn btn-outline-secondary\"\n (click)=\"refresh()\"\n [disabled]=\"isLoading()\"\n title=\"{{ '::Refresh' | abpLocalization }}\"\n >\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"isLoading()\"></i>\n </button>\n </div>\n\n <!-- Loading spinner -->\n @if (isLoading() && documents().items.length === 0) {\n <div class=\"text-center py-5\">\n <div class=\"spinner-border text-primary\" role=\"status\"></div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!isLoading() && documents().totalCount === 0) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center py-5\">\n <i class=\"fas fa-trash-can fa-3x text-muted mb-3 d-block\"></i>\n <p class=\"text-muted mb-0\">{{ '::Document:RecycleBinEmpty' | abpLocalization }}</p>\n </div>\n </div>\n }\n\n <!-- List -->\n @if (documents().totalCount > 0) {\n <div class=\"card shadow-sm document-recycle-table\">\n <div class=\"card-body p-0\">\n <ng-template #actionsTemplate let-doc>\n @if (hasRecycleActions) {\n <div ngbDropdown container=\"body\" class=\"d-inline-block\">\n <button type=\"button\" class=\"btn btn-sm btn-primary\" ngbDropdownToggle>\n {{ 'AbpUi::Actions' | abpLocalization }}\n </button>\n <div ngbDropdownMenu>\n @if (canRestore) {\n <button type=\"button\" ngbDropdownItem (click)=\"restore(doc)\">\n <i class=\"fas fa-rotate-left me-2\"></i>\n {{ '::Document:Restore' | abpLocalization }}\n </button>\n }\n @if (canPermanentDelete) {\n <button type=\"button\" ngbDropdownItem (click)=\"permanentDelete(doc)\">\n <i class=\"fas fa-times me-2\"></i>\n {{ '::Document:PermanentDelete' | abpLocalization }}\n </button>\n }\n </div>\n </div>\n }\n </ng-template>\n\n @if (hasRecycleActions) {\n <abp-extensible-table\n [data]=\"documents().items\"\n [recordsTotal]=\"documents().totalCount\"\n [list]=\"list\"\n [actionsTemplate]=\"actionsTemplate\"\n actionsText=\"AbpUi::Actions\"\n [actionsColumnWidth]=\"150\"\n />\n } @else {\n <abp-extensible-table\n [data]=\"documents().items\"\n [recordsTotal]=\"documents().totalCount\"\n [list]=\"list\"\n />\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host ::ng-deep .recycle-file-cell{align-items:center;display:inline-flex;max-width:100%;min-width:0}:host ::ng-deep .recycle-file-cell .text-truncate{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap}\n"] }]
175
+ }], ctorParameters: () => [] });
176
+
177
+ export { DocumentRecycleBinComponent };
178
+ //# sourceMappingURL=dignite-vault-extract-documents-document-recycle-bin.component-dqeBrw22.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-vault-extract-documents-document-recycle-bin.component-dqeBrw22.mjs","sources":["../../../packages/vault-extract/documents/src/lib/documents/document-recycle-bin/document-recycle-bin.component.ts","../../../packages/vault-extract/documents/src/lib/documents/document-recycle-bin/document-recycle-bin.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, DestroyRef, LOCALE_ID, OnInit, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CommonModule, formatDate } from '@angular/common';\nimport { ListService, LocalizationPipe, PermissionService, escapeHtmlChars } from '@abp/ng.core';\nimport {\n EntityProp,\n EXTENSIONS_IDENTIFIER,\n ExtensionsService,\n ExtensibleTableComponent,\n ePropType,\n} from '@abp/ng.components/extensible';\nimport { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared';\nimport { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';\nimport { of } from 'rxjs';\nimport {\n DocumentListItemDto,\n DocumentService,\n EXTRACT_PERMISSIONS,\n} from '@dignite/vault-extract';\nimport { ClientPagedResult, configureEntityTable, EXTRACT_TABLES } from '../../shared/extensible-table';\nimport { formatBytes } from '../../shared/format-bytes';\n\n@Component({\n selector: 'lib-document-recycle-bin',\n templateUrl: './document-recycle-bin.component.html',\n styleUrls: ['./document-recycle-bin.component.scss'],\n imports: [CommonModule, LocalizationPipe, ExtensibleTableComponent, NgbDropdownModule],\n providers: [\n ListService,\n {\n provide: EXTENSIONS_IDENTIFIER,\n useValue: EXTRACT_TABLES.DocumentRecycleBin,\n },\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DocumentRecycleBinComponent implements OnInit {\n private readonly documentService = inject(DocumentService);\n private readonly confirmation = inject(ConfirmationService);\n private readonly toaster = inject(ToasterService);\n private readonly permissionService = inject(PermissionService);\n private readonly destroyRef = inject(DestroyRef);\n private readonly extensions = inject(ExtensionsService);\n private readonly locale = inject(LOCALE_ID);\n\n readonly list = inject(ListService);\n\n documents = signal<ClientPagedResult<DocumentListItemDto>>({ totalCount: 0, items: [] });\n isLoading = signal(true);\n\n readonly canRestore = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Documents.Restore,\n );\n readonly canPermanentDelete = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Documents.PermanentDelete,\n );\n readonly hasRecycleActions = this.canRestore || this.canPermanentDelete;\n\n constructor() {\n configureEntityTable<DocumentListItemDto>(this.extensions, EXTRACT_TABLES.DocumentRecycleBin, [\n EntityProp.create<DocumentListItemDto>({\n type: ePropType.String,\n name: 'fileName',\n displayName: '::Document:FileName',\n sortable: false,\n columnWidth: 340,\n valueResolver: data => {\n const doc = data.record;\n const fileName = doc.title || doc.fileOrigin?.originalFileName || '-';\n const iconClass = this.isImage(doc)\n ? 'fas fa-file-image fa-lg text-muted'\n : 'fas fa-file-pdf fa-lg text-muted';\n return of(\n `<span class=\"recycle-file-cell\"><i class=\"${iconClass} me-2\"></i><span class=\"text-muted text-truncate\">${escapeHtmlChars(fileName)}</span></span>`,\n );\n },\n }),\n EntityProp.create<DocumentListItemDto>({\n type: ePropType.String,\n name: 'documentTypeCode',\n displayName: '::Document:Type',\n sortable: false,\n columnWidth: 180,\n valueResolver: data => {\n const typeCode = data.record.documentTypeCode;\n return of(typeCode\n ? `<span class=\"badge bg-secondary\">${escapeHtmlChars(typeCode)}</span>`\n : '<span class=\"text-muted\">-</span>');\n },\n }),\n EntityProp.create<DocumentListItemDto>({\n type: ePropType.String,\n name: 'fileSize',\n displayName: '::Document:Size',\n sortable: false,\n columnWidth: 140,\n valueResolver: data =>\n of(`<span class=\"text-muted small\">${escapeHtmlChars(formatBytes(data.record.fileOrigin?.fileSize))}</span>`),\n }),\n EntityProp.create<DocumentListItemDto>({\n type: ePropType.String,\n name: 'deletionTime',\n displayName: '::Document:DeletedAt',\n sortable: false,\n columnWidth: 180,\n valueResolver: data =>\n of(`<span class=\"text-muted small\">${escapeHtmlChars(this.formatDateTime(data.record.deletionTime))}</span>`),\n }),\n ]);\n }\n\n ngOnInit(): void {\n this.hookListQuery();\n }\n\n refresh(): void {\n this.list.getWithoutPageReset();\n }\n\n private hookListQuery(): void {\n this.list.requestStatus$\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(status => {\n if (status === 'idle' && this.isLoading() && this.documents().items.length === 0) return;\n this.isLoading.set(status === 'loading');\n });\n\n this.list\n .hookToQuery(query =>\n this.documentService.getList({\n isDeleted: true,\n maxResultCount: query.maxResultCount,\n skipCount: query.skipCount,\n sorting: query.sorting || 'creationTime desc',\n }),\n )\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(result => {\n this.documents.set({\n totalCount: result.totalCount ?? 0,\n items: result.items ?? [],\n });\n });\n }\n\n restore(doc: DocumentListItemDto): void {\n this.confirmation\n .warn('::Document:AreYouSureToRestore', '::AreYouSure')\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(status => {\n if (status !== Confirmation.Status.confirm) return;\n this.documentService.restore(doc.id!)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => {\n this.toaster.success('::Document:RestoredSuccessfully', '::Success');\n this.list.getWithoutPageReset();\n },\n error: () => this.toaster.error('::Document:RestoreFailed', '::Error'),\n });\n });\n }\n\n permanentDelete(doc: DocumentListItemDto): void {\n this.confirmation\n .warn('::Document:AreYouSureToPermanentlyDelete', '::AreYouSure', {\n yesText: '::Document:PermanentDelete',\n })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(status => {\n if (status !== Confirmation.Status.confirm) return;\n this.documentService.permanentDelete(doc.id!)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => {\n this.toaster.success('::Document:PermanentlyDeletedSuccessfully', '::Success');\n this.list.getWithoutPageReset();\n },\n error: () => this.toaster.error('::Document:PermanentDeleteFailed', '::Error'),\n });\n });\n }\n\n isImage(doc: DocumentListItemDto): boolean {\n return doc.fileOrigin?.contentType?.startsWith('image/') ?? false;\n }\n\n private formatDateTime(value: string | null | undefined): string {\n if (!value) return '-';\n\n try {\n return formatDate(value, 'yyyy-MM-dd HH:mm', this.locale);\n } catch {\n return value;\n }\n }\n}\n","<div class=\"container-fluid py-4\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h4 class=\"mb-0\">\n <i class=\"fas fa-trash-can me-2\"></i>\n {{ '::Document:RecycleBin' | abpLocalization }}\n </h4>\n <button\n class=\"btn btn-outline-secondary\"\n (click)=\"refresh()\"\n [disabled]=\"isLoading()\"\n title=\"{{ '::Refresh' | abpLocalization }}\"\n >\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"isLoading()\"></i>\n </button>\n </div>\n\n <!-- Loading spinner -->\n @if (isLoading() && documents().items.length === 0) {\n <div class=\"text-center py-5\">\n <div class=\"spinner-border text-primary\" role=\"status\"></div>\n </div>\n }\n\n <!-- Empty state -->\n @if (!isLoading() && documents().totalCount === 0) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center py-5\">\n <i class=\"fas fa-trash-can fa-3x text-muted mb-3 d-block\"></i>\n <p class=\"text-muted mb-0\">{{ '::Document:RecycleBinEmpty' | abpLocalization }}</p>\n </div>\n </div>\n }\n\n <!-- List -->\n @if (documents().totalCount > 0) {\n <div class=\"card shadow-sm document-recycle-table\">\n <div class=\"card-body p-0\">\n <ng-template #actionsTemplate let-doc>\n @if (hasRecycleActions) {\n <div ngbDropdown container=\"body\" class=\"d-inline-block\">\n <button type=\"button\" class=\"btn btn-sm btn-primary\" ngbDropdownToggle>\n {{ 'AbpUi::Actions' | abpLocalization }}\n </button>\n <div ngbDropdownMenu>\n @if (canRestore) {\n <button type=\"button\" ngbDropdownItem (click)=\"restore(doc)\">\n <i class=\"fas fa-rotate-left me-2\"></i>\n {{ '::Document:Restore' | abpLocalization }}\n </button>\n }\n @if (canPermanentDelete) {\n <button type=\"button\" ngbDropdownItem (click)=\"permanentDelete(doc)\">\n <i class=\"fas fa-times me-2\"></i>\n {{ '::Document:PermanentDelete' | abpLocalization }}\n </button>\n }\n </div>\n </div>\n }\n </ng-template>\n\n @if (hasRecycleActions) {\n <abp-extensible-table\n [data]=\"documents().items\"\n [recordsTotal]=\"documents().totalCount\"\n [list]=\"list\"\n [actionsTemplate]=\"actionsTemplate\"\n actionsText=\"AbpUi::Actions\"\n [actionsColumnWidth]=\"150\"\n />\n } @else {\n <abp-extensible-table\n [data]=\"documents().items\"\n [recordsTotal]=\"documents().totalCount\"\n [list]=\"list\"\n />\n }\n </div>\n </div>\n }\n</div>\n"],"names":[],"mappings":"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"}