@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,192 @@
1
+ # Track Application – Admin Guide
2
+
3
+ ## What is Track?
4
+ Track is a library security incident management tool. It helps staff record, organize, and follow up on incidents such as disturbances, thefts, or trespass events. Track keeps all reports in one place, making it easy to search, review, and update records.
5
+
6
+ ---
7
+
8
+ ## Getting Started
9
+
10
+ ### Access Track
11
+ Open Track from your library’s main menu. You’ll see options to view incidents, create new reports, and manage settings.
12
+
13
+ ### Create a New Incident
14
+ Click **“Create report.”** Fill in details like incident type, location, people involved, and a description. You can attach files (photos, documents) and add witnesses.
15
+
16
+ ### Max file sizes for images and video
17
+ - Image: 10MB
18
+ - Video: 100MB
19
+
20
+ ### Administrative data on a report
21
+ Inside the 'Administrative data' accordion in the UI you can view details for **metadata** and **view the UI checkbox for 'Staff suppress'**.
22
+
23
+ #### Metadata
24
+ View record created date and time, record created by, and if relevant the last updated date and time, and user who last updated the record. The created by and updated by user name rendered is a link to that user's profile view in the Users application.
25
+
26
+ #### Staff suppress
27
+ Checking the staff suppress checkbox and saving the record will remove the record from appearing in default. This action can be reverted in Edit mode. To view staff suppressed records in a search use the 'Staff suppressed' filter section to choose 'Yes' or 'Both' (default is 'No').
28
+
29
+ ### Non-customer Incident Report
30
+ You can choose the UI checkbox 'Customer n/a' for building a report that is not associated with a customer. In Edit mode, this can be unchecked and will allow for a customer to be added to the report. You can also edit a customer related report to move from customer related to non-customer. Checking the 'Customer n/a' box in this scenario will remove the previously associated customer(s) from the report.
31
+
32
+ ### Edit or View Incidents
33
+ Select any incident to see full details. You can update information, add attachments/remove attachments, or change the status.
34
+
35
+ ### Search and Filter
36
+ When you view the incident records, you’ll see a **search bar** and several **filter options**.
37
+ These tools make it easy to find exactly what you’re looking for, even in a large database.
38
+
39
+ ---
40
+
41
+ ### Search Features in the UI
42
+
43
+ #### 1. Search Index Dropdown
44
+ You can choose how you want to search using a dropdown menu.
45
+ Each option targets different information:
46
+
47
+ ##### **Keywords (Default)**
48
+ This is the broadest search.
49
+ It looks for your search terms almost everywhere—**customer names**, **descriptions**, **incident types**, **witness names**, **barcodes**, and even the **incident’s own details**.
50
+ If you’re not sure where to look, start here.
51
+
52
+ ##### **Name or Barcode**
53
+ Focuses on **customer first names**, **last names**, and **barcodes**.
54
+ Best when you already know the person involved.
55
+
56
+ ##### **Customer Description**
57
+ Searches only the **description fields** for customers and the **detailed description of the incident**.
58
+ Use this to find incidents based on what was written about a customer or the event.
59
+
60
+ ##### **Witnessed By**
61
+ Looks for matches in the **names and barcodes of witnesses**.
62
+ Use this if you want to find incidents involving a specific witness.
63
+
64
+ ##### **Created By**
65
+ Searches for the **staff member** who created the incident, using their name or barcode.
66
+ Useful for tracking incidents entered by a particular staff member.
67
+
68
+ ### 2. Additional Filters
69
+ You can narrow your search further using filters for:
70
+ - **Location:** Find incidents that happened at a specific branch or area.
71
+ - **Date Range:** Search for incidents within a certain time period.
72
+ - **Incident Type:** Filter by the type of incident (e.g., trespass, disturbance).
73
+ - **Trespass Status:** Show only incidents where someone was trespassed, or filter by current/expired trespass.
74
+ - **Staff Suppression:** Choose to see only non-suppressed, suppressed, or all incidents. Non-suppressed is the default.
75
+
76
+ ### 3. Sorting Options
77
+ You can sort your results by:
78
+ - **Date of Incident:** Shows the newest or oldest incidents first.
79
+ - **Trespass Expiration Date:** Sorts by when trespass bans expire.
80
+ - **Location, Incident Type, Created By, Customers, Witnesses:**
81
+ Sorts alphabetically or naturally by these fields, making it easy to group similar incidents together.
82
+
83
+ ### What Happens Behind the Scenes
84
+ - The system uses your search and filter choices to look through the database and find matching incidents.
85
+ - If you use the **Keywords** search, it checks almost every field for matches—providing the widest results.
86
+ - Other search options (like **Created By** or **Witnessed By**) are more focused and only look at specific fields.
87
+ - Filters and sorting help you quickly narrow down and organize results.
88
+
89
+ ### Summary
90
+ Users can easily **search**, **filter**, and **sort** incident records using a user-friendly interface.
91
+ The system is designed to help you find what you need quickly—whether you’re looking for a **specific person**, **incident type**, or simply **browsing recent events**.
92
+
93
+ The search options are flexible:
94
+ - **Broad when you need them**, and
95
+ - **Focused when you want precision**.
96
+
97
+ ---
98
+
99
+ ### Results in the UI - Sorting and Choosing Columns in Incident Results
100
+ Users can customize how incident records appear in the results list by sorting columns and choosing which columns to display.
101
+ These tools help you organize and focus on the most relevant information for your workflow.
102
+
103
+ ### Column Sorting
104
+ In the **incident results list**, you can sort records by clicking any column header.
105
+
106
+ ### Sortable Columns
107
+ You can sort by:
108
+ - **Customers**
109
+ - **Location**
110
+ - **Date of Incident**
111
+ - **Incident Type**
112
+ - **Witnessed By**
113
+ - **Created By**
114
+ - **Trespass Expiration**
115
+
116
+ ### How Sorting Works
117
+ - Clicking a column header toggles between **ascending** and **descending** order.
118
+ - The system updates the results instantly, showing incidents sorted by your chosen field and direction.
119
+ - Sorting **persists** when you page through results or apply filters, keeping your view consistent.
120
+
121
+ ### How It Works Behind the Scenes
122
+ - When you change sorting or visibility, the UI updates the query and re-renders the results.
123
+ - The backend returns only the data you need, in the order you requested.
124
+
125
+ ### Choosing Which Columns to Display
126
+ Use the **Column Chooser** (from within the 'Action' button in the Results pane) to customize which columns are visible in the results list.
127
+
128
+ ### Available Columns
129
+ You can show or hide:
130
+ - **Location**
131
+ - **Date of Incident**
132
+ - **Incident Type**
133
+ - **Witnessed By**
134
+ - **Created By**
135
+ - **Trespass Expiration**
136
+
137
+ Your column selections are **saved for your session**, so your layout stays personalized as you work.
138
+ This helps you focus on the information most relevant to your tasks.
139
+
140
+ ### Summary
141
+ Users can **sort incident records** by any key field and **customize which columns** are displayed.
142
+ This flexibility makes it easy to organize, review, and find information quickly—streamlining your workflow in Track.
143
+
144
+ ---
145
+
146
+ ### Settings
147
+ Admins can manage **categories**, **types**, **locations**, **trespass templates**, and **trespass reasons** in the Settings area. This customizes Track for your library’s needs.
148
+
149
+ ### Trespass Redeclaration – What It Means and How It Works
150
+ Redeclaration is the process of updating or re-issuing a trespass notice for a customer. This is needed if the trespass period changes, new reasons are added, or the declaration needs to be refreshed.
151
+
152
+ ### Rules for Redeclaration
153
+
154
+ **Eligibility:**
155
+ You can only redeclare a trespass if the customer has at least one current, valid (not suppressed) trespass reason selected.
156
+
157
+ **UI Behavior:**
158
+ In the incident edit screen, you’ll see a checkbox labeled **“Update declaration.”** This is only enabled if the above rule is met.
159
+
160
+ **What Happens:**
161
+ When you redeclare, Track generates a new trespass document for the customer. The updated document includes the latest reasons, dates, and other details.
162
+
163
+ **Why Redeclare?**
164
+ Redeclaration ensures the trespass notice is up-to-date and legally valid. It’s useful if circumstances change or if the original notice needs to be refreshed.
165
+
166
+ ---
167
+
168
+ ## Linking Reports – How and Why
169
+
170
+ ### What is Linking?
171
+ Linking connects related incident reports. For example, if two incidents involve the same person or event, you can link them together.
172
+
173
+ ### How to Link Reports
174
+ 1. In the incident edit or create screen, use the **“Link reports”** option.
175
+ 2. Search for other incidents using filters (type, location, trespass status, etc.).
176
+ 3. Select the reports you want to link.
177
+
178
+ ### Rules and Logic
179
+ - Linked reports appear in each other’s details pane.
180
+ - You’ll see a list of linked reports, making it easy to jump between them.
181
+ - Linking does **not** merge data.
182
+ - Each report keeps its own details, but staff can quickly see connections.
183
+ - You can link and unlink reports at any time.
184
+ - This helps track patterns or repeated incidents.
185
+
186
+ ---
187
+
188
+ ## Tips for Admins
189
+ - Keep categories and types organized for easier reporting.
190
+ - Double-check before deleting items in settings; deleted items cannot be recovered.
191
+ - Use templates for trespass notices to ensure consistency.
192
+ - Set defaults carefully for reasons and templates.
package/icons/app.png ADDED
Binary file
package/icons/app.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="120" height="120"><path d="M0 0h120v120H0V0Z" fill="#4A0B3D"/><path d="M58 17c4.614.445 8.302 2.597 12.313 4.75 7.02 3.662 14.051 6.175 21.652 8.34C95 31 95 31 96 32c1.59 17.208-.703 35.84-11.5 49.875A280.976 280.976 0 0 1 81 86l-2.281 2.887c-2.89 3.465-6.193 6.252-9.719 9.05l-1.828 1.534c-4.404 3.508-4.404 3.508-7.777 3.6C45.16 96.707 33.876 81.522 28.187 67.5c-3.15-8.83-4.368-17.17-4.625-26.5l-.104-2.251C23.44 36.625 23.44 36.625 24 33c3.56-3.215 8.608-4.334 13.125-5.813C48.394 23.573 48.394 23.573 58 17Z" fill="#FBC402"/><path d="M58 17c4.614.445 8.302 2.597 12.313 4.75 7.02 3.662 14.051 6.175 21.652 8.34C95 31 95 31 96 32c1.59 17.208-.703 35.84-11.5 49.875A280.976 280.976 0 0 1 81 86l-2.281 2.887c-2.89 3.465-6.193 6.252-9.719 9.05l-1.828 1.534c-4.404 3.508-4.404 3.508-7.777 3.6C45.16 96.707 33.876 81.522 28.187 67.5c-3.15-8.83-4.368-17.17-4.625-26.5l-.104-2.251C23.44 36.625 23.44 36.625 24 33c3.56-3.215 8.608-4.334 13.125-5.813C48.394 23.573 48.394 23.573 58 17Zm-7.625 11.813c-5.45 2.837-10.663 5.205-16.652 6.714-1.74.325-1.74.325-2.723 1.473-1.581 15.554 2.54 31.7 12.328 43.902C50.263 89.04 50.263 89.04 59 95c4.213-.106 6.909-3.265 9.813-6 .53-.498 1.062-.995 1.61-1.508 11.346-10.821 18.235-23.534 18.69-39.449A346.73 346.73 0 0 0 89 37l-1.725-.67a2586 2586 0 0 1-7.838-3.08l-2.712-1.055c-4.046-1.6-7.901-3.204-11.627-5.46-5.8-3.247-9.411-.767-14.723 2.078Z" fill="#F7C003"/><path d="M60.77 25.168c3.654.941 6.588 2.372 9.917 4.145C76.632 32.412 82.621 34.904 89 37c.83 16.059-1.376 31.909-12.313 44.513C73.615 84.847 70.376 87.973 67 91l-2.586 2.375C62 95 62 95 59.98 94.961 48.795 89.534 39.653 78.27 34.996 66.94 31.626 56.987 30.53 47.471 31 37l3.48-1.277c4.34-1.628 8.59-3.423 12.833-5.285l2.126-.92c10.11-4.424 10.11-4.424 11.33-4.35Zm-7.52 6.645C41.564 37.788 41.564 37.788 35 39c-.837 15.294 1.996 28.888 12.443 40.621C53.411 86.503 53.411 86.503 61 91c13.72-11.85 22.586-23.45 25-42 .062-1.624.087-3.25.063-4.875l-.028-2.367L86 40l-1.695-.629c-6.067-2.331-11.826-5.056-17.57-8.084-5.673-2.936-7.936-2.369-13.485.526Z" fill="#541539"/><path d="M76.879 46.664 79 47c.729 2.223.729 2.223 1 5-1.46 2.22-1.46 2.22-3.664 4.426l-2.402 2.412-2.559 2.474-2.488 2.534c-6.14 6.055-6.14 6.055-9.5 6.499-3.456-.5-5.076-2.147-7.637-4.47l-2.64-2.32C46.685 60.62 46.191 58.765 46 55c1-1 1-1 3.188-1.438C53.38 54.215 55.303 56.886 58 60c4.716-1.877 7.98-5.655 11.49-9.188 2.469-2.371 3.928-3.744 7.389-4.148Z" fill="#4F103A"/></svg>
Binary file
Binary file
package/jest.config.js ADDED
@@ -0,0 +1,10 @@
1
+ const path = require('path');
2
+ const config = require('@folio/jest-config-stripes');
3
+
4
+ module.exports = {
5
+ ...config,
6
+ setupFiles: [
7
+ ...config.setupFiles,
8
+ path.join(__dirname, './test/jest/setupFiles.js'),
9
+ ],
10
+ };
@@ -0,0 +1,75 @@
1
+ {
2
+ "id": "spokane-folio_security-incident-1.0.28",
3
+ "name": "Security incident tracking and trespass automation",
4
+ "permissionSets": [
5
+ {
6
+ "permissionName": "module.security-incident.enabled",
7
+ "displayName": "UI: security-incident module is enabled",
8
+ "visible": true
9
+ },
10
+ {
11
+ "permissionName": "settings.security-incident.enabled",
12
+ "displayName": "Settings (security-incident): display list of settings pages",
13
+ "subPermissions": [
14
+ "settings.enabled",
15
+ "configuration.entries.item.get",
16
+ "configuration.entries.collection.get",
17
+ "security-incident.configurations.get"
18
+ ],
19
+ "visible": true
20
+ },
21
+ {
22
+ "permissionName": "settings.security-incident.edit",
23
+ "displayName": "Settings (security-incident): Edit settings",
24
+ "subPermissions": [
25
+ "settings.security-incident.enabled",
26
+ "configuration.entries.collection.get",
27
+ "configuration.entries.item.put",
28
+ "security-incident.configurations.get",
29
+ "security-incident.configurations.edit"
30
+ ],
31
+ "visible": true
32
+ },
33
+ {
34
+ "permissionName": "ui-security-incident.view",
35
+ "displayName": "Security Incident Report (security-incident): Search and view reports",
36
+ "description": "Can view incidents list and incident details. Can view endpoint at ui-users for linked customer.",
37
+ "subPermissions": [
38
+ "module.security-incident.enabled",
39
+ "security-incident.collection.get",
40
+ "configuration.entries.collection.get",
41
+ "users.item.get",
42
+ "users.collection.get",
43
+ "security-incident.configurations.get",
44
+ "inventory-storage.locations.collection.get"
45
+ ],
46
+ "visible": true
47
+ },
48
+ {
49
+ "permissionName": "ui-security-incident.edit",
50
+ "displayName": "Security Incident Report (security-incident): Search, create and edit reports",
51
+ "description": "Can create and edit incident details. Can view endpoint at ui-users for linked customer.",
52
+ "subPermissions": [
53
+ "ui-security-incident.view",
54
+ "security-incident.incident.edit",
55
+ "usergroups.collection.get"
56
+ ],
57
+ "visible": true
58
+ }
59
+ ],
60
+ "requires": [
61
+ {
62
+ "id": "users",
63
+ "version": "16.0"
64
+ },
65
+ {
66
+ "id": "users-bl",
67
+ "version": "5.0 6.0"
68
+ },
69
+ {
70
+ "id": "configuration",
71
+ "version": "2.0"
72
+ }
73
+ ],
74
+ "optional": []
75
+ }
File without changes
package/package.json ADDED
@@ -0,0 +1,146 @@
1
+ {
2
+ "name": "@spokane-folio/security-incident",
3
+ "version": "1.0.28",
4
+ "description": "Security incident tracking and trespass automation",
5
+ "main": "src/index.js",
6
+ "repository": "",
7
+ "license": "Apache-2.0",
8
+ "scripts": {
9
+ "start": "stripes serve",
10
+ "build": "stripes build --output ./output",
11
+ "build-mod-descriptor": "stripes mod descriptor --full --strict | jq '.[]' > module-descriptor.json ",
12
+ "formatjs-compile": "stripes translate compile",
13
+ "lint": "eslint .",
14
+ "test": "jest"
15
+ },
16
+ "devDependencies": {
17
+ "@babel/core": "^7.21.0",
18
+ "@babel/eslint-parser": "^7.24.7",
19
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
20
+ "@babel/plugin-proposal-decorators": "^7.0.0",
21
+ "@babel/plugin-transform-runtime": "^7.0.0",
22
+ "@babel/preset-env": "^7.0.0",
23
+ "@babel/preset-react": "^7.9.0",
24
+ "@folio/eslint-config-stripes": "^7.0.0",
25
+ "@folio/jest-config-stripes": "^2.0.0",
26
+ "@folio/stripes": "^9.1.4",
27
+ "@folio/stripes-cli": "^3.0.0 || ^3.0.0",
28
+ "@folio/stripes-core": "^10.1.1",
29
+ "@folio/users": "^10.1.1",
30
+ "core-js": "^3.6.4",
31
+ "eslint": "^7.32.0",
32
+ "eslint-config-airbnb": "^19.0.4",
33
+ "eslint-config-prettier": "^9.1.0",
34
+ "eslint-plugin-import": "^2.29.1",
35
+ "eslint-plugin-jsx-a11y": "^6.9.0",
36
+ "eslint-plugin-no-only-tests": "^3.1.0",
37
+ "eslint-plugin-prettier": "^5.2.1",
38
+ "eslint-plugin-react": "^7.34.3",
39
+ "eslint-plugin-react-hooks": "^4.6.2",
40
+ "history": "^4.10.1",
41
+ "lodash": "^4.17.21",
42
+ "prettier": "^3.3.3",
43
+ "react": "^18.2.0",
44
+ "react-dom": "^18.2.0",
45
+ "react-intl": "^6.4.4",
46
+ "react-redux": "^8.0.5",
47
+ "react-router-dom": "^5.2.0",
48
+ "redux": "^4.0.5",
49
+ "regenerator-runtime": "^0.13.3",
50
+ "yarn": "^1.22.22"
51
+ },
52
+ "dependencies": {
53
+ "dompurify": "^3.1.7",
54
+ "file-type": "^20.0.0",
55
+ "handlebars": "^4.7.8",
56
+ "html-entities": "^2.6.0",
57
+ "html2pdf.js": "0.9.3",
58
+ "prop-types": "^15.6.0",
59
+ "react-image": "^4.1.0",
60
+ "uuid": "^10.0.0"
61
+ },
62
+ "peerDependencies": {
63
+ "@folio/stripes": "^9.0.0",
64
+ "@folio/users": "^10.1.1",
65
+ "react": "18.2.0",
66
+ "react-intl": "^6.4.4",
67
+ "react-router": "^5.2.0",
68
+ "react-router-dom": "^5.2.0"
69
+ },
70
+ "stripes": {
71
+ "actsAs": [
72
+ "app",
73
+ "settings"
74
+ ],
75
+ "displayName": "ui-security-incident.meta.title",
76
+ "route": "/incidents",
77
+ "icons": [
78
+ {
79
+ "name": "app",
80
+ "alt": "Create, view and update security incident reports",
81
+ "title": "Track"
82
+ }
83
+ ],
84
+ "okapiInterfaces": {
85
+ "users": "16.0",
86
+ "users-bl": "5.0 6.0",
87
+ "configuration": "2.0"
88
+ },
89
+ "permissionSets": [
90
+ {
91
+ "permissionName": "module.security-incident.enabled",
92
+ "displayName": "UI: security-incident module is enabled",
93
+ "visible": true
94
+ },
95
+ {
96
+ "permissionName": "settings.security-incident.enabled",
97
+ "displayName": "Settings (security-incident): display list of settings pages",
98
+ "subPermissions": [
99
+ "settings.enabled",
100
+ "configuration.entries.item.get",
101
+ "configuration.entries.collection.get",
102
+ "security-incident.configurations.get"
103
+ ],
104
+ "visible": true
105
+ },
106
+ {
107
+ "permissionName": "settings.security-incident.edit",
108
+ "displayName": "Settings (security-incident): Edit settings",
109
+ "subPermissions": [
110
+ "settings.security-incident.enabled",
111
+ "configuration.entries.collection.get",
112
+ "configuration.entries.item.put",
113
+ "security-incident.configurations.get",
114
+ "security-incident.configurations.edit"
115
+ ],
116
+ "visible": true
117
+ },
118
+ {
119
+ "permissionName": "ui-security-incident.view",
120
+ "displayName": "Security Incident Report (security-incident): Search and view reports",
121
+ "description": "Can view incidents list and incident details. Can view endpoint at ui-users for linked customer.",
122
+ "subPermissions": [
123
+ "module.security-incident.enabled",
124
+ "security-incident.collection.get",
125
+ "configuration.entries.collection.get",
126
+ "users.item.get",
127
+ "users.collection.get",
128
+ "security-incident.configurations.get",
129
+ "inventory-storage.locations.collection.get"
130
+ ],
131
+ "visible": true
132
+ },
133
+ {
134
+ "permissionName": "ui-security-incident.edit",
135
+ "displayName": "Security Incident Report (security-incident): Search, create and edit reports",
136
+ "description": "Can create and edit incident details. Can view endpoint at ui-users for linked customer.",
137
+ "subPermissions": [
138
+ "ui-security-incident.view",
139
+ "security-incident.incident.edit",
140
+ "usergroups.collection.get"
141
+ ],
142
+ "visible": true
143
+ }
144
+ ]
145
+ }
146
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { Checkbox } from '@folio/stripes/components';
3
+ import { Row, Col, Label } from '@folio/stripes/components';
4
+
5
+ const ColumnChooser = ({
6
+ possibleColumns,
7
+ visibleColumns,
8
+ toggleColumn,
9
+ columnLabels = {} // default empty obj
10
+ }) => (
11
+ <>
12
+ <Label>
13
+ Choose columns
14
+ </Label>
15
+ <Row style={{ marginLeft: '3px' }}>
16
+ <Col>
17
+ <ul style={{ listStyle: 'none', paddingLeft: 0, margin: 0 }}>
18
+ {possibleColumns.map(colId => (
19
+ <li key={colId}>
20
+ <Checkbox
21
+ value={colId}
22
+ checked={visibleColumns.includes(colId)}
23
+ label={columnLabels[colId] ?? colId}
24
+ onChange={(e) => {
25
+ e.stopPropagation();
26
+ toggleColumn(colId);
27
+ }}
28
+ />
29
+ </li>
30
+ ))}
31
+ </ul>
32
+ </Col>
33
+ </Row>
34
+ </>
35
+ );
36
+
37
+ export default ColumnChooser;
@@ -0,0 +1,132 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { stripesConnect } from '@folio/stripes/core';
4
+ import { getHeaderWithCredentials } from '@folio/stripes/util';
5
+ import { IncidentContext } from '../../contexts/IncidentContext';
6
+
7
+ class CreateMedia extends React.Component {
8
+ static contextType = IncidentContext;
9
+
10
+ constructor(props) {
11
+ super(props);
12
+ const { stripes } = this.props;
13
+ const { okapi } = stripes;
14
+ this.okapiURL = okapi.url;
15
+ };
16
+
17
+ componentDidMount() {
18
+ this.createMedia(this.props.id, this.props.formDataArray);
19
+ };
20
+
21
+ componentDidUpdate(prevProps) {
22
+ if (
23
+ this.props.id !== prevProps.id &&
24
+ this.props.formDataArray !== prevProps.formDataArray
25
+ ) {
26
+ this.createMedia(this.props.id, this.props.formDataArray);
27
+ }
28
+ };
29
+
30
+ // reset states, invoked regardless of 'new' or 'edit' this.props.context cases
31
+ resetStates = () => {
32
+ this.context.setIdForMediaCreate(null);
33
+ this.context.setFormDataArrayForMediaCreate(null);
34
+ this.context.setAttachmentsData([]);
35
+ this.context.setIncidentsList([]);
36
+ };
37
+
38
+ createMedia = async (id, formDataArray) => {
39
+ const { stripes } = this.props;
40
+ const headersWithCredentials = getHeaderWithCredentials(stripes.okapi);
41
+ // destructure content type key/value and pass headers w/ out it
42
+ // the browser will set "content type": "multipart/form-data" programmatically
43
+ const headers = { ...headersWithCredentials.headers };
44
+ delete headers['Content-Type'];
45
+
46
+ const readyAttachmentsArray = formDataArray.map((att) => {
47
+ const metadata = {
48
+ id: att.id,
49
+ description: att.description,
50
+ contentType: att.contentType,
51
+ };
52
+ const formData = new FormData();
53
+ formData.append('metadata', JSON.stringify(metadata));
54
+
55
+ if (att.contentType === 'application/pdf' && !att.file.name) {
56
+ // provide filename for PDF (PDFs are return as Blob w/ out filename)
57
+ formData.append('file', att.file, `${att.id}.pdf`)
58
+ } else {
59
+ formData.append('file', att.file)
60
+ };
61
+
62
+ return formData;
63
+ });
64
+
65
+ try{
66
+ const results = [];
67
+
68
+ for(const attachment of readyAttachmentsArray) {
69
+ const response = await fetch(`${this.okapiURL}/incidents/${id}/media`, {
70
+ method: 'POST',
71
+ headers: {
72
+ ...headers,
73
+ },
74
+ body: attachment,
75
+ });
76
+ results.push(response)
77
+ };
78
+
79
+ const allSuccess = results.every(response => response.status === 201);
80
+
81
+ if (!allSuccess) {
82
+ console.error('one ore more attachments failed to save')
83
+ return;
84
+ };
85
+
86
+ this.resetStates();
87
+
88
+ if (this.props.context === 'edit') {
89
+ this.context.setUseGetList(false);
90
+ this.context.setIsLoadingDetails(false);
91
+ this.context.setIsUpdatingReport(false);
92
+ this.props.handleCloseEdit();
93
+
94
+ } else if (this.props.context === 'new') {
95
+ this.context.setIsCreatingReport(false)
96
+ this.props.handleCloseNewOnSuccess(id);
97
+ }
98
+ } catch (error) {
99
+ this.context.setUseGetList(false);
100
+ console.error('@CreateMedia - in CATCH - error uploading files: ', error);
101
+ }
102
+ };
103
+
104
+ render() {
105
+ return <></>;
106
+ };
107
+ };
108
+
109
+ CreateMedia.contextType = IncidentContext;
110
+ CreateMedia.propTypes = {
111
+ stripes: PropTypes.object.isRequired,
112
+ id: PropTypes.string.isRequired,
113
+ formDataArray: PropTypes.arrayOf(
114
+ PropTypes.shape({
115
+ id: PropTypes.string.isRequired,
116
+ description: PropTypes.string.isRequired,
117
+ contentType: PropTypes.string.isRequired,
118
+ file: PropTypes.instanceOf(File).isRequired,
119
+ })
120
+ ).isRequired,
121
+ handleCloseEdit: PropTypes.func,
122
+ handleCloseNewOnSuccess: PropTypes.func,
123
+ context: PropTypes.shape({
124
+ setIdForMediaCreate: PropTypes.string.isRequired,
125
+ setFormDataArrayForMediaCreate: PropTypes.func.isRequired,
126
+ setAttachmentsData: PropTypes.func.isRequired,
127
+ setIncidentsList: PropTypes.func.isRequired,
128
+ setUseGetList: PropTypes.func.isRequired,
129
+ }).isRequired,
130
+ };
131
+
132
+ export default stripesConnect(CreateMedia, '@spokane-folio/security-incident');