@conduction/nextcloud-vue 2.108.0 → 2.110.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 (161) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +115 -12
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +36 -9
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js +10 -0
  7. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js.map +1 -0
  8. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js +305 -0
  9. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js.map +1 -0
  10. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js +38 -0
  11. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js.map +1 -0
  12. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +10 -0
  13. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js.map +1 -0
  14. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +398 -0
  15. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -0
  16. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +155 -0
  17. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -0
  18. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js +9 -0
  19. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js.map +1 -0
  20. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js +281 -0
  21. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js.map +1 -0
  22. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js +137 -0
  23. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js.map +1 -0
  24. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js +9 -0
  25. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js.map +1 -0
  26. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js +225 -0
  27. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js.map +1 -0
  28. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js +110 -0
  29. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js.map +1 -0
  30. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  31. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +19 -3
  32. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  33. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  34. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
  35. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  36. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +513 -481
  37. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  38. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +9 -0
  39. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js.map +1 -0
  40. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +181 -0
  41. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -0
  42. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js +50 -0
  43. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -0
  44. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  45. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +102 -1
  46. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  47. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -6
  48. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  49. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js +9 -0
  50. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js.map +1 -0
  51. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js +190 -0
  52. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js.map +1 -0
  53. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js +91 -0
  54. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js.map +1 -0
  55. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js +9 -0
  56. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js.map +1 -0
  57. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js +64 -0
  58. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js.map +1 -0
  59. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js +49 -0
  60. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js.map +1 -0
  61. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js +9 -0
  62. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js.map +1 -0
  63. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js +382 -0
  64. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js.map +1 -0
  65. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js +285 -0
  66. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js.map +1 -0
  67. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +9 -0
  68. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js.map +1 -0
  69. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +277 -0
  70. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -0
  71. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +183 -0
  72. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -0
  73. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  74. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  75. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +53 -22
  76. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  77. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js +9 -0
  78. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js.map +1 -0
  79. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js +253 -0
  80. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js.map +1 -0
  81. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js +197 -0
  82. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js.map +1 -0
  83. package/dist/esm/index.js +12 -0
  84. package/dist/esm/index.js.map +1 -1
  85. package/dist/esm/l10n/en.json.js +106 -1
  86. package/dist/esm/l10n/en.json.js.map +1 -1
  87. package/dist/esm/l10n/nl.json.js +105 -1
  88. package/dist/esm/l10n/nl.json.js.map +1 -1
  89. package/dist/esm/library-style.js +1 -1
  90. package/dist/esm/nextcloud-vue.css +510 -76
  91. package/dist/esm/utils/actionsDispatcher.js +11 -3
  92. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  93. package/dist/esm/utils/builtInModals.js +3 -1
  94. package/dist/esm/utils/builtInModals.js.map +1 -1
  95. package/dist/esm/utils/duplicateWarning.js +62 -0
  96. package/dist/esm/utils/duplicateWarning.js.map +1 -0
  97. package/dist/esm/utils/errors.js +4 -0
  98. package/dist/esm/utils/errors.js.map +1 -1
  99. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  100. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  101. package/dist/esm/utils/openDuplicateWarning.js +39 -0
  102. package/dist/esm/utils/openDuplicateWarning.js.map +1 -0
  103. package/dist/esm/utils/optionsFrom.js +69 -2
  104. package/dist/esm/utils/optionsFrom.js.map +1 -1
  105. package/dist/esm/utils/planTree.js +57 -0
  106. package/dist/esm/utils/planTree.js.map +1 -0
  107. package/dist/esm/utils/redactionSegments.js +53 -0
  108. package/dist/esm/utils/redactionSegments.js.map +1 -0
  109. package/dist/esm/utils/registerNcDashboardWidgets.js +61 -0
  110. package/dist/esm/utils/registerNcDashboardWidgets.js.map +1 -0
  111. package/dist/esm/utils/searchRefusal.js +62 -0
  112. package/dist/esm/utils/searchRefusal.js.map +1 -0
  113. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  114. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  115. package/dist/nextcloud-vue.cjs.js +17560 -12844
  116. package/dist/nextcloud-vue.cjs.js.map +2 -2
  117. package/dist/nextcloud-vue.css +510 -76
  118. package/l10n/en.json +106 -1
  119. package/l10n/nl.json +105 -1
  120. package/package.json +1 -1
  121. package/src/components/CnActionDialog/CnActionDialog.vue +134 -13
  122. package/src/components/CnAddressSearch/CnAddressSearch.vue +343 -0
  123. package/src/components/CnAddressSearch/index.js +12 -0
  124. package/src/components/CnAssignDialog/CnAssignDialog.vue +525 -0
  125. package/src/components/CnAssignDialog/index.js +12 -0
  126. package/src/components/CnDecisionListWidget/CnDecisionListWidget.vue +388 -0
  127. package/src/components/CnDecisionListWidget/index.js +12 -0
  128. package/src/components/CnDuplicateWarning/CnDuplicateWarning.vue +318 -0
  129. package/src/components/CnDuplicateWarning/index.js +12 -0
  130. package/src/components/CnFormDialog/CnFormDialog.vue +19 -3
  131. package/src/components/CnIndexPage/CnIndexPage.vue +52 -0
  132. package/src/components/CnNcDashboardList/CnNcDashboardList.vue +218 -0
  133. package/src/components/CnNcDashboardList/index.js +12 -0
  134. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +107 -6
  135. package/src/components/CnPlanWidget/CnPlanWidget.vue +246 -0
  136. package/src/components/CnPlanWidget/CnPlanWidgetRow.vue +117 -0
  137. package/src/components/CnPlanWidget/index.js +12 -0
  138. package/src/components/CnRedactionEditor/CnRedactionEditor.vue +655 -0
  139. package/src/components/CnRedactionEditor/index.js +13 -0
  140. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +413 -0
  141. package/src/components/CnRetentionWidget/index.js +12 -0
  142. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  143. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +31 -0
  144. package/src/components/CnWorkQueue/CnWorkQueue.vue +463 -0
  145. package/src/components/CnWorkQueue/index.js +12 -0
  146. package/src/components/index.js +9 -0
  147. package/src/css/index-page.css +24 -0
  148. package/src/index.js +12 -0
  149. package/src/schemas/app-manifest-v2.schema.json +119 -3
  150. package/src/utils/actionsDispatcher.js +10 -2
  151. package/src/utils/builtInModals.js +3 -1
  152. package/src/utils/duplicateWarning.js +59 -0
  153. package/src/utils/errors.js +4 -0
  154. package/src/utils/libraryWidgetKeys.js +4 -0
  155. package/src/utils/openDuplicateWarning.js +35 -0
  156. package/src/utils/optionsFrom.js +68 -1
  157. package/src/utils/planTree.js +54 -0
  158. package/src/utils/redactionSegments.js +50 -0
  159. package/src/utils/registerNcDashboardWidgets.js +57 -0
  160. package/src/utils/searchRefusal.js +59 -0
  161. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -0,0 +1,285 @@
