@spokane-folio/security-incident 1.0.28

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 (188) hide show
  1. package/.eslintrc +32 -0
  2. package/.github/workflows/CODEOWNERS +8 -0
  3. package/.github/workflows/pr-validation.yml +44 -0
  4. package/.github/workflows/release.yml +64 -0
  5. package/.prettierrc +6 -0
  6. package/.stripesclirc +4 -0
  7. package/CHANGELOG.md +8 -0
  8. package/CONTRIBUTING.md +4 -0
  9. package/LICENSE +201 -0
  10. package/README.md +16 -0
  11. package/administrator-documentation/roles-and-permissions.md +65 -0
  12. package/administrator-documentation/track-settings-admin-guide-sketch.md +192 -0
  13. package/administrator-documentation/using-the-application.md +192 -0
  14. package/icons/app.png +0 -0
  15. package/icons/app.svg +1 -0
  16. package/icons/playButton.png +0 -0
  17. package/icons/profilePicThumbnail.png +0 -0
  18. package/jest.config.js +10 -0
  19. package/module-descriptor.json +75 -0
  20. package/output/service-worker.js +0 -0
  21. package/package.json +146 -0
  22. package/src/components/incidents/ColumnChooser.js +37 -0
  23. package/src/components/incidents/CreateMedia.js +132 -0
  24. package/src/components/incidents/CreatePane.js +1215 -0
  25. package/src/components/incidents/CreatePane.test.js +138 -0
  26. package/src/components/incidents/CreateReport.js +102 -0
  27. package/src/components/incidents/DetailsPane.js +1267 -0
  28. package/src/components/incidents/DetailsPane.test.js +150 -0
  29. package/src/components/incidents/EditPane.js +2334 -0
  30. package/src/components/incidents/EditPane.test.js +187 -0
  31. package/src/components/incidents/GetDetails.js +55 -0
  32. package/src/components/incidents/GetListDQLinkIncident.js +81 -0
  33. package/src/components/incidents/GetListDynamicQuery.js +66 -0
  34. package/src/components/incidents/GetLocations.js +57 -0
  35. package/src/components/incidents/GetMedia.js +98 -0
  36. package/src/components/incidents/GetName.js +111 -0
  37. package/src/components/incidents/GetNameCreatedBy.js +94 -0
  38. package/src/components/incidents/GetOrgLocaleSettings.js +61 -0
  39. package/src/components/incidents/GetPatronGroups.js +52 -0
  40. package/src/components/incidents/GetSelf.js +65 -0
  41. package/src/components/incidents/GetSummary.js +110 -0
  42. package/src/components/incidents/IncidentTypeCard.js +53 -0
  43. package/src/components/incidents/IncidentTypeCard.test.js +133 -0
  44. package/src/components/incidents/IncidentsPaneset.js +810 -0
  45. package/src/components/incidents/IncidentsPaneset.test.js +128 -0
  46. package/src/components/incidents/LinkedIncident.js +86 -0
  47. package/src/components/incidents/ModalAddMedia.js +262 -0
  48. package/src/components/incidents/ModalAddMedia.test.js +97 -0
  49. package/src/components/incidents/ModalAttentionDecOfService.js +111 -0
  50. package/src/components/incidents/ModalCustomWitness.js +469 -0
  51. package/src/components/incidents/ModalCustomWitness.test.js +147 -0
  52. package/src/components/incidents/ModalCustomerDetails.js +480 -0
  53. package/src/components/incidents/ModalCustomerDetails.test.js +116 -0
  54. package/src/components/incidents/ModalDescribeCustomer.js +361 -0
  55. package/src/components/incidents/ModalDescribeCustomer.test.js +156 -0
  56. package/src/components/incidents/ModalDirtyFormWarn.js +62 -0
  57. package/src/components/incidents/ModalLinkIncident.js +1213 -0
  58. package/src/components/incidents/ModalLinkIncidentStyle.css +32 -0
  59. package/src/components/incidents/ModalSelectIncidentTypes.js +178 -0
  60. package/src/components/incidents/ModalSelectIncidentTypes.test.js +273 -0
  61. package/src/components/incidents/ModalSelectKnownCustomer.js +395 -0
  62. package/src/components/incidents/ModalSelectWitness.js +406 -0
  63. package/src/components/incidents/ModalSelectWitness.test.js +308 -0
  64. package/src/components/incidents/ModalStyle.css +44 -0
  65. package/src/components/incidents/ModalTrespass.js +741 -0
  66. package/src/components/incidents/ModalViewCustomerDetails.js +241 -0
  67. package/src/components/incidents/ModalViewMedia.js +86 -0
  68. package/src/components/incidents/ModalViewTrespass.js +210 -0
  69. package/src/components/incidents/ResultsPane.js +437 -0
  70. package/src/components/incidents/ResultsPane.test.js +120 -0
  71. package/src/components/incidents/SearchCustomerOrWitness.js +108 -0
  72. package/src/components/incidents/Thumbnail.js +72 -0
  73. package/src/components/incidents/ThumbnailMarkRemoval.js +38 -0
  74. package/src/components/incidents/ThumbnailSkeleton.js +30 -0
  75. package/src/components/incidents/ThumbnailStyles.js +49 -0
  76. package/src/components/incidents/ThumbnailTempPreSave.js +71 -0
  77. package/src/components/incidents/UpdateReport.js +84 -0
  78. package/src/components/incidents/__snapshots__/CreatePane.test.js.snap +3 -0
  79. package/src/components/incidents/__snapshots__/DetailsPane.test.js.snap +3 -0
  80. package/src/components/incidents/__snapshots__/EditPane.test.js.snap +3 -0
  81. package/src/components/incidents/__snapshots__/IncidentTypeCard.test.js.snap +3 -0
  82. package/src/components/incidents/__snapshots__/IncidentsPaneset.test.js.snap +3 -0
  83. package/src/components/incidents/__snapshots__/ModalAddMedia.test.js.snap +3 -0
  84. package/src/components/incidents/__snapshots__/ModalCustomerDetails.test.js.snap +3 -0
  85. package/src/components/incidents/__snapshots__/ModalSelectWitness.test.js.snap +3 -0
  86. package/src/components/incidents/__snapshots__/ResultsPane.test.js.snap +3 -0
  87. package/src/components/incidents/helpers/ProfilePicture/ProfilePicture.css +5 -0
  88. package/src/components/incidents/helpers/ProfilePicture/ProfilePicture.js +51 -0
  89. package/src/components/incidents/helpers/ProfilePicture/isAValidURL.js +3 -0
  90. package/src/components/incidents/helpers/ProfilePicture/useProfilePicture.js +127 -0
  91. package/src/components/incidents/helpers/buildQueryString.js +28 -0
  92. package/src/components/incidents/helpers/cleanFormValues.js +53 -0
  93. package/src/components/incidents/helpers/computeEditedCustomers.js +124 -0
  94. package/src/components/incidents/helpers/convertDateIgnoringTZ.js +8 -0
  95. package/src/components/incidents/helpers/convertUTCISOToLocalePrettyTime.js +15 -0
  96. package/src/components/incidents/helpers/convertUTCISOToPrettyDate.js +19 -0
  97. package/src/components/incidents/helpers/decodeParamsToForm.js +20 -0
  98. package/src/components/incidents/helpers/deepNormalizeForComparison.js +39 -0
  99. package/src/components/incidents/helpers/extractFilterString.js +12 -0
  100. package/src/components/incidents/helpers/formatDateAndTimeToUTCISO.js +14 -0
  101. package/src/components/incidents/helpers/formatDateToUTCISO.js +14 -0
  102. package/src/components/incidents/helpers/formatTimeToUTCISO.js +28 -0
  103. package/src/components/incidents/helpers/getCurrentTime.js +20 -0
  104. package/src/components/incidents/helpers/getTodayDate.js +12 -0
  105. package/src/components/incidents/helpers/handlebarsHelpers.js +148 -0
  106. package/src/components/incidents/helpers/hasFormChangedAtCreate.js +50 -0
  107. package/src/components/incidents/helpers/hasTopLevelChangeAffectedDeclaration.js +90 -0
  108. package/src/components/incidents/helpers/hasTopLevelFormChanged.js +111 -0
  109. package/src/components/incidents/helpers/identifyCurrentTrespassDocs.js +109 -0
  110. package/src/components/incidents/helpers/isSameHtml.js +13 -0
  111. package/src/components/incidents/helpers/isValidDateFormat.js +14 -0
  112. package/src/components/incidents/helpers/isValidTimeInput.js +11 -0
  113. package/src/components/incidents/helpers/isValidUTCTimeFormat.js +14 -0
  114. package/src/components/incidents/helpers/parseMMDDYYYY.js +7 -0
  115. package/src/components/incidents/helpers/parseQueryString.js +16 -0
  116. package/src/components/incidents/helpers/sortTrespassDocuments.js +44 -0
  117. package/src/components/incidents/helpers/stripHTML.js +11 -0
  118. package/src/components/incidents/helpers/trespassDocUtils.js +197 -0
  119. package/src/components/incidents/helpers/validateTrespassDetails.js +37 -0
  120. package/src/components/incidents/usePersistedColModalLink.js +70 -0
  121. package/src/components/incidents/usePersistedColumns.js +70 -0
  122. package/src/components/incidents/usePersistedSort.js +23 -0
  123. package/src/components/incidents/usePersistedSortModalLink.js +23 -0
  124. package/src/contexts/IncidentContext.js +433 -0
  125. package/src/index.js +61 -0
  126. package/src/routes/Application.js +13 -0
  127. package/src/settings/GetIncidentCategories.js +56 -0
  128. package/src/settings/GetIncidentTypesDetails.js +88 -0
  129. package/src/settings/GetIncidentTypesIds.js +74 -0
  130. package/src/settings/GetLocationsInService.js +54 -0
  131. package/src/settings/GetSingleCustomLocationDetails.js +60 -0
  132. package/src/settings/GetSingleIncidentTypeDetails.js +60 -0
  133. package/src/settings/GetTrespassReasons.js +67 -0
  134. package/src/settings/GetTrespassTemplates.js +51 -0
  135. package/src/settings/IncidentCategoriesPane.js +285 -0
  136. package/src/settings/IncidentCategoriesPane.test.js +229 -0
  137. package/src/settings/IncidentTypeDetailsPane.js +215 -0
  138. package/src/settings/IncidentTypeDetailsPane.test.js +220 -0
  139. package/src/settings/IncidentTypeEditPane.js +211 -0
  140. package/src/settings/IncidentTypeEditPane.test.js +170 -0
  141. package/src/settings/IncidentTypesPaneset.js +167 -0
  142. package/src/settings/IncidentTypesPaneset.test.js +124 -0
  143. package/src/settings/LocationInServiceEditPane.js +320 -0
  144. package/src/settings/LocationsPaneset.js +415 -0
  145. package/src/settings/LocationsPaneset.test.js +106 -0
  146. package/src/settings/ModalDeleteCategory.js +47 -0
  147. package/src/settings/ModalDeleteIncidentType.js +49 -0
  148. package/src/settings/ModalDeleteLocationInService.js +49 -0
  149. package/src/settings/ModalDeleteTrespassReason.js +49 -0
  150. package/src/settings/ModalPreviewTrespassDoc.js +65 -0
  151. package/src/settings/ModalTrespassDocTokens.js +83 -0
  152. package/src/settings/NewIncidentTypePane.js +182 -0
  153. package/src/settings/PutIncidentType.js +60 -0
  154. package/src/settings/PutLocationsInService.js +52 -0
  155. package/src/settings/PutTrespassReasons.js +61 -0
  156. package/src/settings/PutTrespassTemplate.js +50 -0
  157. package/src/settings/TrespassDoc.css +17 -0
  158. package/src/settings/TrespassDocDetailsPane.js +215 -0
  159. package/src/settings/TrespassDocEditPane.js +538 -0
  160. package/src/settings/TrespassDocPaneset.js +581 -0
  161. package/src/settings/TrespassReasonDetailsPane.js +171 -0
  162. package/src/settings/TrespassReasonEditPane.js +221 -0
  163. package/src/settings/TrespassReasonsPaneset.js +282 -0
  164. package/src/settings/__snapshots__/IncidentCategoriesPane.test.js.snap +3 -0
  165. package/src/settings/__snapshots__/IncidentTypeDetailsPane.test.js.snap +3 -0
  166. package/src/settings/__snapshots__/IncidentTypeEditPane.test.js.snap +3 -0
  167. package/src/settings/__snapshots__/IncidentTypesPaneset.test.js.snap +3 -0
  168. package/src/settings/__snapshots__/LocationsPaneset.test.js.snap +3 -0
  169. package/src/settings/data/exampleJSON.json +92 -0
  170. package/src/settings/data/templateTokens.js +396 -0
  171. package/src/settings/helpers/alphabetize.js +18 -0
  172. package/src/settings/helpers/getCategoryTitleById.js +13 -0
  173. package/src/settings/helpers/makeId.js +15 -0
  174. package/src/settings/index.js +48 -0
  175. package/stripes.config.js +10 -0
  176. package/test/jest/__mock__/index.js +8 -0
  177. package/test/jest/__mock__/intl.mock.js +27 -0
  178. package/test/jest/__mock__/stripes.mock.js +26 -0
  179. package/test/jest/__mock__/stripesComponents.mock.js +151 -0
  180. package/test/jest/__mock__/stripesConfig.mock.js +1 -0
  181. package/test/jest/__mock__/stripesCore.mock.js +9 -0
  182. package/test/jest/__mock__/stripesIcon.mock.js +5 -0
  183. package/test/jest/__mock__/stripesSmartComponents.mock.js +7 -0
  184. package/test/jest/__mock__/stripesUtils.mock.js +3 -0
  185. package/test/jest/eslintrc.js +12 -0
  186. package/test/jest/setupFiles.js +5 -0
  187. package/translations/ui-security-incident/en_US.json +542 -0
  188. package/ui-module-acceptance-criteria.md +34 -0
