@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-field-definition-list.component-ClmWkRun.mjs","sources":["../../../packages/vault-extract/documents/src/lib/fields/field-definition-list/field-definition-list.component.ts","../../../packages/vault-extract/documents/src/lib/fields/field-definition-list/field-definition-list.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n OnInit,\n inject,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CommonModule } from '@angular/common';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';\nimport { escapeHtmlChars, ListService, LocalizationPipe, LocalizationService, PermissionService } from '@abp/ng.core';\nimport type { ABP } 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 { map, of, Subject, takeUntil } from 'rxjs';\nimport {\n CreateFieldDefinitionDto,\n DocumentTypeService,\n FieldDataType,\n FieldDefinitionDraftDto,\n FieldDefinitionDto,\n FieldDefinitionService,\n FieldDraftSuggestionService,\n fieldDataTypeOptions,\n EXTRACT_PERMISSIONS,\n SlugSuggestionService,\n} from '@dignite/vault-extract';\nimport {\n ClientPagedResult,\n configureEntityTable,\n pageClientItems,\n EXTRACT_TABLES,\n SortAccessors,\n} from '../../shared/extensible-table';\nimport { FieldReextractionModalComponent } from '../../reprocessing/field-reextraction-modal/field-reextraction-modal.component';\nimport { SlugSuggestionHandle, wireSlugSuggestion } from '../../shared/slug-suggestion';\n\n// Mirrors FieldDefinitionConsts (Domain.Shared): Name whitelist + length caps.\nconst NAME_PATTERN = /^[A-Za-z0-9_\\-]{1,64}$/;\nconst MAX_NAME_LENGTH = 64;\nconst MAX_DISPLAY_NAME_LENGTH = 128;\nconst MAX_PROMPT_LENGTH = 1024;\n\nconst FIELD_DEFINITION_SORTS: SortAccessors<FieldDefinitionDto> = {\n displayOrder: field => field.displayOrder,\n name: field => field.name,\n displayName: field => field.displayName,\n dataType: field => field.dataType,\n isRequired: field => field.isRequired,\n prompt: field => field.prompt,\n};\n\n@Component({\n selector: 'lib-field-definition-list',\n templateUrl: './field-definition-list.component.html',\n styleUrls: ['./field-definition-list.component.scss'],\n imports: [\n CommonModule,\n ReactiveFormsModule,\n LocalizationPipe,\n ExtensibleTableComponent,\n NgbDropdownModule,\n FieldReextractionModalComponent,\n ],\n providers: [\n ListService,\n {\n provide: EXTENSIONS_IDENTIFIER,\n useValue: EXTRACT_TABLES.FieldDefinitions,\n },\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class FieldDefinitionListComponent implements OnInit {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly service = inject(FieldDefinitionService);\n private readonly documentTypeService = inject(DocumentTypeService);\n private readonly slugService = inject(SlugSuggestionService);\n private readonly draftService = inject(FieldDraftSuggestionService);\n private readonly fb = inject(FormBuilder);\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\n readonly list = inject(ListService);\n\n // Create/edit/delete buttons require any FieldDefinitions write grant (#217); the route's\n // FieldDefinitions.Default only lists. ABP evaluates the `||` policy expression.\n readonly canManage = this.permissionService.getGrantedPolicy(\n `${EXTRACT_PERMISSIONS.FieldDefinitions.Create} || ${EXTRACT_PERMISSIONS.FieldDefinitions.Update} || ${EXTRACT_PERMISSIONS.FieldDefinitions.Delete}`,\n );\n // Bulk field re-extraction entry point (#289): admin-level and independent from field CRUD\n // permissions.\n readonly canReextractFields = this.permissionService.getGrantedPolicy(\n EXTRACT_PERMISSIONS.Documents.Reprocessing.FieldExtraction,\n );\n // null/false means the re-extraction modal is closed.\n showReextract = signal(false);\n readonly dataTypeOptions = fieldDataTypeOptions;\n readonly FieldDataType = FieldDataType;\n\n // Route binding uses immutable DocumentTypeId (#207). The header badge primarily shows the\n // user-friendly DisplayName (#261), while TypeCode is demoted to hover text. Both are resolved by id\n // from types visible in the current layer, so renames are pierced.\n documentTypeId = '';\n documentTypeDisplayName = signal('');\n documentTypeCode = signal('');\n allFields = signal<FieldDefinitionDto[]>([]);\n fields = signal<ClientPagedResult<FieldDefinitionDto>>({ totalCount: 0, items: [] });\n isLoading = signal(true);\n showDeleted = signal(false);\n\n editing = signal<FieldDefinitionDto | 'create' | null>(null);\n isSubmitting = signal(false);\n isSuggesting = signal(false);\n // #264: \"draft from prompt\" is in progress / just completed once. Drives the spinner and \"review the\n // draft\" notice.\n isDrafting = signal(false);\n justDrafted = signal(false);\n\n private slugHandle?: SlugSuggestionHandle;\n private tableQuery: Partial<ABP.PageQueryParams> = {};\n\n // #264: signal that cancels in-flight draft requests. Emit when closing the modal so a late draft does\n // not overwrite a reopened form for an unrelated field.\n // The component-level destroyRef does not fire when the modal closes, because the modal only sets\n // editing=null and the component is not destroyed. Therefore a separate per-modal cancellation gate is\n // needed.\n private readonly draftCancelled$ = new Subject<void>();\n\n readonly form = this.fb.nonNullable.group({\n name: [\n '',\n [Validators.required, Validators.maxLength(MAX_NAME_LENGTH), Validators.pattern(NAME_PATTERN)],\n ],\n displayName: ['', [Validators.required, Validators.maxLength(MAX_DISPLAY_NAME_LENGTH)]],\n // Extraction instruction is optional based on measured feedback: remove Validators.required and keep\n // only the length cap. Backend NormalizePrompt converges blank values to null.\n prompt: ['', [Validators.maxLength(MAX_PROMPT_LENGTH)]],\n dataType: [FieldDataType.Text, [Validators.required]],\n displayOrder: [0, [Validators.required]],\n isRequired: [false],\n // #212: multiple values are valid only for text, mirroring the backend\n // FieldDefinition.ValidateMultiValue invariant. For non-text fields, applyAllowMultiplePolicy forces\n // false and disables the control; getRawValue still returns false before submit.\n allowMultiple: [false],\n // #411: whether this field participates in the type's duplicate-detection unique key.\n isUniqueKey: [false],\n });\n\n // Drives the template: allow checking \"multiple values\" only when dataType === Text.\n readonly isTextType = signal(true);\n\n constructor() {\n configureEntityTable<FieldDefinitionDto>(this.extensions, EXTRACT_TABLES.FieldDefinitions, [\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.Number,\n name: 'displayOrder',\n displayName: '::FieldDefinition:DisplayOrder',\n sortable: true,\n columnWidth: 120,\n }),\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.String,\n name: 'name',\n displayName: '::FieldDefinition:Name',\n sortable: true,\n columnWidth: 180,\n valueResolver: data => of(`<code>${escapeHtmlChars(data.record.name)}</code>`),\n }),\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.String,\n name: 'displayName',\n displayName: '::FieldDefinition:DisplayName',\n sortable: true,\n }),\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.String,\n name: 'dataType',\n displayName: '::FieldDefinition:DataType',\n sortable: true,\n columnWidth: 170,\n valueResolver: data => {\n const localization = data.getInjected(LocalizationService);\n const label = fieldDataTypeOptions.find(o => o.value === data.record.dataType)?.key ?? String(data.record.dataType);\n const suffix = data.record.allowMultiple ? '[]' : '';\n return of(`<span class=\"badge bg-light text-dark border\">${escapeHtmlChars(localization.instant('::FieldDataType:' + label))}${suffix}</span>`);\n },\n }),\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.String,\n name: 'isRequired',\n displayName: '::FieldDefinition:Required',\n sortable: true,\n columnWidth: 150,\n valueResolver: data => {\n const localization = data.getInjected(LocalizationService);\n return of(data.record.isRequired\n ? `<span class=\"badge bg-warning text-dark\">${escapeHtmlChars(localization.instant('::FieldDefinition:Required'))}</span>`\n : '<span class=\"text-muted\">-</span>');\n },\n }),\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.String,\n name: 'isUniqueKey',\n displayName: '::FieldDefinition:IsUniqueKey',\n sortable: true,\n columnWidth: 150,\n valueResolver: data => {\n const localization = data.getInjected(LocalizationService);\n return of(data.record.isUniqueKey\n ? `<span class=\"badge bg-primary\">${escapeHtmlChars(localization.instant('::FieldDefinition:IsUniqueKey'))}</span>`\n : '<span class=\"text-muted\">-</span>');\n },\n }),\n EntityProp.create<FieldDefinitionDto>({\n type: ePropType.String,\n name: 'prompt',\n displayName: '::FieldDefinition:Prompt',\n sortable: true,\n columnWidth: 320,\n valueResolver: data => {\n const prompt = data.record.prompt;\n return of(prompt\n ? `<span class=\"d-inline-block text-truncate\" style=\"max-width:280px\" title=\"${escapeHtmlChars(prompt)}\">${escapeHtmlChars(prompt)}</span>`\n : '<span class=\"text-muted\">-</span>');\n },\n }),\n ]);\n }\n\n ngOnInit(): void {\n this.hookTableQuery();\n this.documentTypeId = this.route.snapshot.paramMap.get('typeId') ?? '';\n this.resolveDocumentType();\n this.slugHandle = wireSlugSuggestion({\n displayName: this.form.controls.displayName,\n target: this.form.controls.name,\n suggest: text => this.slugService.suggest({ label: text }, undefined).pipe(map(r => r.slug ?? '')),\n fallback: () => this.nextFieldSlug(),\n destroyRef: this.destroyRef,\n onPending: pending => this.isSuggesting.set(pending),\n });\n // #212: apply the \"multiple values only for text\" policy whenever dataType changes, mirroring the\n // backend invariant and preventing illegal combinations from failing loudly on submit.\n this.form.controls.dataType.valueChanges\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(dataType => this.applyAllowMultiplePolicy(dataType));\n this.load();\n }\n\n // Non-text fields force allowMultiple=false and disable the checkbox. Switching back to text re-enables\n // it while preserving the current value.\n // Text plus multiple values is the only combination allowed by the backend entity layer\n // (FieldDefinition.MultiValueRequiresStringType), and the client mirrors that constraint for UX\n // guardrails.\n private applyAllowMultiplePolicy(dataType: FieldDataType): void {\n const isText = dataType === FieldDataType.Text;\n this.isTextType.set(isText);\n const control = this.form.controls.allowMultiple;\n if (isText) {\n control.enable({ emitEvent: false });\n } else {\n control.setValue(false, { emitEvent: false });\n control.disable({ emitEvent: false });\n }\n }\n\n // For the header badge: resolve the current type by immutable id from types visible in the current\n // layer. DisplayName is primary, and TypeCode is hover text, piercing renames.\n private resolveDocumentType(): void {\n this.documentTypeService.getVisible()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: types => {\n const type = types.find(t => t.id === this.documentTypeId);\n this.documentTypeDisplayName.set(type?.displayName ?? '');\n this.documentTypeCode.set(type?.typeCode ?? '');\n },\n });\n }\n\n // Local fallback when the LLM is unavailable or does not translate: choose the smallest field_{n} that\n // does not conflict with existing field names.\n private nextFieldSlug(): string {\n const existing = new Set(this.allFields().map(f => f.name));\n let i = 1;\n while (existing.has(`field_${i}`)) i++;\n return `field_${i}`;\n }\n\n refresh(): void {\n this.load();\n }\n\n toggleDeleted(): void {\n this.showDeleted.update(v => !v);\n this.load();\n }\n\n goBack(): void {\n this.router.navigate(['/documents/types']);\n }\n\n openReextractFields(): void {\n if (this.documentTypeId) {\n this.showReextract.set(true);\n }\n }\n\n private load(): void {\n this.isLoading.set(true);\n const source$ = this.service.getList({\n documentTypeId: this.documentTypeId,\n onlyDeleted: this.showDeleted(),\n });\n source$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: list => {\n this.allFields.set([...list].sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0)));\n this.list.totalCount = list.length;\n this.applyTableQuery();\n this.isLoading.set(false);\n },\n error: () => {\n this.allFields.set([]);\n this.fields.set({ totalCount: 0, items: [] });\n this.list.totalCount = 0;\n this.isLoading.set(false);\n },\n });\n }\n\n private hookTableQuery(): void {\n this.list.query$\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(query => this.applyTableQuery(query));\n }\n\n private applyTableQuery(query: Partial<ABP.PageQueryParams> = this.tableQuery): void {\n this.tableQuery = query;\n this.fields.set(pageClientItems(this.allFields(), query, FIELD_DEFINITION_SORTS));\n }\n\n openCreate(): void {\n const nextOrder = this.allFields().reduce((max, f) => Math.max(max, f.displayOrder ?? 0), -1) + 1;\n this.form.reset({\n name: '',\n displayName: '',\n prompt: '',\n dataType: FieldDataType.Text,\n displayOrder: nextOrder,\n isRequired: false,\n allowMultiple: false,\n isUniqueKey: false,\n });\n this.form.controls.name.enable();\n this.applyAllowMultiplePolicy(FieldDataType.Text);\n // Must be called after form.reset()/enable(): both trigger valueChanges that can be misread as\n // \"manual edit\". reset() clears that marker and resets suggestion state, including the spinner.\n this.slugHandle?.reset();\n this.justDrafted.set(false);\n this.isDrafting.set(false);\n this.editing.set('create');\n }\n\n openEdit(field: FieldDefinitionDto): void {\n // Disable before reset so slug auto-suggestion sees edit-mode reset as not automatically managed and\n // does not clear the existing name as a stale key. See wireSlugSuggestion comments.\n this.form.controls.name.disable();\n this.form.reset({\n name: field.name,\n displayName: field.displayName,\n prompt: field.prompt ?? '',\n dataType: field.dataType,\n displayOrder: field.displayOrder,\n isRequired: field.isRequired,\n allowMultiple: field.allowMultiple,\n isUniqueKey: field.isUniqueKey ?? false,\n });\n this.form.controls.name.enable();\n this.applyAllowMultiplePolicy(field.dataType ?? FieldDataType.Text);\n this.slugHandle?.markManual();\n this.justDrafted.set(false);\n this.isDrafting.set(false);\n this.editing.set(field);\n }\n\n // #264: draft field metadata from the prompt. The prompt is the primary input; one LLM call drafts the\n // remaining fields, applies them as a group, and lets the user review or modify each item.\n draft(): void {\n const prompt = (this.form.controls.prompt.value ?? '').trim();\n if (!prompt || this.isDrafting()) return;\n // forNewField controls whether the backend also suggests the machine key Name. When editing an\n // existing field, Name is a contract-level frozen identity key and is not overwritten by drafting\n // (guardrail 1).\n const forNewField = this.editing() === 'create';\n this.isDrafting.set(true);\n this.draftService.draft({ prompt, forNewField }, undefined)\n // takeUntil(draftCancelled$): cancel when the modal closes, so late responses do not write into a\n // new form (#264 review #1).\n .pipe(takeUntil(this.draftCancelled$), takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: draft => {\n this.applyDraft(draft, forNewField);\n this.isDrafting.set(false);\n },\n error: () => {\n this.isDrafting.set(false);\n // No draft was produced this time. Reset the \"review draft\" banner to avoid contradicting the\n // \"draft unavailable\" hint on the same screen (#264 review2 #1, aligned with the empty-draft\n // branch).\n this.justDrafted.set(false);\n this.toaster.warn('::FieldDefinition:DraftUnavailable', '::Warning');\n },\n });\n }\n\n // Apply the corresponding controls as a group, the landing behavior confirmed in issue #264.\n // emitEvent:false avoids triggering the displayName-to-slug wiring and clearing the just-drafted name.\n private applyDraft(draft: FieldDefinitionDraftDto, forNewField: boolean): void {\n // Backend draft failure or timeout falls back to a conservative empty draft. Empty DisplayName means\n // unavailable: keep user-entered content, show a manual-entry hint, and do not overwrite.\n if (!draft.displayName) {\n // Reset the \"review draft\" banner: this run produced no draft, avoiding a contradiction between a\n // previous success banner and the \"draft unavailable\" hint (#264 review #6).\n this.justDrafted.set(false);\n this.toaster.info('::FieldDefinition:DraftUnavailable', '::Info');\n return;\n }\n const dataType = draft.dataType ?? FieldDataType.Text;\n this.form.controls.displayName.setValue(draft.displayName, { emitEvent: false });\n this.form.controls.dataType.setValue(dataType, { emitEvent: false });\n this.form.controls.isRequired.setValue(draft.isRequired ?? false, { emitEvent: false });\n // setValue(dataType) used emitEvent:false, so valueChanges does not fire; manually apply the\n // \"multiple values only for text\" policy to enable/disable the checkbox.\n this.applyAllowMultiplePolicy(dataType);\n this.form.controls.allowMultiple.setValue(\n dataType === FieldDataType.Text && (draft.allowMultiple ?? false),\n { emitEvent: false },\n );\n if (forNewField) {\n // Create mode: overwrite the machine key as part of the group. Use the suggested value, or fall\n // back to local placeholder field_{n} when missing, such as when pure CJK sanitizes to empty after\n // no translation.\n // Never leave behind a stale key based on the previous display name (#264 review #2). Mark it as\n // manually retained so later displayName blur does not overwrite this drafted/reviewed key with a\n // slug; the user may still edit name manually.\n this.form.controls.name.setValue(draft.name || this.nextFieldSlug(), { emitEvent: false });\n this.slugHandle?.markManual();\n }\n this.form.markAsDirty();\n this.justDrafted.set(true);\n }\n\n // Display-name blur triggers slug auto-suggestion. Measured feedback changed this from pause debounce\n // to blur trigger.\n onDisplayNameBlur(): void {\n // Do not trigger the blur slug path while drafting is in flight; otherwise two LLM responses compete\n // to write name, and the last landing response is random (#264 review #2).\n // Drafting itself applies the group and markManual name, so the blur path does not need to supplement it.\n if (this.isDrafting()) return;\n this.slugHandle?.notifyDisplayNameBlur();\n }\n\n // Backdrop close guard: close only when both mousedown and click occur on the backdrop itself, not\n // inside the dialog.\n // Otherwise, dragging selected text inside an input and releasing over the backdrop can make the\n // browser fire click on the backdrop, the nearest common ancestor of mousedown/mouseup, closing the\n // modal and losing entered content. Recording the mousedown origin is the only reliable way to know\n // whether this click truly started from the backdrop.\n private backdropMouseDownOnSelf = false;\n\n onBackdropMouseDown(event: MouseEvent): void {\n this.backdropMouseDownOnSelf = event.target === event.currentTarget;\n }\n\n onBackdropClick(event: MouseEvent): void {\n if (this.backdropMouseDownOnSelf && event.target === event.currentTarget) {\n this.closeModal();\n }\n this.backdropMouseDownOnSelf = false;\n }\n\n closeModal(): void {\n // Cancel any in-flight draft request and clear the spinner, preventing late drafts from contaminating\n // the next opened form or leaving the draft button permanently disabled (#264 review #1).\n this.draftCancelled$.next();\n this.isDrafting.set(false);\n this.justDrafted.set(false);\n this.editing.set(null);\n }\n\n submit(): void {\n if (this.form.invalid) {\n this.form.markAllAsTouched();\n return;\n }\n const mode = this.editing();\n if (mode === null) return;\n\n this.isSubmitting.set(true);\n const raw = this.form.getRawValue();\n\n if (mode === 'create') {\n const input: CreateFieldDefinitionDto = {\n documentTypeId: this.documentTypeId,\n name: raw.name,\n displayName: raw.displayName,\n prompt: raw.prompt,\n dataType: raw.dataType,\n displayOrder: raw.displayOrder,\n isRequired: raw.isRequired,\n // For non-text fields the control is disabled, but getRawValue still carries it back after the\n // policy has set it to false.\n allowMultiple: raw.allowMultiple,\n isUniqueKey: raw.isUniqueKey,\n };\n this.service.create(input)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => this.onSaved('::FieldDefinition:CreatedSuccessfully'),\n error: () => this.isSubmitting.set(false),\n });\n } else {\n this.service.update(mode.id!, {\n name: raw.name,\n displayName: raw.displayName,\n prompt: raw.prompt,\n dataType: raw.dataType,\n displayOrder: raw.displayOrder,\n isRequired: raw.isRequired,\n allowMultiple: raw.allowMultiple,\n isUniqueKey: raw.isUniqueKey,\n })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => this.onSaved('::FieldDefinition:UpdatedSuccessfully'),\n error: () => this.isSubmitting.set(false),\n });\n }\n }\n\n private onSaved(messageKey: string): void {\n this.isSubmitting.set(false);\n this.closeModal();\n this.toaster.success(messageKey, '::Success');\n this.load();\n }\n\n delete(field: FieldDefinitionDto): void {\n this.confirmation\n .warn('::FieldDefinition:AreYouSureToDelete', '::AreYouSure')\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(status => {\n if (status !== Confirmation.Status.confirm) return;\n this.service.delete(field.id!)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => {\n this.toaster.success('::FieldDefinition:DeletedSuccessfully', '::Success');\n this.load();\n },\n error: () => this.toaster.error('::FieldDefinition:DeleteFailed', '::Error'),\n });\n });\n }\n\n restore(field: FieldDefinitionDto): void {\n this.service.restore(field.id!)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => {\n this.toaster.success('::FieldDefinition:RestoredSuccessfully', '::Success');\n this.load();\n },\n });\n }\n}\n","<div class=\"container-fluid py-4\">\n <!-- Header -->\n <div class=\"d-flex align-items-center mb-3 gap-2 flex-wrap\">\n <button class=\"btn btn-outline-secondary btn-sm\" (click)=\"goBack()\">\n <i class=\"fas fa-arrow-left me-1\"></i>\n {{ '::Back' | abpLocalization }}\n </button>\n <h5 class=\"mb-0 ms-2\">\n <i class=\"fas fa-list-ul me-2\"></i>\n {{ '::FieldDefinition:Title' | abpLocalization }}\n </h5>\n <span class=\"badge bg-info text-dark ms-1\" [title]=\"documentTypeCode()\">{{ documentTypeDisplayName() }}</span>\n <div class=\"ms-auto d-flex gap-2\">\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 <button\n class=\"btn\"\n [class.btn-secondary]=\"showDeleted()\"\n [class.btn-outline-secondary]=\"!showDeleted()\"\n (click)=\"toggleDeleted()\"\n >\n <i class=\"fas fa-trash-can me-1\"></i>\n {{ '::FieldDefinition:ShowDeleted' | abpLocalization }}\n </button>\n @if (canReextractFields && !showDeleted()) {\n <button class=\"btn btn-outline-primary\" (click)=\"openReextractFields()\" [disabled]=\"isLoading() || !documentTypeId\">\n <i class=\"fas fa-wand-magic-sparkles me-1\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Action' | abpLocalization }}\n </button>\n }\n @if (canManage && !showDeleted()) {\n <button class=\"btn btn-primary\" (click)=\"openCreate()\">\n <i class=\"fas fa-plus me-1\"></i>\n {{ '::FieldDefinition:New' | abpLocalization }}\n </button>\n }\n </div>\n </div>\n\n <!-- Loading spinner -->\n @if (isLoading()) {\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() && fields().totalCount === 0) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body text-center py-5\">\n <i class=\"fas fa-list-ul fa-3x text-muted mb-3 d-block\"></i>\n <p class=\"text-muted mb-0\">\n {{ (showDeleted() ? '::FieldDefinition:RecycleBinEmpty' : '::FieldDefinition:Empty') | abpLocalization }}\n </p>\n </div>\n </div>\n }\n\n <!-- Fields table -->\n @if (!isLoading() && fields().totalCount > 0) {\n <div class=\"card shadow-sm\">\n <div class=\"card-body p-0\">\n <ng-template #actionsTemplate let-field>\n @if (showDeleted()) {\n @if (canManage) {\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 <button type=\"button\" ngbDropdownItem (click)=\"restore(field)\">\n <i class=\"fas fa-rotate-left me-2\"></i>\n {{ '::FieldDefinition:Restore' | abpLocalization }}\n </button>\n </div>\n </div>\n }\n } @else if (canManage) {\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 <button type=\"button\" ngbDropdownItem (click)=\"openEdit(field)\">\n <i class=\"fas fa-pen me-2\"></i>\n {{ '::Edit' | abpLocalization }}\n </button>\n <button type=\"button\" ngbDropdownItem (click)=\"delete(field)\">\n <i class=\"fas fa-trash me-2\"></i>\n {{ '::Delete' | abpLocalization }}\n </button>\n </div>\n </div>\n }\n </ng-template>\n\n <abp-extensible-table\n [data]=\"fields().items\"\n [recordsTotal]=\"fields().totalCount\"\n [list]=\"list\"\n [actionsTemplate]=\"actionsTemplate\"\n actionsText=\"AbpUi::Actions\"\n [actionsColumnWidth]=\"150\"\n />\n </div>\n </div>\n }\n</div>\n\n<!-- Create / Edit modal -->\n@if (editing(); as mode) {\n <div\n class=\"modal d-block\"\n tabindex=\"-1\"\n style=\"background:rgba(0,0,0,.4);\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n (click)=\"onBackdropClick($event)\"\n >\n <div class=\"modal-dialog modal-dialog-centered modal-lg\">\n <div class=\"modal-content\">\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"fas fa-list-ul me-2\"></i>\n {{ (mode === 'create' ? '::FieldDefinition:New' : '::FieldDefinition:Edit') | abpLocalization }}\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"closeModal()\"></button>\n </div>\n <div class=\"modal-body\">\n <!-- #264: Prompt as the primary input. Admins focus on the extraction prompt, then use AI to draft the remaining editable field metadata. -->\n <div class=\"mb-3\">\n <div class=\"d-flex align-items-center gap-2 mb-1\">\n <label class=\"form-label mb-0\">{{ '::FieldDefinition:Prompt' | abpLocalization }}</label>\n <button\n type=\"button\"\n class=\"btn btn-sm btn-outline-primary ms-auto\"\n (click)=\"draft()\"\n [disabled]=\"!form.controls.prompt.value.trim() || isDrafting()\"\n title=\"{{ '::FieldDefinition:DraftHint' | abpLocalization }}\"\n >\n @if (isDrafting()) {\n <span class=\"spinner-border spinner-border-sm me-1\"></span>\n } @else {\n <i class=\"fas fa-wand-magic-sparkles me-1\"></i>\n }\n {{ '::FieldDefinition:DraftFromPrompt' | abpLocalization }}\n </button>\n </div>\n <textarea\n class=\"form-control\"\n rows=\"3\"\n formControlName=\"prompt\"\n [class.is-invalid]=\"form.controls.prompt.touched && form.controls.prompt.invalid\"\n placeholder=\"{{ '::FieldDefinition:PromptHint' | abpLocalization }}\"\n ></textarea>\n <div class=\"form-text\">{{ '::FieldDefinition:DraftHint' | abpLocalization }}</div>\n </div>\n\n @if (justDrafted()) {\n <div class=\"alert alert-info py-2 px-3 small d-flex align-items-center gap-2\" role=\"alert\">\n <i class=\"fas fa-circle-info\"></i>\n <span>{{ '::FieldDefinition:DraftReviewNotice' | abpLocalization }}</span>\n </div>\n }\n\n <div class=\"row g-3\">\n <div class=\"col-md-6\">\n <label class=\"form-label\">{{ '::FieldDefinition:DisplayName' | abpLocalization }}</label>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"displayName\"\n (blur)=\"onDisplayNameBlur()\"\n [class.is-invalid]=\"form.controls.displayName.touched && form.controls.displayName.invalid\"\n />\n </div>\n <div class=\"col-md-6\">\n <label class=\"form-label d-flex align-items-center gap-2\">\n <span>{{ '::FieldDefinition:Name' | abpLocalization }}</span>\n @if (isSuggesting()) {\n <span class=\"text-muted small fw-normal\">\n <span class=\"spinner-border spinner-border-sm me-1\" style=\"width:.75rem;height:.75rem;\"></span>\n {{ '::FieldDefinition:NameSuggesting' | abpLocalization }}\n </span>\n }\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"name\"\n [class.is-invalid]=\"form.controls.name.touched && form.controls.name.invalid\"\n placeholder=\"party_name\"\n />\n <div class=\"form-text\">{{ '::FieldDefinition:NameHint' | abpLocalization }}</div>\n @if (form.controls.name.touched && form.controls.name.invalid) {\n <div class=\"invalid-feedback d-block\">{{ '::FieldDefinition:NameInvalid' | abpLocalization }}</div>\n }\n </div>\n </div>\n\n <div class=\"row g-3 align-items-start mt-3\">\n <div class=\"col-md-5\">\n <label class=\"form-label\">{{ '::FieldDefinition:DataType' | abpLocalization }}</label>\n <select class=\"form-select\" formControlName=\"dataType\">\n @for (opt of dataTypeOptions; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ '::FieldDataType:' + opt.key | abpLocalization }}</option>\n }\n </select>\n </div>\n <div class=\"col-md-3\">\n <label class=\"form-label\">{{ '::FieldDefinition:DisplayOrder' | abpLocalization }}</label>\n <input type=\"number\" class=\"form-control\" formControlName=\"displayOrder\" step=\"1\" />\n </div>\n <div class=\"col-md-4\">\n <div class=\"form-check mt-2\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"isRequired\" formControlName=\"isRequired\" />\n <label class=\"form-check-label\" for=\"isRequired\">\n {{ '::FieldDefinition:Required' | abpLocalization }}\n </label>\n </div>\n <!-- #212: Multiple values are valid only for text. Non-text controls are disabled by applyAllowMultiplePolicy, and the persistent hint explains the constraint, matching backend FieldDefinition.MultiValueRequiresStringType. -->\n <div class=\"form-check\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"allowMultiple\" formControlName=\"allowMultiple\" />\n <label class=\"form-check-label\" for=\"allowMultiple\">\n {{ '::FieldDefinition:AllowMultiple' | abpLocalization }}\n </label>\n </div>\n <div class=\"form-text\">{{ '::FieldDefinition:AllowMultipleHint' | abpLocalization }}</div>\n <!-- #411: unique-key fields form the duplicate-detection fingerprint. -->\n <div class=\"form-check mt-2\">\n <input type=\"checkbox\" class=\"form-check-input\" id=\"isUniqueKey\" formControlName=\"isUniqueKey\" />\n <label class=\"form-check-label\" for=\"isUniqueKey\">\n {{ '::FieldDefinition:IsUniqueKey' | abpLocalization }}\n </label>\n </div>\n <div class=\"form-text\">{{ '::FieldDefinition:IsUniqueKeyHint' | abpLocalization }}</div>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"closeModal()\">\n {{ '::Cancel' | abpLocalization }}\n </button>\n <button type=\"submit\" class=\"btn btn-primary\" [disabled]=\"form.invalid || isSubmitting()\">\n @if (isSubmitting()) {\n <span class=\"spinner-border spinner-border-sm me-1\"></span>\n }\n {{ '::Save' | abpLocalization }}\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n}\n\n<!-- Bulk field re-extraction modal (#289), scoped to the current document type -->\n@if (showReextract()) {\n <lib-field-reextraction-modal\n [documentTypeId]=\"documentTypeId\"\n [documentTypeDisplayName]=\"documentTypeDisplayName()\"\n (closed)=\"showReextract.set(false)\"\n />\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
2
+ import { Subject, map, filter, switchMap, timeout, catchError, of, finalize } from 'rxjs';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, DestroyRef, EventEmitter, signal, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
5
+ import * as i2 from '@angular/common';
6
+ import { CommonModule } from '@angular/common';
7
+ import { LocalizationPipe } from '@abp/ng.core';
8
+ import { ToasterService } from '@abp/ng.theme.shared';
9
+ import { DocumentReprocessingService } from '@dignite/vault-extract';
10
+
11
+ // Failure fallback for synchronous LLM calls: if no result arrives within this time, treat it as
12
+ // failed, clear the spinner, and fall back to a local placeholder. Suggestion requests must not hang
13
+ // the user experience; users can always enter the machine key manually.
14
+ const SUGGEST_TIMEOUT_MS = 8000;
15
+ function wireSlugSuggestion(config) {
16
+ let manuallyEdited = false;
17
+ // Automatic target management applies when the caller enables target and has not marked it manual.
18
+ // In edit mode, if an existing machine key must be retained, the caller should disable before reset,
19
+ // then enable and markManual().
20
+ const autoManaged = () => config.target.enabled && !manuallyEdited;
21
+ // Display-name blur event stream that triggers LLM translation. Blur is naturally low-frequency, so no
22
+ // debounce is needed.
23
+ const blur$ = new Subject();
24
+ // Real typing on target marks it manual and stops automatic overwrites.
25
+ // Programmatic writes below all use { emitEvent: false }, so they do not reach this subscription.
26
+ config.target.valueChanges.pipe(takeUntilDestroyed(config.destroyRef)).subscribe(() => {
27
+ manuallyEdited = true;
28
+ });
29
+ // Stale-key protection, executed on input and decoupled from trigger timing: clear target as soon as
30
+ // displayName changes, keeping it empty until a new suggestion settles.
31
+ // target is required, so empty value means form.invalid and Save is disabled, preventing a stale
32
+ // machine key based on the old display name from being saved.
33
+ config.displayName.valueChanges.pipe(takeUntilDestroyed(config.destroyRef)).subscribe(() => {
34
+ if (autoManaged() && config.target.value !== '') {
35
+ config.target.setValue('', { emitEvent: false });
36
+ }
37
+ });
38
+ // Trigger changed to blur based on measured feedback: when leaving the displayName input, use its
39
+ // current value and start one LLM translation only if automatic management is active.
40
+ // Deliberately avoid distinctUntilChanged: displayName can change away and then back to the same value
41
+ // while target has already been cleared by stale-key protection. Adjacent de-duplication would block
42
+ // the request that refills target and leave it empty. Blur is low-frequency, so an occasional duplicate
43
+ // request is acceptable.
44
+ blur$
45
+ .pipe(map(() => (config.displayName.value ?? '').trim()), filter(text => text.length > 0 && autoManaged()), switchMap(text => {
46
+ config.onPending?.(true);
47
+ return config.suggest(text).pipe(timeout({ first: SUGGEST_TIMEOUT_MS }), catchError(() => of('')), map(slug => ({ text, slug })), finalize(() => config.onPending?.(false)));
48
+ }), takeUntilDestroyed(config.destroyRef))
49
+ .subscribe(({ text, slug }) => {
50
+ // Second line of defense: when the result returns, discard it if edit mode/manual edit has taken
51
+ // over or displayName changed from the captured value, avoiding writes based on stale displayName.
52
+ if (!autoManaged() || text !== (config.displayName.value ?? '').trim()) {
53
+ return;
54
+ }
55
+ config.target.setValue(slug || config.fallback(text), { emitEvent: false });
56
+ });
57
+ return {
58
+ reset: () => {
59
+ manuallyEdited = false;
60
+ // Reset pending: when reopening a creation form, clear any leftover "generating" hint so callers
61
+ // do not have to clean it manually.
62
+ config.onPending?.(false);
63
+ },
64
+ markManual: () => {
65
+ manuallyEdited = true;
66
+ config.onPending?.(false);
67
+ },
68
+ notifyDisplayNameBlur: () => blur$.next(),
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Bulk field re-extraction preview and trigger modal (#289 scenario 2): leaf operation with a light
74
+ * warning.
75
+ * Fetches the preview on open, including affected document count and the current field list for the
76
+ * type. Confirmation enqueues the dispatcher and immediately reports that work has moved to the
77
+ * background.
78
+ * This component owns preview, submit, and toaster behavior; the parent only controls open/close through
79
+ * [documentTypeId] and (closed).
80
+ */
81
+ class FieldReextractionModalComponent {
82
+ constructor() {
83
+ this.service = inject(DocumentReprocessingService);
84
+ this.toaster = inject(ToasterService);
85
+ this.destroyRef = inject(DestroyRef);
86
+ this.documentTypeDisplayName = '';
87
+ this.closed = new EventEmitter();
88
+ this.preview = signal(null, ...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
89
+ this.isLoadingPreview = signal(true, ...(ngDevMode ? [{ debugName: "isLoadingPreview" }] : /* istanbul ignore next */ []));
90
+ this.previewFailed = signal(false, ...(ngDevMode ? [{ debugName: "previewFailed" }] : /* istanbul ignore next */ []));
91
+ this.isSubmitting = signal(false, ...(ngDevMode ? [{ debugName: "isSubmitting" }] : /* istanbul ignore next */ []));
92
+ // Backdrop close guard: close only when both mousedown and click land on the backdrop itself, matching
93
+ // document-type-list.
94
+ this.backdropMouseDownOnSelf = false;
95
+ }
96
+ ngOnInit() {
97
+ this.loadPreview();
98
+ }
99
+ loadPreview() {
100
+ this.isLoadingPreview.set(true);
101
+ this.previewFailed.set(false);
102
+ this.service
103
+ .previewFieldExtraction(this.documentTypeId)
104
+ .pipe(takeUntilDestroyed(this.destroyRef))
105
+ .subscribe({
106
+ next: dto => {
107
+ this.preview.set(dto);
108
+ this.isLoadingPreview.set(false);
109
+ },
110
+ error: () => {
111
+ this.previewFailed.set(true);
112
+ this.isLoadingPreview.set(false);
113
+ },
114
+ });
115
+ }
116
+ confirm() {
117
+ if (this.isSubmitting())
118
+ return;
119
+ this.isSubmitting.set(true);
120
+ this.service
121
+ .startFieldExtraction({ documentTypeId: this.documentTypeId })
122
+ .pipe(takeUntilDestroyed(this.destroyRef))
123
+ .subscribe({
124
+ next: () => {
125
+ this.isSubmitting.set(false);
126
+ this.toaster.success('::Document:Reprocess:FieldExtraction:Queued', '::Success');
127
+ this.close();
128
+ },
129
+ error: () => {
130
+ this.isSubmitting.set(false);
131
+ this.toaster.error('::Document:Reprocess:Failed', '::Error');
132
+ },
133
+ });
134
+ }
135
+ close() {
136
+ this.closed.emit();
137
+ }
138
+ onBackdropMouseDown(event) {
139
+ this.backdropMouseDownOnSelf = event.target === event.currentTarget;
140
+ }
141
+ onBackdropClick(event) {
142
+ if (this.backdropMouseDownOnSelf && event.target === event.currentTarget && !this.isSubmitting()) {
143
+ this.close();
144
+ }
145
+ this.backdropMouseDownOnSelf = false;
146
+ }
147
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FieldReextractionModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
148
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FieldReextractionModalComponent, isStandalone: true, selector: "lib-field-reextraction-modal", inputs: { documentTypeId: "documentTypeId", documentTypeDisplayName: "documentTypeDisplayName" }, outputs: { closed: "closed" }, ngImport: i0, template: "<div\n class=\"modal d-block\"\n tabindex=\"-1\"\n style=\"background:rgba(0,0,0,.4);\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n (click)=\"onBackdropClick($event)\"\n>\n <div class=\"modal-dialog modal-dialog-centered\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"fas fa-wand-magic-sparkles me-2\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Title' | abpLocalization }}\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"close()\" [disabled]=\"isSubmitting()\"></button>\n </div>\n\n <div class=\"modal-body\">\n <p class=\"text-muted small mb-3\">\n {{ '::Document:Reprocess:FieldExtraction:Subtitle' | abpLocalization: documentTypeDisplayName }}\n </p>\n\n @if (isLoadingPreview()) {\n <div class=\"text-center py-4\">\n <div class=\"spinner-border text-primary\" role=\"status\"></div>\n </div>\n } @else if (previewFailed()) {\n <div class=\"alert alert-danger d-flex justify-content-between align-items-center mb-0\">\n <span>{{ '::Document:Reprocess:PreviewFailed' | abpLocalization }}</span>\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger\" (click)=\"loadPreview()\">\n {{ '::Refresh' | abpLocalization }}\n </button>\n </div>\n } @else if (preview(); as p) {\n <div class=\"alert alert-secondary\">\n <div class=\"fw-semibold mb-1\">\n {{ '::Document:Reprocess:AffectedCount' | abpLocalization: ((p.documentCount | number) ?? '') }}\n </div>\n @if ((p.fieldNames?.length ?? 0) > 0) {\n <div class=\"small text-muted mb-1\">{{ '::Document:Reprocess:FieldExtraction:FieldsToExtract' | abpLocalization }}</div>\n <div class=\"d-flex flex-wrap gap-1\">\n @for (name of (p.fieldNames ?? []); track name) {\n <span class=\"badge bg-info text-dark\">{{ name }}</span>\n }\n </div>\n } @else {\n <div class=\"small text-muted\">{{ '::Document:Reprocess:FieldExtraction:NoFields' | abpLocalization }}</div>\n }\n </div>\n\n <!-- Light warning: overwrites field values, including manual corrections. -->\n <div class=\"alert alert-warning mb-0 small\">\n <i class=\"fas fa-triangle-exclamation me-1\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Warning' | abpLocalization }}\n </div>\n }\n </div>\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"close()\" [disabled]=\"isSubmitting()\">\n {{ '::Cancel' | abpLocalization }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"confirm()\"\n [disabled]=\"isLoadingPreview() || previewFailed() || isSubmitting() || (preview()?.documentCount ?? 0) === 0\"\n >\n @if (isSubmitting()) {\n <span class=\"spinner-border spinner-border-sm me-1\"></span>\n }\n {{ '::Document:Reprocess:FieldExtraction:Start' | abpLocalization }}\n </button>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: LocalizationPipe, name: "abpLocalization" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
149
+ }
150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FieldReextractionModalComponent, decorators: [{
151
+ type: Component,
152
+ args: [{ selector: 'lib-field-reextraction-modal', standalone: true, imports: [CommonModule, LocalizationPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"modal d-block\"\n tabindex=\"-1\"\n style=\"background:rgba(0,0,0,.4);\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n (click)=\"onBackdropClick($event)\"\n>\n <div class=\"modal-dialog modal-dialog-centered\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"fas fa-wand-magic-sparkles me-2\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Title' | abpLocalization }}\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"close()\" [disabled]=\"isSubmitting()\"></button>\n </div>\n\n <div class=\"modal-body\">\n <p class=\"text-muted small mb-3\">\n {{ '::Document:Reprocess:FieldExtraction:Subtitle' | abpLocalization: documentTypeDisplayName }}\n </p>\n\n @if (isLoadingPreview()) {\n <div class=\"text-center py-4\">\n <div class=\"spinner-border text-primary\" role=\"status\"></div>\n </div>\n } @else if (previewFailed()) {\n <div class=\"alert alert-danger d-flex justify-content-between align-items-center mb-0\">\n <span>{{ '::Document:Reprocess:PreviewFailed' | abpLocalization }}</span>\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger\" (click)=\"loadPreview()\">\n {{ '::Refresh' | abpLocalization }}\n </button>\n </div>\n } @else if (preview(); as p) {\n <div class=\"alert alert-secondary\">\n <div class=\"fw-semibold mb-1\">\n {{ '::Document:Reprocess:AffectedCount' | abpLocalization: ((p.documentCount | number) ?? '') }}\n </div>\n @if ((p.fieldNames?.length ?? 0) > 0) {\n <div class=\"small text-muted mb-1\">{{ '::Document:Reprocess:FieldExtraction:FieldsToExtract' | abpLocalization }}</div>\n <div class=\"d-flex flex-wrap gap-1\">\n @for (name of (p.fieldNames ?? []); track name) {\n <span class=\"badge bg-info text-dark\">{{ name }}</span>\n }\n </div>\n } @else {\n <div class=\"small text-muted\">{{ '::Document:Reprocess:FieldExtraction:NoFields' | abpLocalization }}</div>\n }\n </div>\n\n <!-- Light warning: overwrites field values, including manual corrections. -->\n <div class=\"alert alert-warning mb-0 small\">\n <i class=\"fas fa-triangle-exclamation me-1\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Warning' | abpLocalization }}\n </div>\n }\n </div>\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"close()\" [disabled]=\"isSubmitting()\">\n {{ '::Cancel' | abpLocalization }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"confirm()\"\n [disabled]=\"isLoadingPreview() || previewFailed() || isSubmitting() || (preview()?.documentCount ?? 0) === 0\"\n >\n @if (isSubmitting()) {\n <span class=\"spinner-border spinner-border-sm me-1\"></span>\n }\n {{ '::Document:Reprocess:FieldExtraction:Start' | abpLocalization }}\n </button>\n </div>\n </div>\n </div>\n</div>\n" }]
153
+ }], propDecorators: { documentTypeId: [{
154
+ type: Input,
155
+ args: [{ required: true }]
156
+ }], documentTypeDisplayName: [{
157
+ type: Input
158
+ }], closed: [{
159
+ type: Output
160
+ }] } });
161
+
162
+ export { FieldReextractionModalComponent as F, wireSlugSuggestion as w };
163
+ //# sourceMappingURL=dignite-vault-extract-documents-field-reextraction-modal.component-D7OOycv9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-vault-extract-documents-field-reextraction-modal.component-D7OOycv9.mjs","sources":["../../../packages/vault-extract/documents/src/lib/shared/slug-suggestion.ts","../../../packages/vault-extract/documents/src/lib/reprocessing/field-reextraction-modal/field-reextraction-modal.component.ts","../../../packages/vault-extract/documents/src/lib/reprocessing/field-reextraction-modal/field-reextraction-modal.component.html"],"sourcesContent":["import { DestroyRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormControl } from '@angular/forms';\nimport {\n Observable,\n Subject,\n catchError,\n filter,\n finalize,\n map,\n of,\n switchMap,\n timeout,\n} from 'rxjs';\n\n// Failure fallback for synchronous LLM calls: if no result arrives within this time, treat it as\n// failed, clear the spinner, and fall back to a local placeholder. Suggestion requests must not hang\n// the user experience; users can always enter the machine key manually.\nconst SUGGEST_TIMEOUT_MS = 8000;\n\n/**\n * Automatic display-name-to-machine-key suggestion (issue #190). FieldDefinition and DocumentType\n * creation forms share the same wiring logic.\n *\n * Behavior:\n * - When the admin blurs displayName, call the backend LLM translation endpoint to prefill target\n * (name / typeCode).\n * Earlier versions debounced after displayName paused for 400ms. Measured feedback changed this to\n * blur-triggered behavior: fewer LLM calls, and no translation of a half-entered display name.\n * - Applies only when the caller allows it: automatic suggestion takes over only while target is\n * enabled and not marked as manually edited.\n * - Once the user manually edits target, do not overwrite it: target.valueChanges is observed, and all\n * programmatic writes use `{ emitEvent: false }`, so only real typing is treated as manual.\n * - Stale-key protection: as soon as displayName changes, immediately clear target, independent of\n * trigger timing, keeping it empty until a new suggestion settles. Together with required validation,\n * this disables Save and prevents persisting a slug based on the previous display name as an immutable\n * key. When a suggestion returns, compare the captured displayName with the current value and discard\n * mismatches. switchMap already cancels stale in-flight requests; this is the second line of defense.\n * - If the LLM returns empty or is unavailable, fall back to a local placeholder such as `field_3`, so\n * target has a valid default after settling.\n */\nexport interface SlugSuggestionHandle {\n /**\n * Re-enables automatic suggestions and resets state by clearing the manual-edit marker and pending\n * hint. Call this in the creation form opener after `form.reset()` and `enable()`.\n */\n reset(): void;\n /** Marks the current target value as retained by the user or caller, so later displayName changes no longer overwrite it automatically. */\n markManual(): void;\n /**\n * Called when the display-name input blurs. If automatic management is active (target enabled and\n * not manual), this starts one LLM slug translation.\n * The caller wires this to the displayName control's `(blur)` event.\n */\n notifyDisplayNameBlur(): void;\n}\n\nexport interface SlugSuggestionConfig {\n /** Source: human-readable display name. */\n displayName: FormControl<string>;\n /** Target: machine key (FieldDefinition.name / DocumentType.typeCode). */\n target: FormControl<string>;\n /** Calls the backend LLM endpoint and emits the sanitized slug, or '' on failure or empty output. */\n suggest: (displayName: string) => Observable<string>;\n /** Local dependency-free fallback, such as `field_3`, used when the LLM has no result. */\n fallback: (displayName: string) => string;\n destroyRef: DestroyRef;\n /** Optional: toggled while suggestion is pending, for spinner or hint text. */\n onPending?: (pending: boolean) => void;\n}\n\nexport function wireSlugSuggestion(config: SlugSuggestionConfig): SlugSuggestionHandle {\n let manuallyEdited = false;\n\n // Automatic target management applies when the caller enables target and has not marked it manual.\n // In edit mode, if an existing machine key must be retained, the caller should disable before reset,\n // then enable and markManual().\n const autoManaged = (): boolean => config.target.enabled && !manuallyEdited;\n\n // Display-name blur event stream that triggers LLM translation. Blur is naturally low-frequency, so no\n // debounce is needed.\n const blur$ = new Subject<void>();\n\n // Real typing on target marks it manual and stops automatic overwrites.\n // Programmatic writes below all use { emitEvent: false }, so they do not reach this subscription.\n config.target.valueChanges.pipe(takeUntilDestroyed(config.destroyRef)).subscribe(() => {\n manuallyEdited = true;\n });\n\n // Stale-key protection, executed on input and decoupled from trigger timing: clear target as soon as\n // displayName changes, keeping it empty until a new suggestion settles.\n // target is required, so empty value means form.invalid and Save is disabled, preventing a stale\n // machine key based on the old display name from being saved.\n config.displayName.valueChanges.pipe(takeUntilDestroyed(config.destroyRef)).subscribe(() => {\n if (autoManaged() && config.target.value !== '') {\n config.target.setValue('', { emitEvent: false });\n }\n });\n\n // Trigger changed to blur based on measured feedback: when leaving the displayName input, use its\n // current value and start one LLM translation only if automatic management is active.\n // Deliberately avoid distinctUntilChanged: displayName can change away and then back to the same value\n // while target has already been cleared by stale-key protection. Adjacent de-duplication would block\n // the request that refills target and leave it empty. Blur is low-frequency, so an occasional duplicate\n // request is acceptable.\n blur$\n .pipe(\n map(() => (config.displayName.value ?? '').trim()),\n filter(text => text.length > 0 && autoManaged()),\n switchMap(text => {\n config.onPending?.(true);\n return config.suggest(text).pipe(\n timeout({ first: SUGGEST_TIMEOUT_MS }),\n catchError(() => of('')),\n map(slug => ({ text, slug })),\n finalize(() => config.onPending?.(false)),\n );\n }),\n takeUntilDestroyed(config.destroyRef),\n )\n .subscribe(({ text, slug }) => {\n // Second line of defense: when the result returns, discard it if edit mode/manual edit has taken\n // over or displayName changed from the captured value, avoiding writes based on stale displayName.\n if (!autoManaged() || text !== (config.displayName.value ?? '').trim()) {\n return;\n }\n config.target.setValue(slug || config.fallback(text), { emitEvent: false });\n });\n\n return {\n reset: () => {\n manuallyEdited = false;\n // Reset pending: when reopening a creation form, clear any leftover \"generating\" hint so callers\n // do not have to clean it manually.\n config.onPending?.(false);\n },\n markManual: () => {\n manuallyEdited = true;\n config.onPending?.(false);\n },\n notifyDisplayNameBlur: () => blur$.next(),\n };\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n EventEmitter,\n Input,\n OnInit,\n Output,\n inject,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CommonModule } from '@angular/common';\nimport { LocalizationPipe } from '@abp/ng.core';\nimport { ToasterService } from '@abp/ng.theme.shared';\nimport {\n DocumentReprocessingService,\n FieldReextractionPreviewDto,\n} from '@dignite/vault-extract';\n\n/**\n * Bulk field re-extraction preview and trigger modal (#289 scenario 2): leaf operation with a light\n * warning.\n * Fetches the preview on open, including affected document count and the current field list for the\n * type. Confirmation enqueues the dispatcher and immediately reports that work has moved to the\n * background.\n * This component owns preview, submit, and toaster behavior; the parent only controls open/close through\n * [documentTypeId] and (closed).\n */\n@Component({\n selector: 'lib-field-reextraction-modal',\n templateUrl: './field-reextraction-modal.component.html',\n standalone: true,\n imports: [CommonModule, LocalizationPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class FieldReextractionModalComponent implements OnInit {\n private readonly service = inject(DocumentReprocessingService);\n private readonly toaster = inject(ToasterService);\n private readonly destroyRef = inject(DestroyRef);\n\n @Input({ required: true }) documentTypeId!: string;\n @Input() documentTypeDisplayName = '';\n\n @Output() closed = new EventEmitter<void>();\n\n readonly preview = signal<FieldReextractionPreviewDto | null>(null);\n readonly isLoadingPreview = signal(true);\n readonly previewFailed = signal(false);\n readonly isSubmitting = signal(false);\n\n ngOnInit(): void {\n this.loadPreview();\n }\n\n loadPreview(): void {\n this.isLoadingPreview.set(true);\n this.previewFailed.set(false);\n this.service\n .previewFieldExtraction(this.documentTypeId)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: dto => {\n this.preview.set(dto);\n this.isLoadingPreview.set(false);\n },\n error: () => {\n this.previewFailed.set(true);\n this.isLoadingPreview.set(false);\n },\n });\n }\n\n confirm(): void {\n if (this.isSubmitting()) return;\n this.isSubmitting.set(true);\n this.service\n .startFieldExtraction({ documentTypeId: this.documentTypeId })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => {\n this.isSubmitting.set(false);\n this.toaster.success('::Document:Reprocess:FieldExtraction:Queued', '::Success');\n this.close();\n },\n error: () => {\n this.isSubmitting.set(false);\n this.toaster.error('::Document:Reprocess:Failed', '::Error');\n },\n });\n }\n\n close(): void {\n this.closed.emit();\n }\n\n // Backdrop close guard: close only when both mousedown and click land on the backdrop itself, matching\n // document-type-list.\n private backdropMouseDownOnSelf = false;\n onBackdropMouseDown(event: MouseEvent): void {\n this.backdropMouseDownOnSelf = event.target === event.currentTarget;\n }\n onBackdropClick(event: MouseEvent): void {\n if (this.backdropMouseDownOnSelf && event.target === event.currentTarget && !this.isSubmitting()) {\n this.close();\n }\n this.backdropMouseDownOnSelf = false;\n }\n}\n","<div\n class=\"modal d-block\"\n tabindex=\"-1\"\n style=\"background:rgba(0,0,0,.4);\"\n (mousedown)=\"onBackdropMouseDown($event)\"\n (click)=\"onBackdropClick($event)\"\n>\n <div class=\"modal-dialog modal-dialog-centered\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"fas fa-wand-magic-sparkles me-2\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Title' | abpLocalization }}\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"close()\" [disabled]=\"isSubmitting()\"></button>\n </div>\n\n <div class=\"modal-body\">\n <p class=\"text-muted small mb-3\">\n {{ '::Document:Reprocess:FieldExtraction:Subtitle' | abpLocalization: documentTypeDisplayName }}\n </p>\n\n @if (isLoadingPreview()) {\n <div class=\"text-center py-4\">\n <div class=\"spinner-border text-primary\" role=\"status\"></div>\n </div>\n } @else if (previewFailed()) {\n <div class=\"alert alert-danger d-flex justify-content-between align-items-center mb-0\">\n <span>{{ '::Document:Reprocess:PreviewFailed' | abpLocalization }}</span>\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger\" (click)=\"loadPreview()\">\n {{ '::Refresh' | abpLocalization }}\n </button>\n </div>\n } @else if (preview(); as p) {\n <div class=\"alert alert-secondary\">\n <div class=\"fw-semibold mb-1\">\n {{ '::Document:Reprocess:AffectedCount' | abpLocalization: ((p.documentCount | number) ?? '') }}\n </div>\n @if ((p.fieldNames?.length ?? 0) > 0) {\n <div class=\"small text-muted mb-1\">{{ '::Document:Reprocess:FieldExtraction:FieldsToExtract' | abpLocalization }}</div>\n <div class=\"d-flex flex-wrap gap-1\">\n @for (name of (p.fieldNames ?? []); track name) {\n <span class=\"badge bg-info text-dark\">{{ name }}</span>\n }\n </div>\n } @else {\n <div class=\"small text-muted\">{{ '::Document:Reprocess:FieldExtraction:NoFields' | abpLocalization }}</div>\n }\n </div>\n\n <!-- Light warning: overwrites field values, including manual corrections. -->\n <div class=\"alert alert-warning mb-0 small\">\n <i class=\"fas fa-triangle-exclamation me-1\"></i>\n {{ '::Document:Reprocess:FieldExtraction:Warning' | abpLocalization }}\n </div>\n }\n </div>\n\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"close()\" [disabled]=\"isSubmitting()\">\n {{ '::Cancel' | abpLocalization }}\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"confirm()\"\n [disabled]=\"isLoadingPreview() || previewFailed() || isSubmitting() || (preview()?.documentCount ?? 0) === 0\"\n >\n @if (isSubmitting()) {\n <span class=\"spinner-border spinner-border-sm me-1\"></span>\n }\n {{ '::Document:Reprocess:FieldExtraction:Start' | abpLocalization }}\n </button>\n </div>\n </div>\n </div>\n</div>\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Formats a byte count into a human-readable string (e.g. 1536 -> "1.5 KB"), used by the overview
3
+ * statistics tiles (#333). The unit suffix is universal so it is not localized; the surrounding label
4
+ * is localized by the component. Returns "0 B" for null / non-finite / non-positive input.
5
+ */
6
+ function formatBytes(bytes, fractionDigits = 1) {
7
+ if (bytes == null || !isFinite(bytes) || bytes <= 0) {
8
+ return '0 B';
9
+ }
10
+ const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
11
+ const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
12
+ const value = bytes / Math.pow(1024, exponent);
13
+ // Whole bytes never need a fraction; larger units show up to `fractionDigits`.
14
+ const formatted = exponent === 0 ? String(value) : value.toFixed(fractionDigits);
15
+ return `${formatted} ${units[exponent]}`;
16
+ }
17
+
18
+ export { formatBytes as f };
19
+ //# sourceMappingURL=dignite-vault-extract-documents-format-bytes-Cd3QwfQZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-vault-extract-documents-format-bytes-Cd3QwfQZ.mjs","sources":["../../../packages/vault-extract/documents/src/lib/shared/format-bytes.ts"],"sourcesContent":["/**\n * Formats a byte count into a human-readable string (e.g. 1536 -> \"1.5 KB\"), used by the overview\n * statistics tiles (#333). The unit suffix is universal so it is not localized; the surrounding label\n * is localized by the component. Returns \"0 B\" for null / non-finite / non-positive input.\n */\nexport function formatBytes(bytes: number | null | undefined, fractionDigits = 1): string {\n if (bytes == null || !isFinite(bytes) || bytes <= 0) {\n return '0 B';\n }\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const exponent = Math.min(\n Math.floor(Math.log(bytes) / Math.log(1024)),\n units.length - 1,\n );\n const value = bytes / Math.pow(1024, exponent);\n // Whole bytes never need a fraction; larger units show up to `fractionDigits`.\n const formatted = exponent === 0 ? String(value) : value.toFixed(fractionDigits);\n return `${formatted} ${units[exponent]}`;\n}\n"],"names":[],"mappings":"AAAA;;;;AAIG;SACa,WAAW,CAAC,KAAgC,EAAE,cAAc,GAAG,CAAC,EAAA;AAC9E,IAAA,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE;AACnD,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;AACjD,IAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAC5C,KAAK,CAAC,MAAM,GAAG,CAAC,CACjB;AACD,IAAA,MAAM,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC;;IAE9C,MAAM,SAAS,GAAG,QAAQ,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;IAChF,OAAO,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,KAAK,CAAC,QAAQ,CAAC,EAAE;AAC1C;;;;"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Renders an `ExtractedFields` value for display. Shared by the document list and detail views so
3
+ * the two cannot drift (#212): multi-value fields arrive as JSON arrays, and a stray object must
4
+ * never surface as `[object Object]`.
5
+ * - null / undefined → "—"
6
+ * - array → elements joined with ", " (empty array → "—")
7
+ * - object → JSON
8
+ * - scalar → String(value)
9
+ */
10
+ function formatExtractedFieldValue(value) {
11
+ if (value === null || value === undefined)
12
+ return '—';
13
+ if (Array.isArray(value)) {
14
+ return value.length > 0 ? value.map(v => String(v)).join(', ') : '—';
15
+ }
16
+ if (typeof value === 'object')
17
+ return JSON.stringify(value);
18
+ return String(value);
19
+ }
20
+
21
+ export { formatExtractedFieldValue as f };
22
+ //# sourceMappingURL=dignite-vault-extract-documents-format-field-value-Xjb8lwzA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-vault-extract-documents-format-field-value-Xjb8lwzA.mjs","sources":["../../../packages/vault-extract/documents/src/lib/shared/format-field-value.ts"],"sourcesContent":["/**\n * Renders an `ExtractedFields` value for display. Shared by the document list and detail views so\n * the two cannot drift (#212): multi-value fields arrive as JSON arrays, and a stray object must\n * never surface as `[object Object]`.\n * - null / undefined → \"—\"\n * - array → elements joined with \", \" (empty array → \"—\")\n * - object → JSON\n * - scalar → String(value)\n */\nexport function formatExtractedFieldValue(value: unknown): string {\n if (value === null || value === undefined) return '—';\n if (Array.isArray(value)) {\n return value.length > 0 ? value.map(v => String(v)).join(', ') : '—';\n }\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n"],"names":[],"mappings":"AAAA;;;;;;;;AAQG;AACG,SAAU,yBAAyB,CAAC,KAAc,EAAA;AACtD,IAAA,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;AAAE,QAAA,OAAO,GAAG;AACrD,IAAA,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AACxB,QAAA,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG;IACtE;IACA,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;AAC3D,IAAA,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB;;;;"}
@@ -0,0 +1,71 @@
1
+ import { authGuard, permissionGuard } from '@abp/ng.core';
2
+ import { EXTRACT_PERMISSIONS } from '@dignite/vault-extract';
3
+
4
+ const DOCUMENTS_ROUTES = [
5
+ {
6
+ path: '',
7
+ pathMatch: 'full',
8
+ redirectTo: 'overview',
9
+ },
10
+ {
11
+ path: 'overview',
12
+ canActivate: [authGuard, permissionGuard],
13
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },
14
+ loadComponent: () => import('./dignite-vault-extract-documents-document-overview.component-BHUUUIVr.mjs').then(c => c.DocumentOverviewComponent),
15
+ },
16
+ {
17
+ path: 'list',
18
+ canActivate: [authGuard, permissionGuard],
19
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },
20
+ loadComponent: () => import('./dignite-vault-extract-documents-document-list.component-jThR5cct.mjs').then(c => c.DocumentListComponent),
21
+ },
22
+ {
23
+ path: 'recycle',
24
+ canActivate: [authGuard, permissionGuard],
25
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Restore },
26
+ loadComponent: () => import('./dignite-vault-extract-documents-document-recycle-bin.component-dqeBrw22.mjs').then(c => c.DocumentRecycleBinComponent),
27
+ },
28
+ {
29
+ path: 'types',
30
+ canActivate: [authGuard, permissionGuard],
31
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.DocumentTypes.Default },
32
+ loadComponent: () => import('./dignite-vault-extract-documents-document-type-list.component-C8kXFJGb.mjs').then(c => c.DocumentTypeListComponent),
33
+ },
34
+ {
35
+ path: 'types/:typeId/fields',
36
+ canActivate: [authGuard, permissionGuard],
37
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.FieldDefinitions.Default },
38
+ loadComponent: () => import('./dignite-vault-extract-documents-field-definition-list.component-ClmWkRun.mjs').then(c => c.FieldDefinitionListComponent),
39
+ },
40
+ {
41
+ path: 'export-templates',
42
+ canActivate: [authGuard, permissionGuard],
43
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Templates.Default },
44
+ loadComponent: () => import('./dignite-vault-extract-documents-export-template-list.component-DlmZFFF1.mjs').then(c => c.ExportTemplateListComponent),
45
+ },
46
+ {
47
+ path: 'cabinets',
48
+ canActivate: [authGuard, permissionGuard],
49
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Cabinets.Default },
50
+ loadComponent: () => import('./dignite-vault-extract-documents-cabinet-list.component-Ch0gpSCc.mjs').then(c => c.CabinetListComponent),
51
+ },
52
+ {
53
+ path: ':id/file',
54
+ canActivate: [authGuard, permissionGuard],
55
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },
56
+ loadComponent: () => import('./dignite-vault-extract-documents-document-file-preview.component-CStXf8v9.mjs').then(c => c.DocumentFilePreviewComponent),
57
+ },
58
+ {
59
+ path: ':id',
60
+ canActivate: [authGuard, permissionGuard],
61
+ data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },
62
+ loadComponent: () => import('./dignite-vault-extract-documents-document-detail.component-DHs42DWJ.mjs').then(c => c.DocumentDetailComponent),
63
+ },
64
+ ];
65
+
66
+ /**
67
+ * Generated bundle index. Do not edit.
68
+ */
69
+
70
+ export { DOCUMENTS_ROUTES };
71
+ //# sourceMappingURL=dignite-vault-extract-documents.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-vault-extract-documents.mjs","sources":["../../../packages/vault-extract/documents/src/lib/documents.routes.ts","../../../packages/vault-extract/documents/src/dignite-vault-extract-documents.ts"],"sourcesContent":["import { Routes } from '@angular/router';\nimport { authGuard, permissionGuard } from '@abp/ng.core';\nimport { EXTRACT_PERMISSIONS } from '@dignite/vault-extract';\n\nexport const DOCUMENTS_ROUTES: Routes = [\n {\n path: '',\n pathMatch: 'full',\n redirectTo: 'overview',\n },\n {\n path: 'overview',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },\n loadComponent: () =>\n import('./documents/document-overview/document-overview.component').then(c => c.DocumentOverviewComponent),\n },\n {\n path: 'list',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },\n loadComponent: () =>\n import('./documents/document-list/document-list.component').then(c => c.DocumentListComponent),\n },\n {\n path: 'recycle',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Restore },\n loadComponent: () =>\n import('./documents/document-recycle-bin/document-recycle-bin.component').then(\n c => c.DocumentRecycleBinComponent,\n ),\n },\n {\n path: 'types',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.DocumentTypes.Default },\n loadComponent: () =>\n import('./document-types/document-type-list/document-type-list.component').then(\n c => c.DocumentTypeListComponent,\n ),\n },\n {\n path: 'types/:typeId/fields',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.FieldDefinitions.Default },\n loadComponent: () =>\n import('./fields/field-definition-list/field-definition-list.component').then(\n c => c.FieldDefinitionListComponent,\n ),\n },\n {\n path: 'export-templates',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Templates.Default },\n loadComponent: () =>\n import('./exports/export-template-list/export-template-list.component').then(\n c => c.ExportTemplateListComponent,\n ),\n },\n {\n path: 'cabinets',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Cabinets.Default },\n loadComponent: () =>\n import('./cabinets/cabinet-list/cabinet-list.component').then(c => c.CabinetListComponent),\n },\n {\n path: ':id/file',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },\n loadComponent: () =>\n import('./documents/document-file-preview/document-file-preview.component').then(\n c => c.DocumentFilePreviewComponent,\n ),\n },\n {\n path: ':id',\n canActivate: [authGuard, permissionGuard],\n data: { requiredPolicy: EXTRACT_PERMISSIONS.Documents.Default },\n loadComponent: () =>\n import('./documents/document-detail/document-detail.component').then(c => c.DocumentDetailComponent),\n },\n];\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}