1
+ import { resolveComponent, openBlock, createElementBlock, createElementVNode, toDisplayString, renderSlot, createVNode, withCtx, createTextVNode, createCommentVNode, Fragment, renderList, normalizeClass, createBlock } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-redaction",
5
+ "data-testid": "cn-redaction-editor"
6
+ };
7
+ const _hoisted_2 = { class: "cn-redaction__head" };
8
+ const _hoisted_3 = { class: "cn-redaction__title" };
9
+ const _hoisted_4 = {
10
+ key: 0,
11
+ class: "cn-redaction__buttons"
12
+ };
13
+ const _hoisted_5 = {
14
+ key: 0,
15
+ class: "cn-redaction__error",
16
+ role: "alert",
17
+ "data-testid": "cn-redaction-load-error"
18
+ };
19
+ const _hoisted_6 = {
20
+ key: 1,
21
+ role: "status"
22
+ };
23
+ const _hoisted_7 = {
24
+ key: 2,
25
+ role: "status",
26
+ class: "cn-redaction__muted",
27
+ "data-testid": "cn-redaction-empty"
28
+ };
29
+ const _hoisted_8 = { class: "cn-redaction__bar" };
30
+ const _hoisted_9 = {
31
+ class: "cn-redaction__muted",
32
+ "data-testid": "cn-redaction-position"
33
+ };
34
+ const _hoisted_10 = {
35
+ key: 0,
36
+ class: "cn-redaction__pill cn-redaction__pill--accept",
37
+ "data-testid": "cn-redaction-done"
38
+ };
39
+ const _hoisted_11 = ["aria-label"];
40
+ const _hoisted_12 = {
41
+ key: 0,
42
+ class: "cn-redaction__error",
43
+ role: "alert",
44
+ "data-testid": "cn-redaction-error"
45
+ };
46
+ const _hoisted_13 = {
47
+ key: 1,
48
+ role: "status",
49
+ "data-testid": "cn-redaction-notice"
50
+ };
51
+ const _hoisted_14 = { class: "cn-redaction__columns" };
52
+ const _hoisted_15 = {
53
+ class: "cn-redaction__document",
54
+ "data-testid": "cn-redaction-text"
55
+ };
56
+ const _hoisted_16 = {
57
+ key: 0,
58
+ role: "status"
59
+ };
60
+ const _hoisted_17 = {
61
+ key: 1,
62
+ class: "cn-redaction__muted"
63
+ };
64
+ const _hoisted_18 = {
65
+ key: 2,
66
+ class: "cn-redaction__body"
67
+ };
68
+ const _hoisted_19 = { class: "cn-redaction__proposal" };
69
+ const _hoisted_20 = {
70
+ class: "cn-redaction__muted",
71
+ "data-testid": "cn-redaction-count"
72
+ };
73
+ const _hoisted_21 = { class: "cn-redaction__list" };
74
+ const _hoisted_22 = ["data-testid"];
75
+ const _hoisted_23 = { class: "cn-redaction__line" };
76
+ const _hoisted_24 = {
77
+ key: 0,
78
+ class: "cn-redaction__muted"
79
+ };
80
+ const _hoisted_25 = {
81
+ key: 1,
82
+ class: "cn-redaction__muted"
83
+ };
84
+ const _hoisted_26 = ["aria-label"];
85
+
86
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
87
+ const _component_NcButton = resolveComponent("NcButton");
88
+ const _component_NcSelect = resolveComponent("NcSelect");
89
+ const _component_NcTextField = resolveComponent("NcTextField");
90
+
91
+ return (openBlock(), createElementBlock("section", _hoisted_1, [
92
+ createElementVNode("header", _hoisted_2, [
93
+ createElementVNode("div", null, [
94
+ createElementVNode("h1", _hoisted_3, toDisplayString($options.heading), 1 /* TEXT */),
95
+ renderSlot(_ctx.$slots, "crumbs")
96
+ ]),
97
+ ($data.documents.length > 0)
98
+ ? (openBlock(), createElementBlock("div", _hoisted_4, [
99
+ createVNode(_component_NcButton, {
100
+ "data-testid": "cn-redaction-previous",
101
+ disabled: $data.index === 0 || $data.busy,
102
+ onClick: _cache[0] || (_cache[0] = $event => ($options.open($data.index - 1)))
103
+ }, {
104
+ default: withCtx(() => [
105
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Previous document')), 1 /* TEXT */)
106
+ ]),
107
+ _: 1 /* STABLE */
108
+ }, 8 /* PROPS */, ["disabled"]),
109
+ createVNode(_component_NcButton, {
110
+ "data-testid": "cn-redaction-next",
111
+ disabled: $data.index >= $data.documents.length - 1 || $data.busy,
112
+ onClick: _cache[1] || (_cache[1] = $event => ($options.open($data.index + 1)))
113
+ }, {
114
+ default: withCtx(() => [
115
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Next document')), 1 /* TEXT */)
116
+ ]),
117
+ _: 1 /* STABLE */
118
+ }, 8 /* PROPS */, ["disabled"]),
119
+ createVNode(_component_NcButton, {
120
+ variant: "primary",
121
+ "data-testid": "cn-redaction-record",
122
+ disabled: !$options.canRecord,
123
+ onClick: $options.record
124
+ }, {
125
+ default: withCtx(() => [
126
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Record redaction')), 1 /* TEXT */)
127
+ ]),
128
+ _: 1 /* STABLE */
129
+ }, 8 /* PROPS */, ["disabled", "onClick"])
130
+ ]))
131
+ : createCommentVNode("v-if", true)
132
+ ]),
133
+ ($data.loadError)
134
+ ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString($data.loadError), 1 /* TEXT */))
135
+ : ($data.loading)
136
+ ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString($options.t('nextcloud-vue', 'Loading…')), 1 /* TEXT */))
137
+ : ($data.documents.length === 0)
138
+ ? (openBlock(), createElementBlock("p", _hoisted_7, toDisplayString($options.text($props.config.emptyText) || $options.t('nextcloud-vue', 'No document here needs redacting.')), 1 /* TEXT */))
139
+ : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
140
+ createElementVNode("div", _hoisted_8, [
141
+ createElementVNode("span", _hoisted_9, [
142
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Document {index} of {count}', { index: $data.index + 1, count: $data.documents.length })) + " ", 1 /* TEXT */),
143
+ ($options.current.redactedDocumentRef)
144
+ ? (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString($options.t('nextcloud-vue', 'Redacted copy recorded')), 1 /* TEXT */))
145
+ : createCommentVNode("v-if", true)
146
+ ]),
147
+ ($props.config.urls && $props.config.urls.upload)
148
+ ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
149
+ createVNode(_component_NcButton, {
150
+ "data-testid": "cn-redaction-upload",
151
+ disabled: $data.busy,
152
+ onClick: _cache[2] || (_cache[2] = $event => (_ctx.$refs.upload.click()))
153
+ }, {
154
+ default: withCtx(() => [
155
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Upload redacted version')), 1 /* TEXT */)
156
+ ]),
157
+ _: 1 /* STABLE */
158
+ }, 8 /* PROPS */, ["disabled"]),
159
+ createElementVNode("input", {
160
+ ref: "upload",
161
+ type: "file",
162
+ class: "hidden-visually",
163
+ "data-testid": "cn-redaction-upload-input",
164
+ "aria-label": $options.t('nextcloud-vue', 'Upload redacted version'),
165
+ onChange: _cache[3] || (_cache[3] = $event => ($options.upload($event.target.files)))
166
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_11)
167
+ ], 64 /* STABLE_FRAGMENT */))
168
+ : createCommentVNode("v-if", true)
169
+ ]),
170
+ ($data.error)
171
+ ? (openBlock(), createElementBlock("p", _hoisted_12, toDisplayString($data.error), 1 /* TEXT */))
172
+ : createCommentVNode("v-if", true),
173
+ ($data.notice)
174
+ ? (openBlock(), createElementBlock("p", _hoisted_13, toDisplayString($data.notice), 1 /* TEXT */))
175
+ : createCommentVNode("v-if", true),
176
+ createElementVNode("div", _hoisted_14, [
177
+ createElementVNode("div", _hoisted_15, [
178
+ createElementVNode("strong", null, toDisplayString($options.current.title || $options.t('nextcloud-vue', 'Document without a title')), 1 /* TEXT */),
179
+ ($data.detecting)
180
+ ? (openBlock(), createElementBlock("p", _hoisted_16, toDisplayString($options.t('nextcloud-vue', 'Finding the passages to redact…')), 1 /* TEXT */))
181
+ : ($data.documentText === '')
182
+ ? (openBlock(), createElementBlock("p", _hoisted_17, toDisplayString($options.t('nextcloud-vue', 'No text was extracted from this document.')), 1 /* TEXT */))
183
+ : (openBlock(), createElementBlock("p", _hoisted_18, [
184
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.segments, (segment, at) => {
185
+ return (openBlock(), createElementBlock(Fragment, { key: at }, [
186
+ (segment.verdict)
187
+ ? (openBlock(), createElementBlock("span", {
188
+ key: 0,
189
+ class: normalizeClass(`cn-redaction__mark cn-redaction__mark--${segment.verdict}`)
190
+ }, toDisplayString(segment.text), 3 /* TEXT, CLASS */))
191
+ : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
192
+ createTextVNode(toDisplayString(segment.text), 1 /* TEXT */)
193
+ ], 64 /* STABLE_FRAGMENT */))
194
+ ], 64 /* STABLE_FRAGMENT */))
195
+ }), 128 /* KEYED_FRAGMENT */))
196
+ ]))
197
+ ]),
198
+ createElementVNode("aside", _hoisted_19, [
199
+ createElementVNode("h2", null, toDisplayString($options.t('nextcloud-vue', 'Proposal')), 1 /* TEXT */),
200
+ createElementVNode("span", _hoisted_20, toDisplayString($options.n('nextcloud-vue', '%n passage found. Review each one.', '%n passages found. Review each one.', $data.passages.length)), 1 /* TEXT */),
201
+ createElementVNode("ul", _hoisted_21, [
202
+ (openBlock(true), createElementBlock(Fragment, null, renderList($data.passages, (passage, at) => {
203
+ return (openBlock(), createElementBlock("li", {
204
+ key: at,
205
+ class: "cn-redaction__passage",
206
+ "data-testid": `cn-redaction-passage-${at}`
207
+ }, [
208
+ createElementVNode("span", _hoisted_23, [
209
+ createElementVNode("strong", null, toDisplayString($options.labelOf(passage.type)) + ": \"" + toDisplayString(passage.value) + "\"", 1 /* TEXT */),
210
+ (passage.verdict)
211
+ ? (openBlock(), createElementBlock("span", {
212
+ key: 0,
213
+ class: normalizeClass(`cn-redaction__pill cn-redaction__pill--${passage.verdict}`)
214
+ }, toDisplayString($options.verdictLabel(passage.verdict)), 3 /* TEXT, CLASS */))
215
+ : createCommentVNode("v-if", true)
216
+ ]),
217
+ (passage.locked)
218
+ ? (openBlock(), createElementBlock("span", _hoisted_24, toDisplayString($options.t('nextcloud-vue', 'Always redacted by rule')), 1 /* TEXT */))
219
+ : ($options.grounds)
220
+ ? (openBlock(), createElementBlock("span", _hoisted_25, toDisplayString($options.grounds), 1 /* TEXT */))
221
+ : createCommentVNode("v-if", true),
222
+ (!passage.locked)
223
+ ? (openBlock(), createElementBlock("div", {
224
+ key: 2,
225
+ class: "cn-redaction__verdicts",
226
+ role: "group",
227
+ "aria-label": $options.t('nextcloud-vue', 'Verdict for {passage}', { passage: passage.value })
228
+ }, [
229
+ (openBlock(true), createElementBlock(Fragment, null, renderList($data.VERDICTS, (verdict) => {
230
+ return (openBlock(), createBlock(_component_NcButton, {
231
+ key: verdict,
232
+ size: "small",
233
+ pressed: passage.verdict === verdict,
234
+ "data-testid": `cn-redaction-verdict-${at}-${verdict}`,
235
+ "onUpdate:pressed": $event => ($options.setVerdict(at, verdict))
236
+ }, {
237
+ default: withCtx(() => [
238
+ createTextVNode(toDisplayString($options.verdictLabel(verdict)), 1 /* TEXT */)
239
+ ]),
240
+ _: 2 /* DYNAMIC */
241
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["pressed", "data-testid", "onUpdate:pressed"]))
242
+ }), 128 /* KEYED_FRAGMENT */))
243
+ ], 8 /* PROPS */, _hoisted_26))
244
+ : createCommentVNode("v-if", true),
245
+ (passage.entity.source === 'officer')
246
+ ? (openBlock(), createBlock(_component_NcSelect, {
247
+ key: 3,
248
+ modelValue: $options.typeOptions.find((option) => option.id === passage.type) || null,
249
+ options: $options.typeOptions,
250
+ inputLabel: $options.t('nextcloud-vue', 'Passage type'),
251
+ clearable: false,
252
+ "data-testid": `cn-redaction-type-${at}`,
253
+ "onUpdate:modelValue": $event => ($options.retype(at, $event))
254
+ }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "data-testid", "onUpdate:modelValue"]))
255
+ : createCommentVNode("v-if", true),
256
+ (passage.verdict === 'adjust')
257
+ ? (openBlock(), createBlock(_component_NcTextField, {
258
+ key: 4,
259
+ modelValue: passage.value,
260
+ label: $options.t('nextcloud-vue', 'Passage text'),
261
+ "data-testid": `cn-redaction-value-${at}`,
262
+ "onUpdate:modelValue": $event => ($options.adjust(at, $event))
263
+ }, null, 8 /* PROPS */, ["modelValue", "label", "data-testid", "onUpdate:modelValue"]))
264
+ : createCommentVNode("v-if", true)
265
+ ], 8 /* PROPS */, _hoisted_22))
266
+ }), 128 /* KEYED_FRAGMENT */))
267
+ ]),
268
+ createVNode(_component_NcButton, {
269
+ "data-testid": "cn-redaction-add",
270
+ disabled: $data.busy || $data.detecting || Boolean($options.current.redactedDocumentRef),
271
+ onClick: $options.addPassage
272
+ }, {
273
+ default: withCtx(() => [
274
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Add passage')), 1 /* TEXT */)
275
+ ]),
276
+ _: 1 /* STABLE */
277
+ }, 8 /* PROPS */, ["disabled", "onClick"])
278
+ ])
279
+ ])
280
+ ], 64 /* STABLE_FRAGMENT */))
281
+ ]))
282
+ }
283
+
284
+ export { render };
285
+ //# sourceMappingURL=CnRedactionEditor.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnRedactionEditor.vue3.js","sources":["../../../../src/components/CnRedactionEditor/CnRedactionEditor.vue?vue&type=template&id=5802664c&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<section class=\"cn-redaction\" data-testid=\"cn-redaction-editor\">\n\t\t<header class=\"cn-redaction__head\">\n\t\t\t<div>\n\t\t\t\t<h1 class=\"cn-redaction__title\">\n\t\t\t\t\t{{ heading }}\n\t\t\t\t</h1>\n\t\t\t\t<!-- @slot Under the title: a breadcrumb back to the record. -->\n\t\t\t\t<slot name=\"crumbs\" />\n\t\t\t</div>\n\t\t\t<div v-if=\"documents.length > 0\" class=\"cn-redaction__buttons\">\n\t\t\t\t<NcButton data-testid=\"cn-redaction-previous\" :disabled=\"index === 0 || busy\" @click=\"open(index - 1)\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Previous document') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton data-testid=\"cn-redaction-next\" :disabled=\"index >= documents.length - 1 || busy\" @click=\"open(index + 1)\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Next document') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton variant=\"primary\"\n\t\t\t\t\tdata-testid=\"cn-redaction-record\"\n\t\t\t\t\t:disabled=\"!canRecord\"\n\t\t\t\t\t@click=\"record\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Record redaction') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</header>\n\n\t\t<p v-if=\"loadError\"\n\t\t\tclass=\"cn-redaction__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-redaction-load-error\">\n\t\t\t{{ loadError }}\n\t\t</p>\n\t\t<p v-else-if=\"loading\" role=\"status\">\n\t\t\t{{ t('nextcloud-vue', 'Loading…') }}\n\t\t</p>\n\t\t<p v-else-if=\"documents.length === 0\"\n\t\t\trole=\"status\"\n\t\t\tclass=\"cn-redaction__muted\"\n\t\t\tdata-testid=\"cn-redaction-empty\">\n\t\t\t{{ text(config.emptyText) || t('nextcloud-vue', 'No document here needs redacting.') }}\n\t\t</p>\n\t\t<template v-else>\n\t\t\t<div class=\"cn-redaction__bar\">\n\t\t\t\t<span class=\"cn-redaction__muted\" data-testid=\"cn-redaction-position\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Document {index} of {count}', { index: index + 1, count: documents.length }) }}\n\t\t\t\t\t<span v-if=\"current.redactedDocumentRef\" class=\"cn-redaction__pill cn-redaction__pill--accept\" data-testid=\"cn-redaction-done\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Redacted copy recorded') }}\n\t\t\t\t\t</span>\n\t\t\t\t</span>\n\t\t\t\t<template v-if=\"config.urls && config.urls.upload\">\n\t\t\t\t\t<NcButton data-testid=\"cn-redaction-upload\" :disabled=\"busy\" @click=\"$refs.upload.click()\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Upload redacted version') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<input ref=\"upload\"\n\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\tclass=\"hidden-visually\"\n\t\t\t\t\t\tdata-testid=\"cn-redaction-upload-input\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Upload redacted version')\"\n\t\t\t\t\t\t@change=\"upload($event.target.files)\">\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t\t<p v-if=\"error\"\n\t\t\t\tclass=\"cn-redaction__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-redaction-error\">\n\t\t\t\t{{ error }}\n\t\t\t</p>\n\t\t\t<p v-if=\"notice\" role=\"status\" data-testid=\"cn-redaction-notice\">\n\t\t\t\t{{ notice }}\n\t\t\t</p>\n\n\t\t\t<div class=\"cn-redaction__columns\">\n\t\t\t\t<div class=\"cn-redaction__document\" data-testid=\"cn-redaction-text\">\n\t\t\t\t\t<strong>{{ current.title || t('nextcloud-vue', 'Document without a title') }}</strong>\n\t\t\t\t\t<p v-if=\"detecting\" role=\"status\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Finding the passages to redact…') }}\n\t\t\t\t\t</p>\n\t\t\t\t\t<p v-else-if=\"documentText === ''\" class=\"cn-redaction__muted\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'No text was extracted from this document.') }}\n\t\t\t\t\t</p>\n\t\t\t\t\t<p v-else class=\"cn-redaction__body\">\n\t\t\t\t\t\t<template v-for=\"(segment, at) in segments\" :key=\"at\">\n\t\t\t\t\t\t\t<span v-if=\"segment.verdict\" :class=\"`cn-redaction__mark cn-redaction__mark--${segment.verdict}`\">{{ segment.text }}</span>\n\t\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t\t{{ segment.text }}\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\n\t\t\t\t<aside class=\"cn-redaction__proposal\">\n\t\t\t\t\t<h2>{{ t('nextcloud-vue', 'Proposal') }}</h2>\n\t\t\t\t\t<span class=\"cn-redaction__muted\" data-testid=\"cn-redaction-count\">\n\t\t\t\t\t\t{{ n('nextcloud-vue', '%n passage found. Review each one.', '%n passages found. Review each one.', passages.length) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul class=\"cn-redaction__list\">\n\t\t\t\t\t\t<li v-for=\"(passage, at) in passages\"\n\t\t\t\t\t\t\t:key=\"at\"\n\t\t\t\t\t\t\tclass=\"cn-redaction__passage\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-redaction-passage-${at}`\">\n\t\t\t\t\t\t\t<span class=\"cn-redaction__line\">\n\t\t\t\t\t\t\t\t<strong>{{ labelOf(passage.type) }}: \"{{ passage.value }}\"</strong>\n\t\t\t\t\t\t\t\t<span v-if=\"passage.verdict\" :class=\"`cn-redaction__pill cn-redaction__pill--${passage.verdict}`\">{{ verdictLabel(passage.verdict) }}</span>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span v-if=\"passage.locked\" class=\"cn-redaction__muted\">{{ t('nextcloud-vue', 'Always redacted by rule') }}</span>\n\t\t\t\t\t\t\t<span v-else-if=\"grounds\" class=\"cn-redaction__muted\">{{ grounds }}</span>\n\t\t\t\t\t\t\t<div v-if=\"!passage.locked\"\n\t\t\t\t\t\t\t\tclass=\"cn-redaction__verdicts\"\n\t\t\t\t\t\t\t\trole=\"group\"\n\t\t\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Verdict for {passage}', { passage: passage.value })\">\n\t\t\t\t\t\t\t\t<NcButton v-for=\"verdict in VERDICTS\"\n\t\t\t\t\t\t\t\t\t:key=\"verdict\"\n\t\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t\t:pressed=\"passage.verdict === verdict\"\n\t\t\t\t\t\t\t\t\t:data-testid=\"`cn-redaction-verdict-${at}-${verdict}`\"\n\t\t\t\t\t\t\t\t\t@update:pressed=\"setVerdict(at, verdict)\">\n\t\t\t\t\t\t\t\t\t{{ verdictLabel(verdict) }}\n\t\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<NcSelect v-if=\"passage.entity.source === 'officer'\"\n\t\t\t\t\t\t\t\t:modelValue=\"typeOptions.find((option) => option.id === passage.type) || null\"\n\t\t\t\t\t\t\t\t:options=\"typeOptions\"\n\t\t\t\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Passage type')\"\n\t\t\t\t\t\t\t\t:clearable=\"false\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-redaction-type-${at}`\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"retype(at, $event)\" />\n\t\t\t\t\t\t\t<NcTextField v-if=\"passage.verdict === 'adjust'\"\n\t\t\t\t\t\t\t\t:modelValue=\"passage.value\"\n\t\t\t\t\t\t\t\t:label=\"t('nextcloud-vue', 'Passage text')\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-redaction-value-${at}`\"\n\t\t\t\t\t\t\t\t@update:modelValue=\"adjust(at, $event)\" />\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<NcButton data-testid=\"cn-redaction-add\" :disabled=\"busy || detecting || Boolean(current.redactedDocumentRef)\" @click=\"addPassage\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Add passage') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</aside>\n\t\t\t</div>\n\t\t</template>\n\t</section>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcSelect, NcTextField } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\nimport { redactionSegments } from '../../utils/redactionSegments.js'\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** The verdicts a reviewer gives a passage. */\nconst VERDICTS = ['accept', 'adjust', 'skip']\n\n/**\n * The server's sentence for a failure, or the fallback.\n *\n * @param {unknown} failure The rejection.\n * @param {string} fallback What to say when the server said nothing.\n * @return {string}\n */\nfunction sentenceOf(failure, fallback) {\n\tconst data = failure && failure.response && failure.response.data\n\treturn String((data && (data.message || data.error)) || fallback)\n}\n\n/**\n * CnRedactionEditor — redact the documents of a record, one at a time\n * (board DqLakken): the document text with the passages marked in place,\n * and beside it the proposal, a verdict per passage (agreed, adjust, do not\n * redact), a passage the detection missed added by hand, and Record\n * redaction. It names no procedure: the routes and the passage type labels\n * come from `config`, so dossiq's Woo redaction and filinq's anonymisation\n * use the same editor.\n *\n * The check is the reviewer's: Record redaction is enabled only once every\n * passage has a verdict, and it first records the check of the passages to\n * redact, in the reviewer's name, and only then asks for the redacted copy.\n * A passage marked `locked` (a rule that always applies) starts agreed and\n * cannot be changed. A refusal is shown in the server's words and the\n * upload of a version redacted elsewhere stays offered.\n *\n * `config`:\n * - `urls`: `{ list, detect, check, redact, upload? }`, templates with\n * `{recordId}` and `{documentRef}`. `list` answers `{ documents: [{\n * documentRef, title, redactedDocumentRef?, redaction? }] }`; `detect`\n * answers `{ text, redaction: { entities } }`; `check` receives `{ entities\n * }`; `redact` and `upload` (a `file` form field) answer `{\n * redactedDocumentRef, redaction }`.\n * - `typeLabels`: what a passage type is called (`{ PERSON: \"Name\" }`).\n * - `groundsPath`: a document's grounds (a list) shown under each passage.\n * - `title`, `emptyText`: texts. All through the host translate.\n */\nexport default {\n\tname: 'CnRedactionEditor',\n\n\tcomponents: { NcButton, NcSelect, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The record whose documents are redacted, for `{recordId}`. */\n\t\trecordId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The editor's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['recorded'],\n\n\tdata() {\n\t\treturn {\n\t\t\tVERDICTS,\n\t\t\tloading: true,\n\t\t\tloadError: '',\n\t\t\tdocuments: [],\n\t\t\tindex: 0,\n\t\t\tdocumentText: '',\n\t\t\tpassages: [],\n\t\t\tdetecting: false,\n\t\t\tbusy: false,\n\t\t\terror: '',\n\t\t\tnotice: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The open document. */\n\t\tcurrent() {\n\t\t\treturn this.documents[this.index] || {}\n\t\t},\n\n\t\t/** @return {string} The heading. */\n\t\theading() {\n\t\t\tconst own = this.text(this.config.title)\n\t\t\tif (own) {\n\t\t\t\treturn own\n\t\t\t}\n\t\t\treturn this.current.title ? t('nextcloud-vue', 'Redact: {title}', { title: this.current.title }) : t('nextcloud-vue', 'Redact documents')\n\t\t},\n\n\t\t/** @return {Array<{id: string, label: string}>} The passage types. */\n\t\ttypeOptions() {\n\t\t\treturn Object.keys(this.config.typeLabels || {}).map((type) => ({ id: type, label: this.labelOf(type) }))\n\t\t},\n\n\t\t/** @return {string} The open document's grounds. */\n\t\tgrounds() {\n\t\t\tconst list = this.config.groundsPath ? readPath(this.current, this.config.groundsPath) : null\n\t\t\treturn Array.isArray(list) ? list.join(', ') : ''\n\t\t},\n\n\t\t/** @return {Array<object>} The entities to redact: agreed or adjusted, with text. */\n\t\ttoRedact() {\n\t\t\treturn this.passages\n\t\t\t\t.filter((p) => p.verdict === 'accept' || p.verdict === 'adjust')\n\t\t\t\t.map((p) => ({ ...p.entity, value: p.value.trim() }))\n\t\t\t\t.filter((entity) => entity.value !== '')\n\t\t},\n\n\t\t/**\n\t\t * Whether the redaction may be recorded: every passage judged, at least one to redact.\n\t\t *\n\t\t * @spec openspec/changes/redaction-editor/specs/redaction-editor/spec.md#requirement-a-reviewer-redacts-a-records-documents\n\t\t * @return {boolean}\n\t\t */\n\t\tcanRecord() {\n\t\t\treturn !this.busy && !this.detecting && !this.current.redactedDocumentRef\n\t\t\t\t&& this.passages.length > 0\n\t\t\t\t&& this.passages.every((p) => p.verdict !== null)\n\t\t\t\t&& this.toRedact.length > 0\n\t\t},\n\n\t\t/** @return {Array<{text: string, verdict: (string|null)}>} The text with the passages marked. */\n\t\tsegments() {\n\t\t\treturn redactionSegments(this.documentText, this.passages)\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tn,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A configured url with the record and the open document filled in.\n\t\t *\n\t\t * @param {string} key The `urls` key.\n\t\t * @return {string}\n\t\t */\n\t\turlOf(key) {\n\t\t\tconst template = String((this.config.urls || {})[key] || '')\n\t\t\treturn generateUrl(template\n\t\t\t\t.replace('{recordId}', encodeURIComponent(String(this.recordId)))\n\t\t\t\t.replace('{documentRef}', encodeURIComponent(String(this.current.documentRef || ''))))\n\t\t},\n\n\t\t/**\n\t\t * What a passage type is called.\n\t\t *\n\t\t * @param {string} type The type.\n\t\t * @return {string}\n\t\t */\n\t\tlabelOf(type) {\n\t\t\tconst label = (this.config.typeLabels || {})[type]\n\t\t\treturn label ? this.cnTranslate(String(label)) : String(type || '')\n\t\t},\n\n\t\t/**\n\t\t * What a verdict is called.\n\t\t *\n\t\t * @param {string} verdict The verdict.\n\t\t * @return {string}\n\t\t */\n\t\tverdictLabel(verdict) {\n\t\t\treturn ({ accept: t('nextcloud-vue', 'Agreed'), adjust: t('nextcloud-vue', 'Adjust'), skip: t('nextcloud-vue', 'Do not redact') })[verdict] || ''\n\t\t},\n\n\t\t/**\n\t\t * Read the documents and open the first.\n\t\t *\n\t\t * @spec openspec/changes/redaction-editor/specs/redaction-editor/spec.md#requirement-a-reviewer-redacts-a-records-documents\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.loadError = ''\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(this.urlOf('list'))\n\t\t\t\tthis.documents = Array.isArray(data && data.documents) ? data.documents : []\n\t\t\t} catch (failure) {\n\t\t\t\tthis.documents = []\n\t\t\t\tthis.loadError = sentenceOf(failure, t('nextcloud-vue', 'The documents could not be read.'))\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t\tif (this.documents.length > 0) {\n\t\t\t\tawait this.open(Math.min(this.index, this.documents.length - 1))\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open one document: its recorded redaction, or the passages detected in it.\n\t\t *\n\t\t * @spec openspec/changes/redaction-editor/specs/redaction-editor/spec.md#requirement-a-reviewer-redacts-a-records-documents\n\t\t * @param {number} at The document's position.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync open(at) {\n\t\t\tthis.index = at\n\t\t\tthis.error = ''\n\t\t\tthis.notice = ''\n\t\t\tthis.documentText = ''\n\t\t\tthis.passages = []\n\t\t\tif (this.current.redactedDocumentRef) {\n\t\t\t\tthis.passages = this.passagesOf((this.current.redaction && this.current.redaction.entities) || [], 'accept')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.detecting = true\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.post(this.urlOf('detect'))\n\t\t\t\tthis.documentText = String((data && data.text) || '')\n\t\t\t\tthis.passages = this.passagesOf((data && data.redaction && data.redaction.entities) || [], null)\n\t\t\t} catch (failure) {\n\t\t\t\tthis.error = sentenceOf(failure, t('nextcloud-vue', 'The passages of this document could not be found. Upload a redacted version instead.'))\n\t\t\t} finally {\n\t\t\t\tthis.detecting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Passages from entities; a locked entity starts agreed.\n\t\t *\n\t\t * @param {Array<object>} entities The entities.\n\t\t * @param {string|null} verdict The verdict the others start with.\n\t\t * @return {Array<object>}\n\t\t */\n\t\tpassagesOf(entities, verdict) {\n\t\t\treturn entities.map((entity) => ({\n\t\t\t\tentity,\n\t\t\t\ttype: String((entity && entity.type) || ''),\n\t\t\t\tvalue: String((entity && entity.value) || ''),\n\t\t\t\tlocked: Boolean(entity && entity.locked),\n\t\t\t\tverdict: entity && entity.locked ? 'accept' : verdict,\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Give a passage a verdict.\n\t\t *\n\t\t * @param {number} at The passage.\n\t\t * @param {string} verdict The verdict.\n\t\t */\n\t\tsetVerdict(at, verdict) {\n\t\t\tif (!this.passages[at].locked) {\n\t\t\t\tthis.passages[at] = { ...this.passages[at], verdict }\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Change an adjusted passage's text.\n\t\t *\n\t\t * @param {number} at The passage.\n\t\t * @param {string} value The text.\n\t\t */\n\t\tadjust(at, value) {\n\t\t\tthis.passages[at] = { ...this.passages[at], value: String(value ?? '') }\n\t\t},\n\n\t\t/**\n\t\t * Give a hand-added passage its type.\n\t\t *\n\t\t * @param {number} at The passage.\n\t\t * @param {{id: string}|null} option The type.\n\t\t */\n\t\tretype(at, option) {\n\t\t\tconst type = String((option && option.id) || 'UNKNOWN')\n\t\t\tconst passage = this.passages[at]\n\t\t\tthis.passages[at] = { ...passage, type, entity: { ...passage.entity, type } }\n\t\t},\n\n\t\t/** Add a passage the detection missed. */\n\t\taddPassage() {\n\t\t\tthis.passages.push({ entity: { type: 'UNKNOWN', source: 'officer' }, type: 'UNKNOWN', value: '', locked: false, verdict: 'adjust' })\n\t\t},\n\n\t\t/**\n\t\t * Record the reviewer's check of the passages, then ask for the redacted copy.\n\t\t *\n\t\t * @spec openspec/changes/redaction-editor/specs/redaction-editor/spec.md#requirement-a-reviewer-redacts-a-records-documents\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync record() {\n\t\t\tif (!this.canRecord) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.error = ''\n\t\t\tthis.notice = ''\n\t\t\ttry {\n\t\t\t\tawait axios.post(this.urlOf('check'), { entities: this.toRedact })\n\t\t\t\tconst { data } = await axios.post(this.urlOf('redact'))\n\t\t\t\tthis.recorded(data)\n\t\t\t} catch (failure) {\n\t\t\t\tthis.error = sentenceOf(failure, t('nextcloud-vue', 'The redacted copy could not be made. Upload a redacted version instead.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Upload a version redacted elsewhere.\n\t\t *\n\t\t * @param {FileList|null} files The chosen file.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync upload(files) {\n\t\t\tconst file = files && files[0]\n\t\t\tif (!file) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst form = new FormData()\n\t\t\tform.append('file', file)\n\t\t\tthis.busy = true\n\t\t\tthis.error = ''\n\t\t\tthis.notice = ''\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.post(this.urlOf('upload'), form)\n\t\t\t\tthis.recorded(data)\n\t\t\t} catch (failure) {\n\t\t\t\tthis.error = sentenceOf(failure, t('nextcloud-vue', 'The redacted version could not be stored. Try again.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t\tif (this.$refs.upload) {\n\t\t\t\t\tthis.$refs.upload.value = ''\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Show the recorded copy and refresh the page.\n\t\t *\n\t\t * @param {object} data The server's answer.\n\t\t */\n\t\trecorded(data) {\n\t\t\tthis.documents[this.index] = { ...this.current, redactedDocumentRef: String((data && data.redactedDocumentRef) || ''), redaction: (data && data.redaction) || {} }\n\t\t\tthis.notice = t('nextcloud-vue', 'The redacted copy is recorded. Only this copy will be published.')\n\t\t\temit(PAGE_REFRESH, { reason: 'redaction' })\n\t\t\t/** @event recorded A redacted copy was recorded. Payload: `{ documentRef, redactedDocumentRef }`. */\n\t\t\tthis.$emit('recorded', { documentRef: this.current.documentRef, redactedDocumentRef: this.current.redactedDocumentRef })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-redaction {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-redaction__head,\n.cn-redaction__bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n}\n\n.cn-redaction__title {\n\tmargin: 0;\n\tfont-size: 28px;\n\tfont-weight: 700;\n}\n\n.cn-redaction__buttons,\n.cn-redaction__verdicts {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-redaction__muted {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-redaction__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-redaction__columns {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tgap: 20px;\n}\n\n.cn-redaction__document,\n.cn-redaction__proposal {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--color-main-background);\n}\n\n.cn-redaction__document {\n\tflex: 999 1 520px;\n\tgap: 12px;\n\tpadding: 28px 32px;\n}\n\n.cn-redaction__proposal {\n\tflex: 1 1 300px;\n\tgap: 10px;\n\tpadding: 20px 22px;\n}\n\n.cn-redaction__body {\n\twhite-space: pre-wrap;\n\tline-height: 1.6;\n}\n\n.cn-redaction__mark--accept {\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-text);\n}\n\n.cn-redaction__mark--adjust,\n.cn-redaction__mark--open {\n\toutline: 2px solid var(--color-warning-text, var(--color-warning));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-redaction__mark--skip {\n\ttext-decoration: underline dotted;\n}\n\n.cn-redaction__list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-redaction__passage {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-redaction__line {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-redaction__pill {\n\tdisplay: inline-block;\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-dark);\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n\n.cn-redaction__pill--accept {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-redaction__pill--adjust {\n\tcolor: var(--color-warning-text, var(--color-warning));\n}\n</style>\n"],"names":["_createElementVNode","_toDisplayString","_renderSlot","_createVNode","_createElementBlock","_createTextVNode","_Fragment","_openBlock","_renderList","_normalizeClass","_createBlock"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import script from './CnRetentionWidget.vue2.js';
2
+ import { render } from './CnRetentionWidget.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-d883c442";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnRetentionWidget.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnRetentionWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,277 @@
1
+ import { getCurrentUser } from '@nextcloud/auth';
2
+ import axios__default from '@nextcloud/axios';
3
+ import { translate } from '@nextcloud/l10n';
4
+ import { generateUrl } from '@nextcloud/router';
5
+ import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField } from '@nextcloud/vue';
6
+ import { readPath } from '../../utils/readPath.js';
7
+
8
+ /**
9
+ * CnRetentionWidget — the `retention` detail widget: what happens to a
10
+ * record when its business use ends, as OpenRegister decided it. Every value
11
+ * is read off the record's `@self._retention` (appraisal, disposal date,
12
+ * retention period, selection list row, the rule and when it was written)
13
+ * and `@self.archived`; the widget derives nothing, so it can never disagree
14
+ * with the stored decision. A failed read is an error with a retry, never an
15
+ * empty block. A record nothing could nominate is drawn apart from one that
16
+ * is not closed yet. An archivist (or an admin) may ask OpenRegister to
17
+ * recompute the nomination, with a reason. Any app on OpenRegister uses it;
18
+ * dossiq's case Archiving tab is the first.
19
+ *
20
+ * `content`:
21
+ * - `title`, `subtitle`: texts, through the host translate.
22
+ * - `statusField`, `archivedValues`: a record field and the values that mean
23
+ * archived (`archiveStatus`, `["archived", ...]`); when set, a mismatch with
24
+ * `@self.archived` is said out loud.
25
+ * - `archivistGroups`: groups that may recompute (default `["archivaris"]`;
26
+ * admins always may); `recompute: false` hides the control.
27
+ *
28
+ * The record is read fresh from OpenRegister by `register`, `schema` and
29
+ * `objectId` (props, else the bound record's `@self`).
30
+ */
31
+ var script = {
32
+ name: 'CnRetentionWidget',
33
+
34
+ components: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField },
35
+
36
+ inject: {
37
+ /** The host app's translate (CnAppRoot `cnTranslate`). */
38
+ cnTranslate: { default: () => (key) => key },
39
+ },
40
+
41
+ props: {
42
+ /**
43
+ * The widget's configuration (see the component docs).
44
+ *
45
+ * @type {object}
46
+ */
47
+ content: {
48
+ type: Object,
49
+ default: () => ({}),
50
+ },
51
+
52
+ /**
53
+ * The bound record, on a detail page.
54
+ *
55
+ * @type {object|null}
56
+ */
57
+ objectData: {
58
+ type: Object,
59
+ default: null,
60
+ },
61
+
62
+ /** The record's register (else the bound record's `@self.register`). */
63
+ register: {
64
+ type: [String, Number],
65
+ default: '',
66
+ },
67
+
68
+ /**
69
+ * The record's schema (else the bound record's `@self.schema`); a schema object gives its slug.
70
+ *
71
+ * @type {string|number|object}
72
+ */
73
+ schema: {
74
+ type: [String, Number, Object],
75
+ default: '',
76
+ },
77
+
78
+ /** The record's id (else the bound record's). */
79
+ objectId: {
80
+ type: [String, Number],
81
+ default: '',
82
+ },
83
+ },
84
+
85
+ data() {
86
+ return {
87
+ loading: true,
88
+ error: '',
89
+ record: null,
90
+ reason: '',
91
+ busy: false,
92
+ }
93
+ },
94
+
95
+ computed: {
96
+ /** @return {object} The bound record's `@self`. */
97
+ boundSelf() {
98
+ return (this.objectData && this.objectData['@self']) || {}
99
+ },
100
+
101
+ /** @return {string} The record id. */
102
+ id() {
103
+ return String(this.objectId || (this.objectData && this.objectData.id) || this.boundSelf.id || '')
104
+ },
105
+
106
+ /** @return {string} The register. */
107
+ registerRef() {
108
+ return String(this.register || this.boundSelf.register || '')
109
+ },
110
+
111
+ /** @return {string} The schema. */
112
+ schemaRef() {
113
+ const s = this.schema || this.boundSelf.schema || '';
114
+ return String(typeof s === 'object' ? (s.slug || s.id || '') : s)
115
+ },
116
+
117
+ /** @return {object} The record's `@self`. */
118
+ self() {
119
+ return (this.record && this.record['@self']) || {}
120
+ },
121
+
122
+ /** @return {object|null} OpenRegister's stored retention decision. */
123
+ retention() {
124
+ return this.self._retention || null
125
+ },
126
+
127
+ /** @return {object|null} The nomination. */
128
+ nomination() {
129
+ return (this.retention && this.retention.nomination) || null
130
+ },
131
+
132
+ /** @return {object|null} The hand-over outcome. */
133
+ outcome() {
134
+ return (this.retention && this.retention.outcome) || null
135
+ },
136
+
137
+ /** @return {object|null} OpenRegister's archived marker. */
138
+ archived() {
139
+ return this.self.archived || null
140
+ },
141
+
142
+ /**
143
+ * Whether the record's own field and the platform marker disagree.
144
+ *
145
+ * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention
146
+ * @return {boolean}
147
+ */
148
+ disagrees() {
149
+ const field = this.content.statusField;
150
+ if (!field || !this.record) {
151
+ return false
152
+ }
153
+ const values = Array.isArray(this.content.archivedValues) ? this.content.archivedValues.map(String) : ['archived'];
154
+ const saysArchived = values.includes(String(readPath(this.record, field) ?? ''));
155
+ return saysArchived !== Boolean(this.archived)
156
+ },
157
+
158
+ /**
159
+ * The stored facts that have a value, in reading order.
160
+ *
161
+ * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention
162
+ * @return {Array<{key: string, label: string, value: string}>}
163
+ */
164
+ facts() {
165
+ const r = this.retention || {};
166
+ const n = this.nomination || {};
167
+ return [
168
+ { key: 'appraisal', label: translate('nextcloud-vue', 'Appraisal'), value: r.appraisal },
169
+ { key: 'disposal-date', label: translate('nextcloud-vue', 'Disposal date'), value: r.disposalDate },
170
+ { key: 'retention-period', label: translate('nextcloud-vue', 'Retention period'), value: r.retentionPeriod },
171
+ { key: 'selection-list-row', label: translate('nextcloud-vue', 'Selection list row'), value: r.selectionListRow },
172
+ { key: 'rule', label: translate('nextcloud-vue', 'Decided by'), value: n.rule },
173
+ { key: 'decided-at', label: translate('nextcloud-vue', 'Written on'), value: n.at },
174
+ ].filter((fact) => fact.value !== undefined && fact.value !== null && fact.value !== '').map((fact) => ({ ...fact, value: String(fact.value) }))
175
+ },
176
+
177
+ /** @return {Array<{key: string, label: string, value: string}>} The facts of a record not closed yet. */
178
+ pendingFacts() {
179
+ const atClosure = translate('nextcloud-vue', 'Decided when it closes');
180
+ return [
181
+ { key: 'appraisal', label: translate('nextcloud-vue', 'Appraisal'), value: atClosure },
182
+ { key: 'retention-period', label: translate('nextcloud-vue', 'Retention period'), value: atClosure },
183
+ { key: 'disposal-date', label: translate('nextcloud-vue', 'Disposal date'), value: atClosure },
184
+ { key: 'decided-at', label: translate('nextcloud-vue', 'Written on'), value: translate('nextcloud-vue', 'Not yet') },
185
+ ]
186
+ },
187
+
188
+ /** @return {string} The signed-in user's id. */
189
+ currentUid() {
190
+ const user = getCurrentUser();
191
+ return String((user && user.uid) || '')
192
+ },
193
+
194
+ /**
195
+ * Whether this user may recompute: an admin or an archivist group member.
196
+ * Shown disabled otherwise, so the user learns which role it needs.
197
+ *
198
+ * @return {boolean}
199
+ */
200
+ mayRecompute() {
201
+ const user = getCurrentUser();
202
+ if (user && user.isAdmin === true) {
203
+ return true
204
+ }
205
+ const groups = Array.isArray(user && user.groups) ? user.groups : [];
206
+ const allowed = Array.isArray(this.content.archivistGroups) ? this.content.archivistGroups : ['archivaris'];
207
+ return allowed.some((g) => groups.includes(g))
208
+ },
209
+ },
210
+
211
+ mounted() {
212
+ this.load();
213
+ },
214
+
215
+ methods: {
216
+ t: translate,
217
+
218
+ /**
219
+ * A config text through the host translate.
220
+ *
221
+ * @param {unknown} value The text.
222
+ * @return {string}
223
+ */
224
+ text(value) {
225
+ return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''
226
+ },
227
+
228
+ /**
229
+ * Read the record fresh from OpenRegister; a failure is an error, never an empty block.
230
+ *
231
+ * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention
232
+ * @return {Promise<void>}
233
+ */
234
+ async load() {
235
+ this.loading = true;
236
+ this.error = '';
237
+ try {
238
+ if (!this.registerRef || !this.schemaRef || !this.id) {
239
+ throw new Error(translate('nextcloud-vue', 'This widget has no record to read.'))
240
+ }
241
+ const { data } = await axios__default.get(generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(this.registerRef)}/${encodeURIComponent(this.schemaRef)}/${encodeURIComponent(this.id)}`));
242
+ this.record = data && typeof data === 'object' ? data : null;
243
+ } catch (failure) {
244
+ const body = failure && failure.response && failure.response.data;
245
+ this.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure);
246
+ } finally {
247
+ this.loading = false;
248
+ }
249
+ },
250
+
251
+ /**
252
+ * Ask OpenRegister to derive the nomination again, then read it back.
253
+ *
254
+ * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention
255
+ * @return {Promise<void>}
256
+ */
257
+ async recompute() {
258
+ if (this.busy || this.reason.trim() === '') {
259
+ return
260
+ }
261
+ this.busy = true;
262
+ try {
263
+ await axios__default.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: this.reason.trim() });
264
+ this.reason = '';
265
+ await this.load();
266
+ } catch (failure) {
267
+ const body = failure && failure.response && failure.response.data;
268
+ this.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure);
269
+ } finally {
270
+ this.busy = false;
271
+ }
272
+ },
273
+ },
274
+ };
275
+
276
+ export { script as default };
277
+ //# sourceMappingURL=CnRetentionWidget.vue2.js.map