@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,320 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { FormattedMessage } from 'react-intl';
3
+ import { useParams } from 'react-router-dom';
4
+ import {
5
+ Button,
6
+ Col,
7
+ Icon,
8
+ Label,
9
+ MessageBanner,
10
+ MultiColumnList,
11
+ Pane,
12
+ PaneHeader,
13
+ Row,
14
+ TextArea,
15
+ TextField
16
+ } from '@folio/stripes/components';
17
+ import { useIncidents } from '../contexts/IncidentContext';
18
+ import PutLocationsInService from './PutLocationsInService';
19
+
20
+ const LocationInServiceEditPane = ({ handleCancelEdit, handleCloseEdit, ...props }) => {
21
+ const { id } = useParams();
22
+ const {
23
+ // institutionally set location names that are set outside of secinci app
24
+ // bringing in for comparison
25
+ locations,
26
+ // opted-in locations for service and the custom locations
27
+ locationsInService
28
+ } = useIncidents();
29
+
30
+ const [detailsData, setDetailsData] = useState();
31
+ const [showErrorBanner, setShowErrorBanner] = useState(false);
32
+ const [isButtonDisabled, setIsButtonDisabled] = useState(false);
33
+ const [isOriginOfInstitutionSettings, setIsOriginOfInsitutionSettings] = useState(false);
34
+ const [formattedData, setFormattedData] = useState(null);
35
+ const [formData, setFormData] = useState({
36
+ id: '',
37
+ location: '',
38
+ subLocations: [],
39
+ });
40
+ const [newSubLocation, setNewSubLocation] = useState({
41
+ name: '',
42
+ description: '',
43
+ });
44
+
45
+ const getSingleLocationInServiceDetails = (list) => {
46
+ const singleLocation = list.find((loc) => loc.id === id);
47
+ setDetailsData(singleLocation);
48
+ };
49
+
50
+ useEffect(() => {
51
+ if (locationsInService.length > 0) {
52
+ getSingleLocationInServiceDetails(locationsInService)
53
+ };
54
+ }, [locationsInService]);
55
+
56
+ // prevent an institutionally named location from being re-named in update by locking the TextField component (can only update a custom location name)
57
+ // although the update api would not touch the institutionally set location name directly, we do not want overlap of names, or typos generated, etc
58
+ useEffect(() => {
59
+ if(detailsData) {
60
+ const isOriginOfInstition = locations.some((loc) => loc === detailsData.location);
61
+ if(isOriginOfInstition) {
62
+ setIsOriginOfInsitutionSettings(true)
63
+ };
64
+ };
65
+ }, [detailsData, locations])
66
+
67
+ useEffect(() => {
68
+ if (detailsData) {
69
+ setFormData({
70
+ id: detailsData.id,
71
+ location: detailsData.location || '',
72
+ subLocations: detailsData.subLocations || [],
73
+ });
74
+ }
75
+ }, [detailsData]);
76
+
77
+ const handleRemoveSubLocation = (index) => {
78
+ const updatedSubLocations = formData.subLocations.filter(
79
+ (_, idx) => idx !== index
80
+ );
81
+ setFormData({ ...formData, subLocations: updatedSubLocations });
82
+ };
83
+
84
+ const handleMessageBannerEntered = () => {
85
+ setTimeout(() => {
86
+ setShowErrorBanner(false)
87
+ }, 2800)
88
+ };
89
+
90
+ const normalizeLocationName = (name) => {
91
+ return name.toLowerCase().replace(/[\s\-_.,'"]/g, '');
92
+ };
93
+
94
+ const handleLocationInput = (event) => {
95
+ const input = event.target.value;
96
+ const normalizedInput = normalizeLocationName(input);
97
+ const isNotNameUnique = locationsInService.some(loc => loc.location !== detailsData.location && normalizeLocationName(loc.location) === normalizedInput)
98
+
99
+ setFormData({ ...formData, location: input })
100
+
101
+ setShowErrorBanner(isNotNameUnique);
102
+ setIsButtonDisabled(input.trim() === '' || isNotNameUnique)
103
+ };
104
+
105
+ const handleAddSubLocation = () => {
106
+ if (newSubLocation && newSubLocation.name.trim() !== '') {
107
+ const updatedSubLocations = [...formData.subLocations, newSubLocation];
108
+ setFormData({ ...formData, subLocations: updatedSubLocations });
109
+ setTimeout(() => {
110
+ setNewSubLocation({ name: '', description: '' })
111
+ }, 200);
112
+ }
113
+ };
114
+
115
+ let endOflistTotal = 0;
116
+ useEffect(() => {
117
+ if (detailsData) {
118
+ endOflistTotal = detailsData.subLocations.length;
119
+ };
120
+ }, [detailsData])
121
+
122
+
123
+ const handleInputChange = (e) => {
124
+ setNewSubLocation({
125
+ ...newSubLocation,
126
+ name: e.target.value
127
+ });
128
+ };
129
+
130
+ const handleDescriptionInputChange = (e) => {
131
+ setNewSubLocation({
132
+ ...newSubLocation,
133
+ description: e.target.value
134
+ });
135
+ };
136
+
137
+ const handleSaveAndClose = () => {
138
+ let updatedLocationsInService = [];
139
+ const prevRemoved = locationsInService.filter((loc) => loc.id !== detailsData.id);
140
+ const updatedLocation = { ...formData };
141
+ updatedLocationsInService = [...prevRemoved, updatedLocation];
142
+
143
+ const readyFormattedData = {
144
+ data: {
145
+ value: {
146
+ locationsInService: updatedLocationsInService
147
+ }
148
+ }
149
+ };
150
+
151
+ setFormattedData(readyFormattedData);
152
+ setTimeout(() => {
153
+ handleCloseEdit();
154
+ }, 800);
155
+ };
156
+
157
+ const MCLItems = formData.subLocations.map((loc, index) => {
158
+ return ({ ...loc, index})
159
+ });
160
+
161
+ const subLocationsFormatter = {
162
+ name: (item) => {
163
+ const subLocName = item.name
164
+ return subLocName
165
+ },
166
+ description: (item) => {
167
+ return (
168
+ <div style={{ whiteSpace: 'normal', wordWrap: 'break-word' }}>
169
+ {item.description}
170
+ </div>
171
+ )
172
+ },
173
+ trash: (item) => {
174
+ const name = item.name;
175
+ return (
176
+ <button
177
+ onClick={() => handleRemoveSubLocation(item.index)}
178
+ aria-label={<FormattedMessage
179
+ id="settings.locations-edit-remove-sub-location-label"
180
+ values={{ name }}/>
181
+ }
182
+ type="button"
183
+ >
184
+ <Icon icon="trash" size="medium" />
185
+ </button>)
186
+ }
187
+ };
188
+
189
+ const columnWidths = {
190
+ name: '100px',
191
+ description: '50%',
192
+ // description: { min: 100, max: 350 },
193
+ trash: '88px'
194
+ };
195
+
196
+ const renderHeader = (renderProps) => (
197
+ <PaneHeader
198
+ {...renderProps}
199
+ dismissible
200
+ onClose={handleCancelEdit}
201
+ paneTitle={<FormattedMessage
202
+ id="settings.custom-locations.edit.paneTitle" />
203
+ }
204
+ />
205
+ );
206
+
207
+ return (
208
+ <Pane
209
+ defaultWidth='100%'
210
+ paneTitle='Edit location'
211
+ renderHeader={renderHeader}
212
+ >
213
+
214
+ {formattedData &&
215
+ <PutLocationsInService
216
+ data={formattedData}
217
+ />}
218
+ <Row>
219
+ <Col xs={6}>
220
+ <Col>
221
+ <TextField
222
+ readOnly={isOriginOfInstitutionSettings}
223
+ label={
224
+ <FormattedMessage id="settings.locations-edit-textfield-label" />
225
+ }
226
+ value={formData.location}
227
+ onChange={handleLocationInput}
228
+ />
229
+ </Col>
230
+ </Col>
231
+ </Row>
232
+ <Row>
233
+ <Col xs={10}>
234
+ <Label
235
+ style={{ marginTop: '5px' }}
236
+ size="medium"
237
+ tag="h2"
238
+ id="location-zones"
239
+ >
240
+ <FormattedMessage id="settings.locations-zones-label" />
241
+ </Label>
242
+ </Col>
243
+ </Row>
244
+
245
+ <Row>
246
+ <Col xs={10}>
247
+ <div style={{ height: '350px', width: 'auto' }}>
248
+ <MultiColumnList
249
+ autosize
250
+ virtualize
251
+ totalCount={endOflistTotal}
252
+ contentData={MCLItems}
253
+ visibleColumns={['name', 'description', 'trash']}
254
+ columnMapping={{
255
+ name: <FormattedMessage id="settings.locations-visibleColumns-name"/>,
256
+ description: <FormattedMessage id="settings.locations-visibleColumns-description"/>,
257
+ trash: ""
258
+ }}
259
+ formatter={subLocationsFormatter}
260
+ columnWidths={columnWidths}
261
+ isEmptyMessage={
262
+ <FormattedMessage
263
+ id="settings.locations-MCL-isEmpty-msg"/>
264
+ }
265
+ />
266
+ </div>
267
+ </Col>
268
+ </Row>
269
+
270
+ <Row style={{ marginTop: '25px' }}>
271
+ <Col xs={8}>
272
+ <div>
273
+ <TextField
274
+ placeholder='Zone name'
275
+ value={newSubLocation.name}
276
+ onChange={handleInputChange}
277
+ />
278
+ <TextArea
279
+ placeholder='Zone description'
280
+ value={newSubLocation.description}
281
+ onChange={handleDescriptionInputChange}
282
+ style={{ marginTop: "20px" }}
283
+ />
284
+ <Button onClick={handleAddSubLocation}>
285
+ <FormattedMessage id="add-button" />
286
+ </Button>
287
+ </div>
288
+ </Col>
289
+ </Row>
290
+
291
+ <Row style={{ marginTop: '25px', marginBottom: '25px' }}>
292
+ <Col xs={8}>
293
+ <div style={{ minHeight: '50px' }}>
294
+ <MessageBanner
295
+ onEntered={() => handleMessageBannerEntered()}
296
+ type="error"
297
+ show={showErrorBanner}>
298
+ <FormattedMessage id="settings.locations-zones-messageBanner-error-msg"/>
299
+ </MessageBanner>
300
+ </div>
301
+ </Col>
302
+ </Row>
303
+
304
+ <Row style={{ marginTop: '30px' }}>
305
+ <Col xs={4}>
306
+ <Button
307
+ disabled={isButtonDisabled}
308
+ buttonStyle='primary'
309
+ fullWidth
310
+ onClick={handleSaveAndClose}
311
+ >
312
+ <FormattedMessage id="save-and-close-button" />
313
+ </Button>
314
+ </Col>
315
+ </Row>
316
+ </Pane>
317
+ );
318
+ };
319
+
320
+ export default LocationInServiceEditPane;