@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,538 @@
|
|
|
1
|
+
import React, { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import DOMPurify from 'dompurify';
|
|
3
|
+
import { FormattedMessage } from 'react-intl';
|
|
4
|
+
import { useParams, useHistory } from 'react-router-dom';
|
|
5
|
+
import {
|
|
6
|
+
AccordionSet,
|
|
7
|
+
Accordion,
|
|
8
|
+
Button,
|
|
9
|
+
Checkbox,
|
|
10
|
+
Col,
|
|
11
|
+
Dropdown,
|
|
12
|
+
DropdownMenu,
|
|
13
|
+
Editor,
|
|
14
|
+
Headline,
|
|
15
|
+
Icon,
|
|
16
|
+
Label,
|
|
17
|
+
List,
|
|
18
|
+
MessageBanner,
|
|
19
|
+
Pane,
|
|
20
|
+
PaneHeader,
|
|
21
|
+
PaneMenu,
|
|
22
|
+
Row,
|
|
23
|
+
TextField
|
|
24
|
+
} from '@folio/stripes/components';
|
|
25
|
+
import GetTrespassTemplates from './GetTrespassTemplates';
|
|
26
|
+
import PutTrespassTemplate from './PutTrespassTemplate';
|
|
27
|
+
import ModalTrespassDocTokens from './ModalTrespassDocTokens';
|
|
28
|
+
import ModalPreviewTrespassDoc from './ModalPreviewTrespassDoc';
|
|
29
|
+
import stripHTML from '../components/incidents/helpers/stripHTML';
|
|
30
|
+
import DOMPurify from 'dompurify';
|
|
31
|
+
import { tokensArray, tokenValues } from './data/templateTokens';
|
|
32
|
+
import { useIncidents } from '../contexts/IncidentContext';
|
|
33
|
+
|
|
34
|
+
const TrespassDocEditPane = ({handleCancelEdit, handleCloseEdit, ...props}) => {
|
|
35
|
+
const { id } = useParams();
|
|
36
|
+
const history = useHistory();
|
|
37
|
+
const {
|
|
38
|
+
trespassTemplates
|
|
39
|
+
} = useIncidents();
|
|
40
|
+
|
|
41
|
+
const [showTokensModal, setShowTokensModal] = useState(false);
|
|
42
|
+
const [selectedTokens, setSelectedTokens] = useState([]);
|
|
43
|
+
const [showPreviewModal, setShowPreviewModal] = useState(false);
|
|
44
|
+
const [previewContent, setPreviewContent] = useState('');
|
|
45
|
+
const [lastRange, setLastRange] = useState(null);
|
|
46
|
+
const [formattedConfigTemplates, setFormattedConfigTemplates] = useState(null);
|
|
47
|
+
const [showErrorBanner, setShowErrorBanner] = useState(false);
|
|
48
|
+
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
|
|
49
|
+
const [originalName, setOriginalName] = useState('');
|
|
50
|
+
const [formData, setFormData] = useState({
|
|
51
|
+
id: '',
|
|
52
|
+
name: '',
|
|
53
|
+
active: false,
|
|
54
|
+
isDefault: false,
|
|
55
|
+
description: '',
|
|
56
|
+
templateValue: ''
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (trespassTemplates && trespassTemplates.length > 0) {
|
|
61
|
+
const foundTemplate = trespassTemplates.find((template) => template.id === id);
|
|
62
|
+
if (foundTemplate) {
|
|
63
|
+
setFormData({
|
|
64
|
+
id: foundTemplate.id,
|
|
65
|
+
name: foundTemplate.name || '',
|
|
66
|
+
active: foundTemplate.active || false,
|
|
67
|
+
isDefault: foundTemplate.isDefault || false,
|
|
68
|
+
description: foundTemplate.description || '', // not required
|
|
69
|
+
templateValue: foundTemplate.templateValue || ''
|
|
70
|
+
});
|
|
71
|
+
setOriginalName(foundTemplate.name || '');
|
|
72
|
+
setPreviewContent(foundTemplate.templateValue);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(`template with id ${id} not found`);
|
|
75
|
+
setFormData({
|
|
76
|
+
name: '',
|
|
77
|
+
active: '',
|
|
78
|
+
description: '',
|
|
79
|
+
templateValue: ''
|
|
80
|
+
});
|
|
81
|
+
setPreviewContent('');
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}, [id, trespassTemplates]);
|
|
85
|
+
|
|
86
|
+
const editorRef = useRef(null);
|
|
87
|
+
|
|
88
|
+
// save current quill current selection
|
|
89
|
+
const saveCurrentSelection = () => {
|
|
90
|
+
const quill = editorRef.current?.getEditor?.();
|
|
91
|
+
if (quill) {
|
|
92
|
+
const range = quill.getSelection();
|
|
93
|
+
if (range) {
|
|
94
|
+
setLastRange(range)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const handleInsertClick = () => {
|
|
100
|
+
saveCurrentSelection();
|
|
101
|
+
setShowTokensModal(true)
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const handleInsertTokens = () => {
|
|
105
|
+
// handleTokenSelect(selectedTokens);
|
|
106
|
+
setShowTokensModal(false);
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
const quill = editorRef.current?.getEditor?.();
|
|
109
|
+
if (quill) {
|
|
110
|
+
quill.focus();
|
|
111
|
+
if (lastRange) {
|
|
112
|
+
quill.setSelection(lastRange);
|
|
113
|
+
const tokensText = selectedTokens.join(' ');
|
|
114
|
+
quill.insertText(lastRange.index, tokensText);
|
|
115
|
+
quill.setSelection(lastRange.index + tokensText.length)
|
|
116
|
+
} else {
|
|
117
|
+
const tokensText = selectedTokens.join(' ');
|
|
118
|
+
quill.insertText(quill.getLength(), tokensText);
|
|
119
|
+
quill.setSelection(quill.getLength());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}, 0)
|
|
123
|
+
// clear selected tokens for next use
|
|
124
|
+
setSelectedTokens([])
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const getContentForSaveUpdate = () => {
|
|
128
|
+
const quill = editorRef.current?.getEditor?.();
|
|
129
|
+
if (quill) {
|
|
130
|
+
const contentHtml = quill.root.innerHTML;
|
|
131
|
+
// console.log("@getContentForSaveUpdate - contentHtml: ", contentHtml)
|
|
132
|
+
return contentHtml;
|
|
133
|
+
} else {
|
|
134
|
+
console.error('quill instance not available')
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const getProcessedContent = () => {
|
|
140
|
+
const quill = editorRef.current?.getEditor?.();
|
|
141
|
+
if (quill) {
|
|
142
|
+
const contentHtml = quill.root.innerHTML;
|
|
143
|
+
let processedContent = contentHtml;
|
|
144
|
+
|
|
145
|
+
Object.keys(tokenValues).forEach((token) => {
|
|
146
|
+
const value = tokenValues[token];
|
|
147
|
+
// replace special chars as escaped
|
|
148
|
+
const regex = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
149
|
+
// replace regex with matched value
|
|
150
|
+
processedContent = processedContent.replace(regex, value);
|
|
151
|
+
})
|
|
152
|
+
return processedContent;
|
|
153
|
+
} else {
|
|
154
|
+
console.error('quill instance not available')
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const handlePreviewClick = () => {
|
|
160
|
+
setShowPreviewModal(true);
|
|
161
|
+
saveCurrentSelection();
|
|
162
|
+
setPreviewContent(getProcessedContent());
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const closePreviewModal = () => {
|
|
166
|
+
setShowPreviewModal(false);
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
const quill = editorRef.current?.getEditor?.();
|
|
169
|
+
if (quill) {
|
|
170
|
+
quill.focus();
|
|
171
|
+
if (lastRange) {
|
|
172
|
+
// set to last range
|
|
173
|
+
quill.setSelection(lastRange);
|
|
174
|
+
} else {
|
|
175
|
+
// else the length of Editor
|
|
176
|
+
quill.setSelection(quill.getLength());
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}, 0)
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
const quill = editorRef.current?.getEditor?.();
|
|
184
|
+
if (quill) {
|
|
185
|
+
const handler = (range, oldRange, source) => {
|
|
186
|
+
// console.log('selection-change:', { range, oldRange, source });
|
|
187
|
+
if (range) {
|
|
188
|
+
setLastRange(range)
|
|
189
|
+
};
|
|
190
|
+
// no update if lastRange is null
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
quill.on('selection-change', handler);
|
|
194
|
+
|
|
195
|
+
// clean up on unmount or when quill changes
|
|
196
|
+
return () => {
|
|
197
|
+
quill.off('selection-change', handler)
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}, [editorRef.current]);
|
|
201
|
+
|
|
202
|
+
const handleChange = (event) => {
|
|
203
|
+
const { name, value } = event.target
|
|
204
|
+
if (name === 'active') {
|
|
205
|
+
setFormData((prev) => ({
|
|
206
|
+
...prev,
|
|
207
|
+
[name]: event.target.checked
|
|
208
|
+
}));
|
|
209
|
+
} else if (name === 'isDefault') {
|
|
210
|
+
setFormData((prev) => ({
|
|
211
|
+
...prev,
|
|
212
|
+
[name]: event.target.checked
|
|
213
|
+
}));
|
|
214
|
+
} else {
|
|
215
|
+
setFormData((prev) => ({
|
|
216
|
+
...prev,
|
|
217
|
+
[name]: value
|
|
218
|
+
}));
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const readyEditedTrespassTemplateObj = (processedTemplate) => {
|
|
223
|
+
const sanitizedContent = DOMPurify.sanitize(processedTemplate);
|
|
224
|
+
return {
|
|
225
|
+
id: formData.id,
|
|
226
|
+
name: formData.name.trim(),
|
|
227
|
+
active: formData.isDefault ? true : formData.active,
|
|
228
|
+
isDefault: formData.isDefault,
|
|
229
|
+
description: formData.description.trim(),
|
|
230
|
+
templateValue: sanitizedContent
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const resetForFreshTemplateInput = () => {
|
|
235
|
+
setPreviewContent('');
|
|
236
|
+
setFormData({
|
|
237
|
+
id: '',
|
|
238
|
+
name: '',
|
|
239
|
+
active: false,
|
|
240
|
+
isDefault: false,
|
|
241
|
+
description: '',
|
|
242
|
+
templateValue: ''
|
|
243
|
+
});
|
|
244
|
+
const quill = editorRef.current?.getEditor?.();
|
|
245
|
+
if (quill) {
|
|
246
|
+
quill.setText('');
|
|
247
|
+
quill.setContents([]);
|
|
248
|
+
quill.history.clear(); // clear undo stack
|
|
249
|
+
quill.setSelection(0); // cursor to first index
|
|
250
|
+
} else {
|
|
251
|
+
console.error('Quill instance not available');
|
|
252
|
+
};
|
|
253
|
+
setLastRange(null);
|
|
254
|
+
handleCloseEdit(id)
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const handleSaveTemplate = (e) => {
|
|
258
|
+
if (e) e.preventDefault();
|
|
259
|
+
// obtain ready template
|
|
260
|
+
const readyTemplate = getContentForSaveUpdate();
|
|
261
|
+
// console.log("readyTemplate: ", readyTemplate)
|
|
262
|
+
// make ready template object
|
|
263
|
+
const editedTemplate = readyEditedTrespassTemplateObj(readyTemplate);
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
// init array
|
|
267
|
+
let updatedTrespassTemplates = [];
|
|
268
|
+
// if edited template is the default template
|
|
269
|
+
if (editedTemplate.isDefault) {
|
|
270
|
+
// convert previous template of default, if any, to 'false' (not default)
|
|
271
|
+
const removedPrevDefaultList = trespassTemplates.map((template) => {
|
|
272
|
+
// ensure if editedTemplate.isDefault === true that it is the only default template
|
|
273
|
+
return { ...template, isDefault: false }
|
|
274
|
+
});
|
|
275
|
+
// merge edited template with updated list
|
|
276
|
+
updatedTrespassTemplates = [...removedPrevDefaultList, editedTemplate];
|
|
277
|
+
} else {
|
|
278
|
+
updatedTrespassTemplates = [...trespassTemplates, editedTemplate]
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// remove prev version of newly edited template (it's not an update, its a replace)
|
|
282
|
+
const filteredList = updatedTrespassTemplates.filter((template) => template.id !== editedTemplate.id );
|
|
283
|
+
|
|
284
|
+
const readyFormattedData = {
|
|
285
|
+
value: {
|
|
286
|
+
templates: [...filteredList, editedTemplate]
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
console.log("readyFormattedData -->", JSON.stringify(readyFormattedData, null, 2));
|
|
291
|
+
|
|
292
|
+
setFormattedConfigTemplates({ data: readyFormattedData });
|
|
293
|
+
setTimeout(() => {
|
|
294
|
+
resetForFreshTemplateInput();
|
|
295
|
+
}, 800);
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const handleMessageBannerEntered = () => {
|
|
299
|
+
setTimeout(() => {
|
|
300
|
+
setShowErrorBanner(false)
|
|
301
|
+
}, 2800)
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// helper for useEffect to check state not empty
|
|
305
|
+
// (at readyEditedTrespassTemplateObj for PUT the value of getProcessedContent()
|
|
306
|
+
// is used for templateValue not formData.templateValue)
|
|
307
|
+
const handleEditorChange = (content) => {
|
|
308
|
+
const sanitizedContent = DOMPurify.sanitize(content);
|
|
309
|
+
setFormData(prev => ({
|
|
310
|
+
...prev,
|
|
311
|
+
templateValue: sanitizedContent
|
|
312
|
+
}))
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const normalizeTemplateName = (name) => {
|
|
316
|
+
return name.toLowerCase().replace(/[\s\-_.,'"]/g, '');
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
const isNameNotEmpty = formData.name && formData.name.trim() !== '';
|
|
321
|
+
const normalizedCurrentName = normalizeTemplateName(formData.name);
|
|
322
|
+
const normalizedOriginalName = normalizeTemplateName(originalName);
|
|
323
|
+
const isNameChanged = normalizedCurrentName !== normalizedOriginalName;
|
|
324
|
+
const isNameUnique = !trespassTemplates.some((template) =>
|
|
325
|
+
template.id !== formData.id &&
|
|
326
|
+
normalizeTemplateName(template.name) === normalizedCurrentName
|
|
327
|
+
);
|
|
328
|
+
const isEditorValid = stripHTML(formData.templateValue).trim() !== '';
|
|
329
|
+
setShowErrorBanner(isNameNotEmpty && !isNameUnique && isNameChanged);
|
|
330
|
+
const isValid = isNameNotEmpty && isEditorValid && (isNameUnique || !isNameChanged);
|
|
331
|
+
|
|
332
|
+
setIsButtonDisabled(!isValid);
|
|
333
|
+
}, [formData, trespassTemplates, originalName]);
|
|
334
|
+
|
|
335
|
+
const modules = {
|
|
336
|
+
toolbar: {
|
|
337
|
+
container: '#toolbar-edit'
|
|
338
|
+
},
|
|
339
|
+
clipboard: {
|
|
340
|
+
matchVisual: false
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const formats = [
|
|
345
|
+
'bold', 'italic', 'underline', 'strike',
|
|
346
|
+
'header', 'script', 'list', 'bullet',
|
|
347
|
+
'indent', 'align',
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
const templateName = formData.name;
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<Pane
|
|
354
|
+
dismissible
|
|
355
|
+
onClose={() => handleCancelEdit(id)}
|
|
356
|
+
defaultWidth="100%"
|
|
357
|
+
paneTitle={<FormattedMessage
|
|
358
|
+
id="settings.trespass-document-edit-paneTitle"
|
|
359
|
+
values={{ templateName }}
|
|
360
|
+
/>}
|
|
361
|
+
{...props}>
|
|
362
|
+
|
|
363
|
+
<GetTrespassTemplates />
|
|
364
|
+
|
|
365
|
+
{formattedConfigTemplates &&
|
|
366
|
+
<PutTrespassTemplate
|
|
367
|
+
data={formattedConfigTemplates}
|
|
368
|
+
/>}
|
|
369
|
+
|
|
370
|
+
{showTokensModal &&
|
|
371
|
+
<ModalTrespassDocTokens
|
|
372
|
+
setShowTokensModal={setShowTokensModal}
|
|
373
|
+
selectedTokens={selectedTokens}
|
|
374
|
+
setSelectedTokens={setSelectedTokens}
|
|
375
|
+
handleInsertTokens={handleInsertTokens}
|
|
376
|
+
tokensArray={tokensArray}
|
|
377
|
+
/>}
|
|
378
|
+
|
|
379
|
+
{showPreviewModal &&
|
|
380
|
+
<ModalPreviewTrespassDoc
|
|
381
|
+
previewContent={previewContent}
|
|
382
|
+
closePreviewModal={closePreviewModal}
|
|
383
|
+
/>}
|
|
384
|
+
|
|
385
|
+
<AccordionSet>
|
|
386
|
+
<Accordion label={<FormattedMessage
|
|
387
|
+
id="details-pane.accordion-label.details"/>}
|
|
388
|
+
>
|
|
389
|
+
<Row>
|
|
390
|
+
<Col xs={6}>
|
|
391
|
+
<TextField
|
|
392
|
+
required
|
|
393
|
+
label={<FormattedMessage id="settings.trespass-document-edit-textfield-name-label"/>}
|
|
394
|
+
name='name'
|
|
395
|
+
value={formData.name}
|
|
396
|
+
onChange={handleChange}
|
|
397
|
+
/>
|
|
398
|
+
</Col>
|
|
399
|
+
</Row>
|
|
400
|
+
<Row>
|
|
401
|
+
<Col xs={6}>
|
|
402
|
+
<TextField
|
|
403
|
+
label={<FormattedMessage id="settings.trespass-document-edit-textfield-description-label"/>}
|
|
404
|
+
name='description'
|
|
405
|
+
value={formData.description}
|
|
406
|
+
onChange={handleChange}
|
|
407
|
+
/>
|
|
408
|
+
</Col>
|
|
409
|
+
</Row>
|
|
410
|
+
<Row>
|
|
411
|
+
<Col xs={6}>
|
|
412
|
+
<Checkbox
|
|
413
|
+
label={<FormattedMessage id="settings.trespass-document-edit-checkbox-active-label"/>}
|
|
414
|
+
name='active'
|
|
415
|
+
checked={formData.active}
|
|
416
|
+
onChange={handleChange}
|
|
417
|
+
/>
|
|
418
|
+
</Col>
|
|
419
|
+
</Row>
|
|
420
|
+
<Row>
|
|
421
|
+
<Col xs={6}>
|
|
422
|
+
<Checkbox
|
|
423
|
+
label={<FormattedMessage id="settings.trespass-document-edit-checkbox-default-label"/>}
|
|
424
|
+
name='isDefault'
|
|
425
|
+
checked={formData.isDefault}
|
|
426
|
+
onChange={handleChange}
|
|
427
|
+
/>
|
|
428
|
+
</Col>
|
|
429
|
+
</Row>
|
|
430
|
+
<Row style={{ marginTop: '25px', marginBottom: '25px' }}>
|
|
431
|
+
<Col xs={8}>
|
|
432
|
+
<div style={{ minHeight: '50px' }}>
|
|
433
|
+
<MessageBanner
|
|
434
|
+
onEntered={() => handleMessageBannerEntered()}
|
|
435
|
+
type="error"
|
|
436
|
+
show={showErrorBanner}>
|
|
437
|
+
<FormattedMessage id="settings.trespass-document-template.message-banner.name-must-be-unique"/>
|
|
438
|
+
</MessageBanner>
|
|
439
|
+
</div>
|
|
440
|
+
</Col>
|
|
441
|
+
</Row>
|
|
442
|
+
</Accordion>
|
|
443
|
+
|
|
444
|
+
<Accordion label='Document'>
|
|
445
|
+
<Row style={{ marginTop: '20px'}}>
|
|
446
|
+
<Col xs={12}>
|
|
447
|
+
<div id='toolbar-edit'>
|
|
448
|
+
{/* text formatting */}
|
|
449
|
+
<span className='ql-formats'>
|
|
450
|
+
<button className='ql-bold'></button>
|
|
451
|
+
<button className='ql-italic'></button>
|
|
452
|
+
<button className='ql-underline'></button>
|
|
453
|
+
<button className='ql-strike'></button>
|
|
454
|
+
</span>
|
|
455
|
+
|
|
456
|
+
{/* headers */}
|
|
457
|
+
<span className='ql-formats'>
|
|
458
|
+
<select className='ql-header'>
|
|
459
|
+
<option value='1'></option>
|
|
460
|
+
<option value='2'></option>
|
|
461
|
+
<option selected></option>
|
|
462
|
+
</select>
|
|
463
|
+
</span>
|
|
464
|
+
|
|
465
|
+
{/* subscript/superscript */}
|
|
466
|
+
<span className='ql-formats'>
|
|
467
|
+
<button className='ql-script' value='sub'></button>
|
|
468
|
+
<button className='ql-script' value='super'></button>
|
|
469
|
+
</span>
|
|
470
|
+
|
|
471
|
+
{/* lists */}
|
|
472
|
+
<span className='ql-formats'>
|
|
473
|
+
<button className='ql-list' value='ordered'></button>
|
|
474
|
+
<button className='ql-list' value='bullet'></button>
|
|
475
|
+
</span>
|
|
476
|
+
|
|
477
|
+
{/* indentation */}
|
|
478
|
+
<span className='ql-formats'>
|
|
479
|
+
<button className='ql-indent' value='-1'></button>
|
|
480
|
+
<button className='ql-indent' value='+1'></button>
|
|
481
|
+
</span>
|
|
482
|
+
|
|
483
|
+
{/* text alignment */}
|
|
484
|
+
<span className='ql-formats'>
|
|
485
|
+
<select className='ql-align'>
|
|
486
|
+
<option selected></option>
|
|
487
|
+
<option value='center'></option>
|
|
488
|
+
<option value='right'></option>
|
|
489
|
+
<option value='justify'></option>
|
|
490
|
+
</select>
|
|
491
|
+
</span>
|
|
492
|
+
|
|
493
|
+
{/* custom button */}
|
|
494
|
+
<span className='ql-formats'>
|
|
495
|
+
<button
|
|
496
|
+
id='custom-button'
|
|
497
|
+
onClick={handleInsertClick}
|
|
498
|
+
>
|
|
499
|
+
<FormattedMessage id="settings.trespass-document-edit-insert-button"/>
|
|
500
|
+
</button>
|
|
501
|
+
</span>
|
|
502
|
+
</div>
|
|
503
|
+
<Editor
|
|
504
|
+
required
|
|
505
|
+
value={formData.templateValue}
|
|
506
|
+
formats={formats}
|
|
507
|
+
modules={modules}
|
|
508
|
+
editorRef={editorRef}
|
|
509
|
+
onChange={handleEditorChange}
|
|
510
|
+
style={{ whiteSpace: 'pre-wrap' }}
|
|
511
|
+
/>
|
|
512
|
+
</Col>
|
|
513
|
+
</Row>
|
|
514
|
+
|
|
515
|
+
<Row style={{ marginTop: '20px'}}>
|
|
516
|
+
<Col xs={4}>
|
|
517
|
+
<Button
|
|
518
|
+
onClick={handlePreviewClick}
|
|
519
|
+
>
|
|
520
|
+
<FormattedMessage id="settings.trespass-document-edit-preview-button"/>
|
|
521
|
+
</Button>
|
|
522
|
+
</Col>
|
|
523
|
+
<Col xs={4}>
|
|
524
|
+
<Button
|
|
525
|
+
buttonStyle='primary'
|
|
526
|
+
disabled={isButtonDisabled}
|
|
527
|
+
onClick={handleSaveTemplate}>
|
|
528
|
+
<FormattedMessage id="save-and-close-button"/>
|
|
529
|
+
</Button>
|
|
530
|
+
</Col>
|
|
531
|
+
</Row>
|
|
532
|
+
</Accordion>
|
|
533
|
+
</AccordionSet>
|
|
534
|
+
</Pane>
|
|
535
|
+
);
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
export default TrespassDocEditPane;
|