@@ -0,0 +1,581 @@
1
+
2
+ import React, { useState, useRef, useEffect } from 'react';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import DOMPurify from 'dompurify';
5
+ import { useHistory, Switch, Route } from 'react-router-dom';
6
+ import {
7
+ Button,
8
+ Checkbox,
9
+ Col,
10
+ Editor,
11
+ Icon,
12
+ MessageBanner,
13
+ MultiColumnList,
14
+ Pane,
15
+ Paneset,
16
+ Row,
17
+ TextField
18
+ } from '@folio/stripes/components';
19
+ import GetTrespassTemplates from './GetTrespassTemplates';
20
+ import PutTrespassTemplate from './PutTrespassTemplate';
21
+ import ModalTrespassDocTokens from './ModalTrespassDocTokens';
22
+ import ModalPreviewTrespassDoc from './ModalPreviewTrespassDoc';
23
+ import TrespassDocDetailsPane from './TrespassDocDetailsPane';
24
+ import TrespassDocEditPane from './TrespassDocEditPane';
25
+ import stripHTML from '../components/incidents/helpers/stripHTML';
26
+ import makeId from './helpers/makeId';
27
+ import { tokensArray, tokenValues } from './data/templateTokens';
28
+ import { useIncidents } from '../contexts/IncidentContext';
29
+
30
+
31
+ const TrespassDocPaneset = ({...props}) => {
32
+ const history = useHistory();
33
+ const {
34
+ trespassTemplates
35
+ } = useIncidents();
36
+
37
+ const [showTokensModal, setShowTokensModal] = useState(false);
38
+ const [selectedTokens, setSelectedTokens] = useState([]);
39
+ const [showPreviewModal, setShowPreviewModal] = useState(false);
40
+ const [previewContent, setPreviewContent] = useState('');
41
+ const [lastRange, setLastRange] = useState(null);
42
+ const [formattedConfigTemplates, setFormattedConfigTemplates] = useState('');
43
+ const [showErrorBanner, setShowErrorBanner] = useState(false);
44
+ const [isButtonDisabled, setIsButtonDisabled] = useState(false);
45
+ const [formData, setFormData] = useState({
46
+ id: '',
47
+ name: '',
48
+ active: false,
49
+ isDefault: false,
50
+ description: '',
51
+ templateValue: ''
52
+ })
53
+
54
+ const editorRef = useRef(null);
55
+
56
+ // save current quill current selection
57
+ const saveCurrentSelection = () => {
58
+ const quill = editorRef.current?.getEditor?.();
59
+ if (quill) {
60
+ const range = quill.getSelection();
61
+ if (range) {
62
+ setLastRange(range)
63
+ }
64
+ }
65
+ };
66
+
67
+ const handleInsertClick = () => {
68
+ saveCurrentSelection();
69
+ setShowTokensModal(true)
70
+ };
71
+
72
+ const handleInsertTokens = () => {
73
+ setShowTokensModal(false);
74
+ setTimeout(() => {
75
+ const quill = editorRef.current?.getEditor?.();
76
+ if (quill) {
77
+ quill.focus();
78
+ if (lastRange) {
79
+ quill.setSelection(lastRange);
80
+ const tokensText = selectedTokens.join(' ');
81
+ quill.insertText(lastRange.index, tokensText);
82
+ quill.setSelection(lastRange.index + tokensText.length)
83
+ } else {
84
+ const tokensText = selectedTokens.join(' ');
85
+ quill.insertText(quill.getLength(), tokensText);
86
+ quill.setSelection(quill.getLength());
87
+ }
88
+ }
89
+ }, 0)
90
+ // clear selected tokens for next use
91
+ setSelectedTokens([])
92
+ };
93
+
94
+
95
+ const getProcessedContentFromEditor = () => {
96
+ const quill = editorRef.current?.getEditor?.();
97
+ if (quill) {
98
+ console.log("quill version: ", quill.version)
99
+ const contentHtml = quill.root.innerHTML;
100
+ // const delta = quill.getContents();
101
+
102
+ let processedContent = contentHtml;
103
+ console.log("processedContent init as = contentHTML: ", processedContent)
104
+ // let processedContent = delta;
105
+
106
+ Object.keys(tokenValues).forEach((token) => {
107
+ const value = tokenValues[token];
108
+ // replace special chars as escaped
109
+ const regex = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
110
+ // replace regex with matched value
111
+ processedContent = processedContent.replace(regex, value);
112
+ })
113
+ console.log("processedContent, replaced: ", processedContent)
114
+ return processedContent;
115
+ } else {
116
+ console.error('quill instance not available')
117
+ return null;
118
+ }
119
+ };
120
+
121
+ const getContentHTMLForSave = () => {
122
+ const quill = editorRef.current?.getEditor?.();
123
+ if (quill) {
124
+ const contentHtml = quill.root.innerHTML;
125
+ return contentHtml
126
+ }
127
+ };
128
+
129
+ const handlePreviewClick = () => {
130
+ setShowPreviewModal(true);
131
+ saveCurrentSelection();
132
+ setPreviewContent(getProcessedContentFromEditor());
133
+ };
134
+
135
+ const closePreviewModal = () => {
136
+ setShowPreviewModal(false);
137
+ setTimeout(() => {
138
+ const quill = editorRef.current?.getEditor?.();
139
+ if (quill) {
140
+ quill.focus();
141
+ if (lastRange) {
142
+ // set to last range
143
+ quill.setSelection(lastRange);
144
+ } else {
145
+ // else the length of Editor
146
+ quill.setSelection(quill.getLength());
147
+ }
148
+ }
149
+ }, 0)
150
+ };
151
+
152
+ useEffect(() => {
153
+ const quill = editorRef.current?.getEditor?.();
154
+ if (quill) {
155
+ const handler = (range, oldRange, source) => {
156
+ console.log('selection-change:', { range, oldRange, source });
157
+ if (range) {
158
+ setLastRange(range)
159
+ };
160
+ // no update if lastRange is null
161
+ };
162
+
163
+ quill.on('selection-change', handler);
164
+
165
+ // clean up on unmount or when quill changes
166
+ return () => {
167
+ quill.off('selection-change', handler)
168
+ };
169
+ }
170
+ }, [editorRef.current]);
171
+
172
+ const handleChange = (event) => {
173
+ const { name, value } = event.target
174
+ if (name === 'active') {
175
+ setFormData((prev) => ({
176
+ ...prev,
177
+ [name]: event.target.checked
178
+ }));
179
+ } else if (name === 'isDefault') {
180
+ setFormData((prev) => ({
181
+ ...prev,
182
+ [name]: event.target.checked
183
+ }));
184
+ } else {
185
+ setFormData((prev) => ({
186
+ ...prev,
187
+ [name]: value
188
+ }));
189
+ };
190
+ };
191
+
192
+ const makeTrespassTemplateObj = (contentHTML) => {
193
+ const sanitizedContent = DOMPurify.sanitize(contentHTML);
194
+ return {
195
+ id: makeId(formData.name),
196
+ name: formData.name.trim(),
197
+ active: formData.isDefault ? true : formData.active,
198
+ isDefault: formData.isDefault,
199
+ description: formData.description.trim(),
200
+ templateValue: sanitizedContent
201
+ }
202
+ };
203
+
204
+ const resetForFreshTemplateInput = () => {
205
+ setPreviewContent('');
206
+ setFormData({
207
+ id: '',
208
+ name: '',
209
+ active: false,
210
+ description: '',
211
+ templateValue: ''
212
+ });
213
+ const quill = editorRef.current?.getEditor?.();
214
+ if (quill) {
215
+ quill.setText('');
216
+ quill.setContents([]);
217
+ quill.history.clear(); // clear undo stack
218
+ quill.setSelection(0); // cursor to first index
219
+ } else {
220
+ console.error('Quill instance not available');
221
+ };
222
+ setLastRange(null);
223
+ };
224
+
225
+ const handleSaveTemplate = () => {
226
+ const readyContentHTML = getContentHTMLForSave();
227
+ const newTemplate = makeTrespassTemplateObj(readyContentHTML);
228
+ const isDuplicate = trespassTemplates.some((template) => template.id === newTemplate.id);
229
+ if (!isDuplicate) {
230
+ let updatedTrespassTemplates = [];
231
+ if (newTemplate.isDefault) {
232
+ const removedPrevDefaultList = trespassTemplates.map((template) => {
233
+ // ensure if newTemplate.isDefault === true that it is the only default template
234
+ return { ...template, isDefault: false }
235
+ });
236
+ updatedTrespassTemplates = [...removedPrevDefaultList, newTemplate];
237
+ } else {
238
+ updatedTrespassTemplates = [...trespassTemplates, newTemplate]
239
+ };
240
+
241
+ const readyFormattedData = {
242
+ value: {
243
+ templates: updatedTrespassTemplates
244
+ }
245
+ };
246
+ setFormattedConfigTemplates({ data: readyFormattedData });
247
+ resetForFreshTemplateInput();
248
+ } else {
249
+ resetForFreshTemplateInput();
250
+ };
251
+ };
252
+
253
+ const handleShowDetails = (event, row) => {
254
+ const id = row.id
255
+ history.push(`/settings/incidents/trespass-template/${id}`);
256
+ };
257
+
258
+ const handleShowEdit = (templateId) => {
259
+ history.push(`/settings/incidents/trespass-template/${templateId}/edit`);
260
+ };
261
+
262
+ const handleCancelEdit = (templateId) => {
263
+ history.push(`/settings/incidents/trespass-template/${templateId}`);
264
+ };
265
+
266
+ const handleCloseEdit = (templateId) => {
267
+ history.push(`/settings/incidents/trespass-template/${templateId}`);
268
+ };
269
+
270
+ const handleMessageBannerEntered = () => {
271
+ setTimeout(() => {
272
+ // setShow(false)
273
+ setShowErrorBanner(false)
274
+ }, 2800)
275
+ };
276
+
277
+ // helper for isFormDataValid to check state not empty
278
+ // (at makeTrespassTemplateObj for PUT the value of getProcessedContentFromEditor()
279
+ // is used for templateValue not formData.templateValue)
280
+ const handleEditorChange = (content) => {
281
+ const sanitizedContent = DOMPurify.sanitize(content);
282
+ setFormData(prev => ({
283
+ ...prev,
284
+ templateValue: sanitizedContent
285
+ }))
286
+ };
287
+
288
+ const normalizeTemplateName = (name) => {
289
+ return name.toLowerCase().replace(/[\s\-_.,'"]/g, '');
290
+ };
291
+
292
+ const getNameIsUnique = () => {
293
+ const normalizedName = normalizeTemplateName(formData.name);
294
+ const isNameUnique = !trespassTemplates.some(
295
+ trespass => normalizeTemplateName(trespass.name) === normalizedName
296
+ );
297
+ return isNameUnique
298
+ };
299
+
300
+ const getValidationResults = () => {
301
+ const isNameNotEmpty = formData.name && formData.name.trim() !== '';
302
+ const nameIsUnique = getNameIsUnique();
303
+ const isEditorValid = stripHTML(formData.templateValue);
304
+ return isNameNotEmpty && nameIsUnique && isEditorValid;;
305
+ };
306
+
307
+ useEffect(() => {
308
+ if (!getNameIsUnique()) {
309
+ setShowErrorBanner(true)
310
+ } else {
311
+ setShowErrorBanner(false)
312
+ };
313
+ }, [formData, trespassTemplates]);
314
+
315
+ useEffect(() => {
316
+ setIsButtonDisabled(!getValidationResults())
317
+ }, [formData, trespassTemplates]);
318
+
319
+ const columnWidths = {
320
+ name: '175px',
321
+ isDefault: '80px',
322
+ active: '60px',
323
+ description: '225px'
324
+ };
325
+
326
+ const formatter = {
327
+ name: (item) => {
328
+ if (item.name.length > 28) {
329
+ return item.name.slice(0, 28) + "...";
330
+ } else {
331
+ return item.name;
332
+ }
333
+ },
334
+ isDefault: (item) => {
335
+ const isDefault = item.isDefault;
336
+ return isDefault ? <span style={{ color: 'green' }}>
337
+ <Icon icon='check-circle'></Icon>
338
+ </span>
339
+ : null
340
+ },
341
+ active: (item) => {
342
+ const isActive = item.active;
343
+ return isActive ? <span style={{ color: 'green' }}>
344
+ <Icon icon='check-circle'></Icon>
345
+ </span>
346
+ : <Icon icon='archive'></Icon>
347
+ },
348
+ description: (item) => {
349
+ if (item.description.length >= 28) {
350
+ return item.description.slice(0, 28) + "...";
351
+ } else {
352
+ return item.description;
353
+ }
354
+ }
355
+ };
356
+
357
+ const modules = {
358
+ toolbar: {
359
+ container: '#toolbar'
360
+ }
361
+ };
362
+
363
+ return (
364
+ <Paneset>
365
+ <Pane
366
+ defaultWidth="fill"
367
+ paneTitle={<FormattedMessage
368
+ id="settings.trespass-document-paneTitle"/>
369
+ }
370
+ >
371
+ <GetTrespassTemplates />
372
+ {formattedConfigTemplates &&
373
+ <PutTrespassTemplate
374
+ data={formattedConfigTemplates}
375
+ />}
376
+ {showTokensModal &&
377
+ <ModalTrespassDocTokens
378
+ setShowTokensModal={setShowTokensModal}
379
+ selectedTokens={selectedTokens}
380
+ setSelectedTokens={setSelectedTokens}
381
+ handleInsertTokens={handleInsertTokens}
382
+ tokensArray={tokensArray}
383
+ />}
384
+ {showPreviewModal &&
385
+ <ModalPreviewTrespassDoc
386
+ previewContent={previewContent}
387
+ closePreviewModal={closePreviewModal}
388
+ />}
389
+
390
+ <Row>
391
+ <Col xs={12}>
392
+ <MultiColumnList
393
+ contentData={trespassTemplates}
394
+ visibleColumns={[
395
+ 'name',
396
+ 'isDefault',
397
+ 'active',
398
+ 'description'
399
+ ]}
400
+ columnMapping={{
401
+ name: <FormattedMessage id="settings.trespass-document-columnmapping-name"/>,
402
+ isDefault: <FormattedMessage id="settings.trespass-document-columnmapping-default"/>,
403
+ active: <FormattedMessage id="settings.trespass-document-columnmapping-active"/>,
404
+ description: <FormattedMessage id="settings.trespass-document-columnmapping-description"/>
405
+ }}
406
+ columnWidths={columnWidths}
407
+ formatter={formatter}
408
+ onRowClick={handleShowDetails}
409
+ />
410
+ </Col>
411
+ </Row>
412
+
413
+ <Row style={{ marginTop: '25px', marginBottom: '25px' }}>
414
+ <Col xs={8}>
415
+ <div style={{ minHeight: '50px' }}>
416
+ <MessageBanner
417
+ onEntered={() => handleMessageBannerEntered()}
418
+ type="error"
419
+ show={showErrorBanner}>
420
+ <FormattedMessage id="settings.trespass-document-template.message-banner.name-must-be-unique"/>
421
+ </MessageBanner>
422
+ </div>
423
+ </Col>
424
+ </Row>
425
+
426
+ <Row style={{ marginTop: '45px' }}>
427
+ <Col xs={6}>
428
+ <TextField
429
+ required
430
+ label={<FormattedMessage id="settings.trespass-document-textfield-name"/>}
431
+ name='name'
432
+ value={formData.name}
433
+ onChange={handleChange}
434
+ />
435
+ </Col>
436
+ </Row>
437
+ <Row>
438
+ <Col xs={6}>
439
+ <TextField
440
+ label={<FormattedMessage id="settings.trespass-document-textfield-description"/>}
441
+ name='description'
442
+ value={formData.description}
443
+ onChange={handleChange}
444
+ />
445
+ </Col>
446
+ </Row>
447
+
448
+ <Row>
449
+ <Col xs={6}>
450
+ <Checkbox
451
+ label={<FormattedMessage id="settings.trespass-document-checkbox-active"/>}
452
+ name='active'
453
+ checked={formData.active}
454
+ onChange={handleChange}
455
+ />
456
+ </Col>
457
+ </Row>
458
+
459
+ <Row>
460
+ <Col xs={6}>
461
+ <Checkbox
462
+ label={<FormattedMessage id="settings.trespass-document-checkbox-isDefault"/>}
463
+ name='isDefault'
464
+ checked={formData.isDefault}
465
+ onChange={handleChange}
466
+ />
467
+ </Col>
468
+ </Row>
469
+
470
+ <Row style={{ marginTop: '20px'}}>
471
+ <Col xs={12}>
472
+ <div id='toolbar'>
473
+ {/* text formatting */}
474
+ <span className='ql-formats'>
475
+ <button className='ql-bold'></button>
476
+ <button className='ql-italic'></button>
477
+ <button className='ql-underline'></button>
478
+ <button className='ql-strike'></button>
479
+ </span>
480
+
481
+ {/* headers */}
482
+ <span className='ql-formats'>
483
+ <select className='ql-header'>
484
+ <option value='1'></option>
485
+ <option value='2'></option>
486
+ <option selected></option>
487
+ </select>
488
+ </span>
489
+
490
+ {/* subscript/superscript */}
491
+ <span className='ql-formats'>
492
+ <button className='ql-script' value='sub'></button>
493
+ <button className='ql-script' value='super'></button>
494
+ </span>
495
+
496
+ {/* lists */}
497
+ <span className='ql-formats'>
498
+ <button className='ql-list' value='ordered'></button>
499
+ <button className='ql-list' value='bullet'></button>
500
+ </span>
501
+
502
+ {/* indentation */}
503
+ <span className='ql-formats'>
504
+ <button className='ql-indent' value='-1'></button>
505
+ <button className='ql-indent' value='+1'></button>
506
+ </span>
507
+
508
+ {/* text alignment */}
509
+ <span className='ql-formats'>
510
+ <select className='ql-align'></select>
511
+ </span>
512
+
513
+ {/* custom button */}
514
+ <span className='ql-formats'>
515
+ <button
516
+ id='custom-button'
517
+ onClick={handleInsertClick}
518
+ >
519
+ Insert
520
+ </button>
521
+ </span>
522
+ </div>
523
+ <Editor
524
+ required
525
+ modules={modules}
526
+ editorRef={editorRef}
527
+ onChange={handleEditorChange}
528
+ />
529
+ </Col>
530
+ </Row>
531
+ <Row style={{ marginTop: '20px', marginBottom: '25px'}}>
532
+ <Col xs={4}>
533
+ <Button onClick={handlePreviewClick}>
534
+ <FormattedMessage
535
+ id="settings.trespass-document-preview-button"
536
+ />
537
+ </Button>
538
+ </Col>
539
+ <Col xs={4}>
540
+ <Button
541
+ buttonStyle='primary'
542
+ disabled={isButtonDisabled}
543
+ onClick={handleSaveTemplate}>
544
+ <FormattedMessage
545
+ id="save-and-close-button"
546
+ />
547
+ </Button>
548
+ </Col>
549
+ </Row>
550
+ </Pane>
551
+
552
+ <Switch>
553
+ <Route
554
+ exact
555
+ path="/settings/incidents/trespass-template/:id"
556
+ render={(props) => (
557
+ <TrespassDocDetailsPane
558
+ {...props}
559
+ // handleCloseDetails={handleCloseDetails}
560
+
561
+ handleShowEdit={handleShowEdit}
562
+ />
563
+ )}/>
564
+ <Route
565
+ exact
566
+ path="/settings/incidents/trespass-template/:id/edit"
567
+ render={(props) => (
568
+ <TrespassDocEditPane
569
+ handleCancelEdit={handleCancelEdit}
570
+ handleCloseEdit={handleCloseEdit}
571
+ {...props}
572
+
573
+ />
574
+ )}/>
575
+ </Switch>
576
+ </Paneset>
577
+ )
578
+
579
+ }
580
+
581
+ export default TrespassDocPaneset;