@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.
- package/.eslintrc +32 -0
- package/.github/workflows/CODEOWNERS +8 -0
- package/.github/workflows/pr-validation.yml +44 -0
- package/.github/workflows/release.yml +64 -0
- package/.prettierrc +6 -0
- package/.stripesclirc +4 -0
- package/CHANGELOG.md +8 -0
- package/CONTRIBUTING.md +4 -0
- package/LICENSE +201 -0
- package/README.md +16 -0
- package/administrator-documentation/roles-and-permissions.md +65 -0
- package/administrator-documentation/track-settings-admin-guide-sketch.md +192 -0
- package/administrator-documentation/using-the-application.md +192 -0
- package/icons/app.png +0 -0
- package/icons/app.svg +1 -0
- package/icons/playButton.png +0 -0
- package/icons/profilePicThumbnail.png +0 -0
- package/jest.config.js +10 -0
- package/module-descriptor.json +75 -0
- package/output/service-worker.js +0 -0
- package/package.json +146 -0
- package/src/components/incidents/ColumnChooser.js +37 -0
- package/src/components/incidents/CreateMedia.js +132 -0
- package/src/components/incidents/CreatePane.js +1215 -0
- package/src/components/incidents/CreatePane.test.js +138 -0
- package/src/components/incidents/CreateReport.js +102 -0
- package/src/components/incidents/DetailsPane.js +1267 -0
- package/src/components/incidents/DetailsPane.test.js +150 -0
- package/src/components/incidents/EditPane.js +2334 -0
- package/src/components/incidents/EditPane.test.js +187 -0
- package/src/components/incidents/GetDetails.js +55 -0
- package/src/components/incidents/GetListDQLinkIncident.js +81 -0
- package/src/components/incidents/GetListDynamicQuery.js +66 -0
- package/src/components/incidents/GetLocations.js +57 -0
- package/src/components/incidents/GetMedia.js +98 -0
- package/src/components/incidents/GetName.js +111 -0
- package/src/components/incidents/GetNameCreatedBy.js +94 -0
- package/src/components/incidents/GetOrgLocaleSettings.js +61 -0
- package/src/components/incidents/GetPatronGroups.js +52 -0
- package/src/components/incidents/GetSelf.js +65 -0
- package/src/components/incidents/GetSummary.js +110 -0
- package/src/components/incidents/IncidentTypeCard.js +53 -0
- package/src/components/incidents/IncidentTypeCard.test.js +133 -0
- package/src/components/incidents/IncidentsPaneset.js +810 -0
- package/src/components/incidents/IncidentsPaneset.test.js +128 -0
- package/src/components/incidents/LinkedIncident.js +86 -0
- package/src/components/incidents/ModalAddMedia.js +262 -0
- package/src/components/incidents/ModalAddMedia.test.js +97 -0
- package/src/components/incidents/ModalAttentionDecOfService.js +111 -0
- package/src/components/incidents/ModalCustomWitness.js +469 -0
- package/src/components/incidents/ModalCustomWitness.test.js +147 -0
- package/src/components/incidents/ModalCustomerDetails.js +480 -0
- package/src/components/incidents/ModalCustomerDetails.test.js +116 -0
- package/src/components/incidents/ModalDescribeCustomer.js +361 -0
- package/src/components/incidents/ModalDescribeCustomer.test.js +156 -0
- package/src/components/incidents/ModalDirtyFormWarn.js +62 -0
- package/src/components/incidents/ModalLinkIncident.js +1213 -0
- package/src/components/incidents/ModalLinkIncidentStyle.css +32 -0
- package/src/components/incidents/ModalSelectIncidentTypes.js +178 -0
- package/src/components/incidents/ModalSelectIncidentTypes.test.js +273 -0
- package/src/components/incidents/ModalSelectKnownCustomer.js +395 -0
- package/src/components/incidents/ModalSelectWitness.js +406 -0
- package/src/components/incidents/ModalSelectWitness.test.js +308 -0
- package/src/components/incidents/ModalStyle.css +44 -0
- package/src/components/incidents/ModalTrespass.js +741 -0
- package/src/components/incidents/ModalViewCustomerDetails.js +241 -0
- package/src/components/incidents/ModalViewMedia.js +86 -0
- package/src/components/incidents/ModalViewTrespass.js +210 -0
- package/src/components/incidents/ResultsPane.js +437 -0
- package/src/components/incidents/ResultsPane.test.js +120 -0
- package/src/components/incidents/SearchCustomerOrWitness.js +108 -0
- package/src/components/incidents/Thumbnail.js +72 -0
- package/src/components/incidents/ThumbnailMarkRemoval.js +38 -0
- package/src/components/incidents/ThumbnailSkeleton.js +30 -0
- package/src/components/incidents/ThumbnailStyles.js +49 -0
- package/src/components/incidents/ThumbnailTempPreSave.js +71 -0
- package/src/components/incidents/UpdateReport.js +84 -0
- package/src/components/incidents/__snapshots__/CreatePane.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/DetailsPane.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/EditPane.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/IncidentTypeCard.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/IncidentsPaneset.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/ModalAddMedia.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/ModalCustomerDetails.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/ModalSelectWitness.test.js.snap +3 -0
- package/src/components/incidents/__snapshots__/ResultsPane.test.js.snap +3 -0
- package/src/components/incidents/helpers/ProfilePicture/ProfilePicture.css +5 -0
- package/src/components/incidents/helpers/ProfilePicture/ProfilePicture.js +51 -0
- package/src/components/incidents/helpers/ProfilePicture/isAValidURL.js +3 -0
- package/src/components/incidents/helpers/ProfilePicture/useProfilePicture.js +127 -0
- package/src/components/incidents/helpers/buildQueryString.js +28 -0
- package/src/components/incidents/helpers/cleanFormValues.js +53 -0
- package/src/components/incidents/helpers/computeEditedCustomers.js +124 -0
- package/src/components/incidents/helpers/convertDateIgnoringTZ.js +8 -0
- package/src/components/incidents/helpers/convertUTCISOToLocalePrettyTime.js +15 -0
- package/src/components/incidents/helpers/convertUTCISOToPrettyDate.js +19 -0
- package/src/components/incidents/helpers/decodeParamsToForm.js +20 -0
- package/src/components/incidents/helpers/deepNormalizeForComparison.js +39 -0
- package/src/components/incidents/helpers/extractFilterString.js +12 -0
- package/src/components/incidents/helpers/formatDateAndTimeToUTCISO.js +14 -0
- package/src/components/incidents/helpers/formatDateToUTCISO.js +14 -0
- package/src/components/incidents/helpers/formatTimeToUTCISO.js +28 -0
- package/src/components/incidents/helpers/getCurrentTime.js +20 -0
- package/src/components/incidents/helpers/getTodayDate.js +12 -0
- package/src/components/incidents/helpers/handlebarsHelpers.js +148 -0
- package/src/components/incidents/helpers/hasFormChangedAtCreate.js +50 -0
- package/src/components/incidents/helpers/hasTopLevelChangeAffectedDeclaration.js +90 -0
- package/src/components/incidents/helpers/hasTopLevelFormChanged.js +111 -0
- package/src/components/incidents/helpers/identifyCurrentTrespassDocs.js +109 -0
- package/src/components/incidents/helpers/isSameHtml.js +13 -0
- package/src/components/incidents/helpers/isValidDateFormat.js +14 -0
- package/src/components/incidents/helpers/isValidTimeInput.js +11 -0
- package/src/components/incidents/helpers/isValidUTCTimeFormat.js +14 -0
- package/src/components/incidents/helpers/parseMMDDYYYY.js +7 -0
- package/src/components/incidents/helpers/parseQueryString.js +16 -0
- package/src/components/incidents/helpers/sortTrespassDocuments.js +44 -0
- package/src/components/incidents/helpers/stripHTML.js +11 -0
- package/src/components/incidents/helpers/trespassDocUtils.js +197 -0
- package/src/components/incidents/helpers/validateTrespassDetails.js +37 -0
- package/src/components/incidents/usePersistedColModalLink.js +70 -0
- package/src/components/incidents/usePersistedColumns.js +70 -0
- package/src/components/incidents/usePersistedSort.js +23 -0
- package/src/components/incidents/usePersistedSortModalLink.js +23 -0
- package/src/contexts/IncidentContext.js +433 -0
- package/src/index.js +61 -0
- package/src/routes/Application.js +13 -0
- package/src/settings/GetIncidentCategories.js +56 -0
- package/src/settings/GetIncidentTypesDetails.js +88 -0
- package/src/settings/GetIncidentTypesIds.js +74 -0
- package/src/settings/GetLocationsInService.js +54 -0
- package/src/settings/GetSingleCustomLocationDetails.js +60 -0
- package/src/settings/GetSingleIncidentTypeDetails.js +60 -0
- package/src/settings/GetTrespassReasons.js +67 -0
- package/src/settings/GetTrespassTemplates.js +51 -0
- package/src/settings/IncidentCategoriesPane.js +285 -0
- package/src/settings/IncidentCategoriesPane.test.js +229 -0
- package/src/settings/IncidentTypeDetailsPane.js +215 -0
- package/src/settings/IncidentTypeDetailsPane.test.js +220 -0
- package/src/settings/IncidentTypeEditPane.js +211 -0
- package/src/settings/IncidentTypeEditPane.test.js +170 -0
- package/src/settings/IncidentTypesPaneset.js +167 -0
- package/src/settings/IncidentTypesPaneset.test.js +124 -0
- package/src/settings/LocationInServiceEditPane.js +320 -0
- package/src/settings/LocationsPaneset.js +415 -0
- package/src/settings/LocationsPaneset.test.js +106 -0
- package/src/settings/ModalDeleteCategory.js +47 -0
- package/src/settings/ModalDeleteIncidentType.js +49 -0
- package/src/settings/ModalDeleteLocationInService.js +49 -0
- package/src/settings/ModalDeleteTrespassReason.js +49 -0
- package/src/settings/ModalPreviewTrespassDoc.js +65 -0
- package/src/settings/ModalTrespassDocTokens.js +83 -0
- package/src/settings/NewIncidentTypePane.js +182 -0
- package/src/settings/PutIncidentType.js +60 -0
- package/src/settings/PutLocationsInService.js +52 -0
- package/src/settings/PutTrespassReasons.js +61 -0
- package/src/settings/PutTrespassTemplate.js +50 -0
- package/src/settings/TrespassDoc.css +17 -0
- package/src/settings/TrespassDocDetailsPane.js +215 -0
- package/src/settings/TrespassDocEditPane.js +538 -0
- package/src/settings/TrespassDocPaneset.js +581 -0
- package/src/settings/TrespassReasonDetailsPane.js +171 -0
- package/src/settings/TrespassReasonEditPane.js +221 -0
- package/src/settings/TrespassReasonsPaneset.js +282 -0
- package/src/settings/__snapshots__/IncidentCategoriesPane.test.js.snap +3 -0
- package/src/settings/__snapshots__/IncidentTypeDetailsPane.test.js.snap +3 -0
- package/src/settings/__snapshots__/IncidentTypeEditPane.test.js.snap +3 -0
- package/src/settings/__snapshots__/IncidentTypesPaneset.test.js.snap +3 -0
- package/src/settings/__snapshots__/LocationsPaneset.test.js.snap +3 -0
- package/src/settings/data/exampleJSON.json +92 -0
- package/src/settings/data/templateTokens.js +396 -0
- package/src/settings/helpers/alphabetize.js +18 -0
- package/src/settings/helpers/getCategoryTitleById.js +13 -0
- package/src/settings/helpers/makeId.js +15 -0
- package/src/settings/index.js +48 -0
- package/stripes.config.js +10 -0
- package/test/jest/__mock__/index.js +8 -0
- package/test/jest/__mock__/intl.mock.js +27 -0
- package/test/jest/__mock__/stripes.mock.js +26 -0
- package/test/jest/__mock__/stripesComponents.mock.js +151 -0
- package/test/jest/__mock__/stripesConfig.mock.js +1 -0
- package/test/jest/__mock__/stripesCore.mock.js +9 -0
- package/test/jest/__mock__/stripesIcon.mock.js +5 -0
- package/test/jest/__mock__/stripesSmartComponents.mock.js +7 -0
- package/test/jest/__mock__/stripesUtils.mock.js +3 -0
- package/test/jest/eslintrc.js +12 -0
- package/test/jest/setupFiles.js +5 -0
- package/translations/ui-security-incident/en_US.json +542 -0
- 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;
|