mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Feature: system status (#5743)
This commit is contained in:
		| @@ -77,7 +77,9 @@ | ||||
|             "scripts": [], | ||||
|             "allowedCommonJsDependencies": [ | ||||
|               "pdfjs-dist", | ||||
|               "pdfjs-dist/web/pdf_viewer" | ||||
|               "pdfjs-dist/web/pdf_viewer", | ||||
|               "filesize", | ||||
|               "file-saver" | ||||
|             ], | ||||
|             "vendorChunk": true, | ||||
|             "extractLicenses": false, | ||||
|   | ||||
| @@ -458,7 +458,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">354</context> | ||||
|           <context context-type="linenumber">375</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context> | ||||
| @@ -600,7 +600,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">342</context> | ||||
|           <context context-type="linenumber">363</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context> | ||||
| @@ -622,6 +622,10 @@ | ||||
|           <context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context> | ||||
|           <context context-type="linenumber">23</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">10</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.html</context> | ||||
|           <context context-type="linenumber">15</context> | ||||
| @@ -667,7 +671,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">293</context> | ||||
|           <context context-type="linenumber">314</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
| @@ -693,238 +697,249 @@ | ||||
|         <source>Start tour</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">7</context> | ||||
|           <context context-type="linenumber">8</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3276228498925657259" datatype="html"> | ||||
|         <source>System Status</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">27</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">2</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4798013226763881638" datatype="html"> | ||||
|         <source>Open Django Admin</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">9</context> | ||||
|           <context context-type="linenumber">30</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6439365426343089851" datatype="html"> | ||||
|         <source>General</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">18</context> | ||||
|           <context context-type="linenumber">39</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8671234314555525900" datatype="html"> | ||||
|         <source>Appearance</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">21</context> | ||||
|           <context context-type="linenumber">42</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3777637051272512093" datatype="html"> | ||||
|         <source>Display language</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">25</context> | ||||
|           <context context-type="linenumber">46</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="53523152145406584" datatype="html"> | ||||
|         <source>You need to reload the page after applying a new language.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">38</context> | ||||
|           <context context-type="linenumber">59</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3766032098416558788" datatype="html"> | ||||
|         <source>Date display</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">46</context> | ||||
|           <context context-type="linenumber">67</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3733378544613473393" datatype="html"> | ||||
|         <source>Date format</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">63</context> | ||||
|           <context context-type="linenumber">84</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3407788781115661841" datatype="html"> | ||||
|         <source>Short: <x id="INTERPOLATION" equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">69,70</context> | ||||
|           <context context-type="linenumber">90,91</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6290748171049664628" datatype="html"> | ||||
|         <source>Medium: <x id="INTERPOLATION" equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">73,74</context> | ||||
|           <context context-type="linenumber">94,95</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7189855711197998347" datatype="html"> | ||||
|         <source>Long: <x id="INTERPOLATION" equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">77,78</context> | ||||
|           <context context-type="linenumber">98,99</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8939587804990976924" datatype="html"> | ||||
|         <source>Items per page</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">85</context> | ||||
|           <context context-type="linenumber">106</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8028535997917730106" datatype="html"> | ||||
|         <source>Document editor</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">101</context> | ||||
|           <context context-type="linenumber">122</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6708098108196142028" datatype="html"> | ||||
|         <source>Use PDF viewer provided by the browser</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">105</context> | ||||
|           <context context-type="linenumber">126</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9003921625412907981" datatype="html"> | ||||
|         <source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">105</context> | ||||
|           <context context-type="linenumber">126</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3982403428275430291" datatype="html"> | ||||
|         <source>Sidebar</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">112</context> | ||||
|           <context context-type="linenumber">133</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4608457133854405683" datatype="html"> | ||||
|         <source>Use 'slim' sidebar (icons only)</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">116</context> | ||||
|           <context context-type="linenumber">137</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1356890996281769972" datatype="html"> | ||||
|         <source>Dark mode</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">123</context> | ||||
|           <context context-type="linenumber">144</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4913823100518391922" datatype="html"> | ||||
|         <source>Use system settings</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">126</context> | ||||
|           <context context-type="linenumber">147</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5782828784040423650" datatype="html"> | ||||
|         <source>Enable dark mode</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">127</context> | ||||
|           <context context-type="linenumber">148</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6336642923114460405" datatype="html"> | ||||
|         <source>Invert thumbnails in dark mode</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">128</context> | ||||
|           <context context-type="linenumber">149</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7983234071833154796" datatype="html"> | ||||
|         <source>Theme Color</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">134</context> | ||||
|           <context context-type="linenumber">155</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7808756054397155068" datatype="html"> | ||||
|         <source>Reset</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">141</context> | ||||
|           <context context-type="linenumber">162</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8901931207592071833" datatype="html"> | ||||
|         <source>Update checking</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">146</context> | ||||
|           <context context-type="linenumber">167</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7141691772243630313" datatype="html"> | ||||
|         <source> Update checking works by pinging the public <x id="START_LINK" ctype="x-a" equiv-text="<a href="https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest" target="_blank" rel="noopener noreferrer">"/>GitHub API<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/> for the latest release to determine whether a new version is available.<x id="LINE_BREAK" ctype="lb" equiv-text="<br/>"/> Actual updating of the app must still be performed manually. </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">150,153</context> | ||||
|           <context context-type="linenumber">171,174</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5489945693955857309" datatype="html"> | ||||
|         <source><x id="START_EMPHASISED_TEXT" ctype="x-em" equiv-text=">"/>No tracking data is collected by the app in any way.<x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="</em>"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">155,157</context> | ||||
|           <context context-type="linenumber">176,178</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5070799004079086984" datatype="html"> | ||||
|         <source>Enable update checking</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">157</context> | ||||
|           <context context-type="linenumber">178</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="908152367861642592" datatype="html"> | ||||
|         <source>Document editing</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">161</context> | ||||
|           <context context-type="linenumber">182</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2959590948110714366" datatype="html"> | ||||
|         <source>Automatically remove inbox tag(s) on save</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">165</context> | ||||
|           <context context-type="linenumber">186</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8508424367627989968" datatype="html"> | ||||
|         <source>Bulk editing</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">169</context> | ||||
|           <context context-type="linenumber">190</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8158899674926420054" datatype="html"> | ||||
|         <source>Show confirmation dialogs</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">173</context> | ||||
|           <context context-type="linenumber">194</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6906812245033969309" datatype="html"> | ||||
|         <source>Deleting documents will always ask for confirmation.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">173</context> | ||||
|           <context context-type="linenumber">194</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="290238406234356122" datatype="html"> | ||||
|         <source>Apply on close</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">174</context> | ||||
|           <context context-type="linenumber">195</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8104421162933956065" datatype="html"> | ||||
|         <source>Notes</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">178</context> | ||||
|           <context context-type="linenumber">199</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> | ||||
| @@ -939,14 +954,14 @@ | ||||
|         <source>Enable notes</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">182</context> | ||||
|           <context context-type="linenumber">203</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7314814725704332646" datatype="html"> | ||||
|         <source>Permissions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">190</context> | ||||
|           <context context-type="linenumber">211</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context> | ||||
| @@ -1001,28 +1016,28 @@ | ||||
|         <source>Default Permissions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">193</context> | ||||
|           <context context-type="linenumber">214</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8222269449891326545" datatype="html"> | ||||
|         <source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">197,199</context> | ||||
|           <context context-type="linenumber">218,220</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4292903881380648974" datatype="html"> | ||||
|         <source>Default Owner</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">204</context> | ||||
|           <context context-type="linenumber">225</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="734147282056744882" datatype="html"> | ||||
|         <source>Objects without an owner can be viewed and edited by all users</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">208</context> | ||||
|           <context context-type="linenumber">229</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context> | ||||
| @@ -1033,18 +1048,18 @@ | ||||
|         <source>Default View Permissions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">213</context> | ||||
|           <context context-type="linenumber">234</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2191775412581217688" datatype="html"> | ||||
|         <source>Users:</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">218</context> | ||||
|           <context context-type="linenumber">239</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">245</context> | ||||
|           <context context-type="linenumber">266</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context> | ||||
| @@ -1067,11 +1082,11 @@ | ||||
|         <source>Groups:</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">228</context> | ||||
|           <context context-type="linenumber">249</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">255</context> | ||||
|           <context context-type="linenumber">276</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context> | ||||
| @@ -1094,14 +1109,14 @@ | ||||
|         <source>Default Edit Permissions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">240</context> | ||||
|           <context context-type="linenumber">261</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3728984448750213892" datatype="html"> | ||||
|         <source>Edit permissions also grant viewing permissions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">264</context> | ||||
|           <context context-type="linenumber">285</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context> | ||||
| @@ -1116,56 +1131,56 @@ | ||||
|         <source>Notifications</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">272</context> | ||||
|           <context context-type="linenumber">293</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8545554728558600606" datatype="html"> | ||||
|         <source>Document processing</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">275</context> | ||||
|           <context context-type="linenumber">296</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3656786776644872398" datatype="html"> | ||||
|         <source>Show notifications when new documents are detected</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">279</context> | ||||
|           <context context-type="linenumber">300</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6057053428592387613" datatype="html"> | ||||
|         <source>Show notifications when document processing completes successfully</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">280</context> | ||||
|           <context context-type="linenumber">301</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="370315664367425513" datatype="html"> | ||||
|         <source>Show notifications when document processing fails</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">281</context> | ||||
|           <context context-type="linenumber">302</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6838309441164918531" datatype="html"> | ||||
|         <source>Suppress notifications on dashboard</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">282</context> | ||||
|           <context context-type="linenumber">303</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2741919327232918179" datatype="html"> | ||||
|         <source>This will suppress all messages about document processing status on the dashboard.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">282</context> | ||||
|           <context context-type="linenumber">303</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="472206565520537964" datatype="html"> | ||||
|         <source>Saved views</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">290</context> | ||||
|           <context context-type="linenumber">311</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
| @@ -1176,14 +1191,14 @@ | ||||
|         <source>Show warning when closing saved views with unsaved changes</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">296</context> | ||||
|           <context context-type="linenumber">317</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2123659921722214537" datatype="html"> | ||||
|         <source>Views</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">300</context> | ||||
|           <context context-type="linenumber">321</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> | ||||
| @@ -1194,7 +1209,7 @@ | ||||
|         <source>Name</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">306</context> | ||||
|           <context context-type="linenumber">327</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context> | ||||
| @@ -1301,14 +1316,14 @@ | ||||
|         <source> <x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="visually-hidden">"/>Appears on<x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">310,311</context> | ||||
|           <context context-type="linenumber">331,332</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4104807402967139762" datatype="html"> | ||||
|         <source>Show on dashboard</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">313</context> | ||||
|           <context context-type="linenumber">334</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context> | ||||
| @@ -1319,7 +1334,7 @@ | ||||
|         <source>Show in sidebar</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">317</context> | ||||
|           <context context-type="linenumber">338</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context> | ||||
| @@ -1330,7 +1345,7 @@ | ||||
|         <source>Actions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">321</context> | ||||
|           <context context-type="linenumber">342</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context> | ||||
| @@ -1393,7 +1408,7 @@ | ||||
|         <source>Delete</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">324</context> | ||||
|           <context context-type="linenumber">345</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.html</context> | ||||
| @@ -1504,28 +1519,28 @@ | ||||
|         <source>No saved views defined.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> | ||||
|           <context context-type="linenumber">336</context> | ||||
|           <context context-type="linenumber">357</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6839066544204061364" datatype="html"> | ||||
|         <source>Use system language</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">51</context> | ||||
|           <context context-type="linenumber">61</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7729897675462249787" datatype="html"> | ||||
|         <source>Use date format of display language</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">54</context> | ||||
|           <context context-type="linenumber">64</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1235706724900303689" datatype="html"> | ||||
|         <source>Error retrieving users</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">159</context> | ||||
|           <context context-type="linenumber">183</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context> | ||||
| @@ -1536,7 +1551,7 @@ | ||||
|         <source>Error retrieving groups</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">178</context> | ||||
|           <context context-type="linenumber">202</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context> | ||||
| @@ -1547,35 +1562,35 @@ | ||||
|         <source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">380</context> | ||||
|           <context context-type="linenumber">415</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7217000812750597833" datatype="html"> | ||||
|         <source>Settings were saved successfully.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">506</context> | ||||
|           <context context-type="linenumber">541</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="525012668859298131" datatype="html"> | ||||
|         <source>Settings were saved successfully. Reload is required to apply some changes.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">510</context> | ||||
|           <context context-type="linenumber">545</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8491974984518503778" datatype="html"> | ||||
|         <source>Reload now</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">511</context> | ||||
|           <context context-type="linenumber">546</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3011185103048412841" datatype="html"> | ||||
|         <source>An error occurred while saving settings.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">521</context> | ||||
|           <context context-type="linenumber">556</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
| @@ -1586,7 +1601,7 @@ | ||||
|         <source>Error while storing settings on server.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">555</context> | ||||
|           <context context-type="linenumber">590</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2991443309752293110" datatype="html"> | ||||
| @@ -4075,6 +4090,10 @@ | ||||
|           <context context-type="sourcefile">src/app/components/common/permissions-select/permissions-select.component.html</context> | ||||
|           <context context-type="linenumber">5</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">45</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1230154438678955604" datatype="html"> | ||||
|         <source>Change</source> | ||||
| @@ -4139,6 +4158,10 @@ | ||||
|           <context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context> | ||||
|           <context context-type="linenumber">29</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">152</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="595732867213154214" datatype="html"> | ||||
|         <source>Regenerate auth token</source> | ||||
| @@ -4370,8 +4393,68 @@ | ||||
|           <context context-type="linenumber">151</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9180110319941008393" datatype="html"> | ||||
|         <source>Environment</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">18</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5973078531069712831" datatype="html"> | ||||
|         <source>Paperless-ngx Version</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">22</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6269705781013540301" datatype="html"> | ||||
|         <source>Install Type</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">24</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7962174670320694437" datatype="html"> | ||||
|         <source>Server OS</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">26</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2903495470702110128" datatype="html"> | ||||
|         <source>Media Storage</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">28</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2571831784751497241" datatype="html"> | ||||
|         <source>available</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">31</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6489441800790477240" datatype="html"> | ||||
|         <source>total</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">31</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4198035112366277884" datatype="html"> | ||||
|         <source>Database</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">41</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5611592591303869712" datatype="html"> | ||||
|         <source>Status</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">47</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/toasts/toasts.component.html</context> | ||||
|           <context context-type="linenumber">26</context> | ||||
| @@ -4381,6 +4464,76 @@ | ||||
|           <context context-type="linenumber">19</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2256165083739630668" datatype="html"> | ||||
|         <source>Migration Status</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">56</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7881311375431899727" datatype="html"> | ||||
|         <source>Latest Migration</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">64</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4632965004151576238" datatype="html"> | ||||
|         <source>Pending Migrations</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">66</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6904866445262015585" datatype="html"> | ||||
|         <source>Tasks</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">83</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6911698235105017958" datatype="html"> | ||||
|         <source>Redis Status</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">87</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5349496739889768589" datatype="html"> | ||||
|         <source>Celery Status</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">96</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="31377277941774469" datatype="html"> | ||||
|         <source>Search Index</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">105</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4089509911694721896" datatype="html"> | ||||
|         <source>Last Updated</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">119</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="46628344485199198" datatype="html"> | ||||
|         <source>Classifier</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">121</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6096684179126491743" datatype="html"> | ||||
|         <source>Last Trained</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context> | ||||
|           <context context-type="linenumber">135</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6732151329960766506" datatype="html"> | ||||
|         <source>Copy Raw Error</source> | ||||
|         <context-group purpose="location"> | ||||
|   | ||||
							
								
								
									
										23
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										23
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -29,6 +29,7 @@ | ||||
|         "ngx-color": "^9.0.0", | ||||
|         "ngx-cookie-service": "^17.1.0", | ||||
|         "ngx-file-drop": "^16.0.0", | ||||
|         "ngx-filesize": "^3.0.3", | ||||
|         "ngx-ui-tour-ng-bootstrap": "^14.0.2", | ||||
|         "pdfjs-dist": "^3.11.174", | ||||
|         "rxjs": "^7.8.1", | ||||
| @@ -9844,6 +9845,15 @@ | ||||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/filesize": { | ||||
|       "version": "9.0.11", | ||||
|       "resolved": "https://registry.npmjs.org/filesize/-/filesize-9.0.11.tgz", | ||||
|       "integrity": "sha512-gTAiTtI0STpKa5xesyTA9hA3LX4ga8sm2nWRcffEa1L/5vQwb4mj2MdzMkoHoGv4QzfDshQZuYscQSf8c4TKOA==", | ||||
|       "peer": true, | ||||
|       "engines": { | ||||
|         "node": ">= 0.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/fill-range": { | ||||
|       "version": "7.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", | ||||
| @@ -14105,6 +14115,19 @@ | ||||
|         "@angular/core": ">=14.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/ngx-filesize": { | ||||
|       "version": "3.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/ngx-filesize/-/ngx-filesize-3.0.3.tgz", | ||||
|       "integrity": "sha512-qqP2p4WbbF7R+NXC9NqRQdAfWfMAYJ2Ijf4ezRCq7j3tPY6ybSP9AZ3FY1U7/95n1hmOJ2U5oY+oFb7LhHQRBw==", | ||||
|       "dependencies": { | ||||
|         "tslib": "^2.3.0" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@angular/common": ">= 14.2.0 < 18.0.0", | ||||
|         "@angular/core": ">= 14.2.0 < 18.0.0", | ||||
|         "filesize": ">= 6.0.0 < 10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/ngx-ui-tour-core": { | ||||
|       "version": "12.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-12.0.1.tgz", | ||||
|   | ||||
| @@ -31,6 +31,7 @@ | ||||
|     "ngx-color": "^9.0.0", | ||||
|     "ngx-cookie-service": "^17.1.0", | ||||
|     "ngx-file-drop": "^16.0.0", | ||||
|     "ngx-filesize": "^3.0.3", | ||||
|     "ngx-ui-tour-ng-bootstrap": "^14.0.2", | ||||
|     "pdfjs-dist": "^3.11.174", | ||||
|     "rxjs": "^7.8.1", | ||||
|   | ||||
| @@ -114,7 +114,10 @@ import { FileComponent } from './components/common/input/file/file.component' | ||||
| import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' | ||||
| import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component' | ||||
| import { MonetaryComponent } from './components/common/input/monetary/monetary.component' | ||||
| import { SystemStatusDialogComponent } from './components/common/system-status-dialog/system-status-dialog.component' | ||||
| import { NgxFilesizeModule } from 'ngx-filesize' | ||||
| import { | ||||
|   airplane, | ||||
|   archive, | ||||
|   arrowCounterclockwise, | ||||
|   arrowDown, | ||||
| @@ -129,12 +132,14 @@ import { | ||||
|   boxes, | ||||
|   calendar, | ||||
|   calendarEvent, | ||||
|   cardChecklist, | ||||
|   caretDown, | ||||
|   caretUp, | ||||
|   chatLeftText, | ||||
|   check, | ||||
|   check2All, | ||||
|   checkAll, | ||||
|   checkCircleFill, | ||||
|   checkLg, | ||||
|   chevronDoubleLeft, | ||||
|   chevronDoubleRight, | ||||
| @@ -148,7 +153,9 @@ import { | ||||
|   doorOpen, | ||||
|   download, | ||||
|   envelope, | ||||
|   exclamationCircleFill, | ||||
|   exclamationTriangle, | ||||
|   exclamationTriangleFill, | ||||
|   eye, | ||||
|   fileEarmark, | ||||
|   fileEarmarkCheck, | ||||
| @@ -200,6 +207,7 @@ import { | ||||
| } from 'ngx-bootstrap-icons' | ||||
|  | ||||
| const icons = { | ||||
|   airplane, | ||||
|   archive, | ||||
|   arrowCounterclockwise, | ||||
|   arrowDown, | ||||
| @@ -214,12 +222,14 @@ const icons = { | ||||
|   boxes, | ||||
|   calendar, | ||||
|   calendarEvent, | ||||
|   cardChecklist, | ||||
|   caretDown, | ||||
|   caretUp, | ||||
|   chatLeftText, | ||||
|   check, | ||||
|   check2All, | ||||
|   checkAll, | ||||
|   checkCircleFill, | ||||
|   checkLg, | ||||
|   chevronDoubleLeft, | ||||
|   chevronDoubleRight, | ||||
| @@ -233,7 +243,9 @@ const icons = { | ||||
|   doorOpen, | ||||
|   download, | ||||
|   envelope, | ||||
|   exclamationCircleFill, | ||||
|   exclamationTriangle, | ||||
|   exclamationTriangleFill, | ||||
|   eye, | ||||
|   fileEarmark, | ||||
|   fileEarmarkCheck, | ||||
| @@ -445,6 +457,7 @@ function initializeApp(settings: SettingsService) { | ||||
|     FileComponent, | ||||
|     ConfirmButtonComponent, | ||||
|     MonetaryComponent, | ||||
|     SystemStatusDialogComponent, | ||||
|   ], | ||||
|   imports: [ | ||||
|     BrowserModule, | ||||
| @@ -459,6 +472,7 @@ function initializeApp(settings: SettingsService) { | ||||
|     TourNgBootstrapModule, | ||||
|     DragDropModule, | ||||
|     NgxBootstrapIconsModule.pick(icons), | ||||
|     NgxFilesizeModule, | ||||
|   ], | ||||
|   providers: [ | ||||
|     { | ||||
|   | ||||
| @@ -4,10 +4,31 @@ | ||||
|   info="Options to customize appearance, notifications, saved views and more. Settings apply to the <strong>current user only</strong>." | ||||
|   i18n-info | ||||
|   > | ||||
|   <button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"><ng-container i18n>Start tour</ng-container></button> | ||||
|   <button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"> | ||||
|     <i-bs class="me-1" name="airplane"></i-bs> <ng-container i18n>Start tour</ng-container> | ||||
|   </button> | ||||
|   <button class="btn btn-sm btn-outline-primary position-relative ms-5" (click)="showSystemStatus()" | ||||
|     [disabled]="!systemStatus" | ||||
|     *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }"> | ||||
|     @if (!systemStatus) { | ||||
|       <div class="spinner-border spinner-border-sm me-1 h-75" role="status"></div> | ||||
|     } @else { | ||||
|       <i-bs class="me-2" name="card-checklist"></i-bs> | ||||
|       @if (systemStatusHasErrors) { | ||||
|         <span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0"> | ||||
|           <i-bs name="exclamation-circle-fill" class="text-danger" width="1.75em" height="1.75em"></i-bs> | ||||
|         </span> | ||||
|       } @else { | ||||
|         <span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0"> | ||||
|           <i-bs name="check-circle-fill" class="text-primary" width="1.75em" height="1.75em"></i-bs> | ||||
|         </span> | ||||
|       } | ||||
|     } | ||||
|     <ng-container i18n>System Status</ng-container> | ||||
|   </button> | ||||
|   <a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary ms-3" href="admin/" target="_blank"> | ||||
|     <ng-container i18n>Open Django Admin</ng-container> | ||||
|     <i-bs name="arrow-up-right"></i-bs> | ||||
|      <i-bs name="arrow-up-right"></i-bs> | ||||
|   </a> | ||||
| </pngx-page-header> | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,8 @@ import { | ||||
|   NgbModule, | ||||
|   NgbAlertModule, | ||||
|   NgbNavLink, | ||||
|   NgbModal, | ||||
|   NgbModalModule, | ||||
| } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { NgSelectModule } from '@ng-select/ng-select' | ||||
| import { of, throwError } from 'rxjs' | ||||
| @@ -39,6 +41,13 @@ import { SettingsComponent } from './settings.component' | ||||
| import { IfOwnerDirective } from 'src/app/directives/if-owner.directive' | ||||
| import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' | ||||
| import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component' | ||||
| import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component' | ||||
| import { SystemStatusService } from 'src/app/services/system-status.service' | ||||
| import { | ||||
|   SystemStatus, | ||||
|   InstallType, | ||||
|   SystemStatusItemStatus, | ||||
| } from 'src/app/data/system-status' | ||||
|  | ||||
| const savedViews = [ | ||||
|   { id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true }, | ||||
| @@ -65,6 +74,8 @@ describe('SettingsComponent', () => { | ||||
|   let userService: UserService | ||||
|   let permissionsService: PermissionsService | ||||
|   let groupService: GroupService | ||||
|   let modalService: NgbModal | ||||
|   let systemStatusService: SystemStatusService | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     TestBed.configureTestingModule({ | ||||
| @@ -96,6 +107,7 @@ describe('SettingsComponent', () => { | ||||
|         NgbAlertModule, | ||||
|         NgSelectModule, | ||||
|         NgxBootstrapIconsModule.pick(allIcons), | ||||
|         NgbModalModule, | ||||
|       ], | ||||
|     }).compileComponents() | ||||
|  | ||||
| @@ -107,6 +119,8 @@ describe('SettingsComponent', () => { | ||||
|     settingsService.currentUser = users[0] | ||||
|     userService = TestBed.inject(UserService) | ||||
|     permissionsService = TestBed.inject(PermissionsService) | ||||
|     modalService = TestBed.inject(NgbModal) | ||||
|     systemStatusService = TestBed.inject(SystemStatusService) | ||||
|     jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) | ||||
|     jest | ||||
|       .spyOn(permissionsService, 'currentUserHasObjectPermissions') | ||||
| @@ -372,4 +386,54 @@ describe('SettingsComponent', () => { | ||||
|     fixture.detectChanges() | ||||
|     expect(toastErrorSpy).toBeCalled() | ||||
|   }) | ||||
|  | ||||
|   it('should load system status on initialize, show errors if needed', () => { | ||||
|     const status: SystemStatus = { | ||||
|       pngx_version: '2.4.3', | ||||
|       server_os: 'macOS-14.1.1-arm64-arm-64bit', | ||||
|       install_type: InstallType.BareMetal, | ||||
|       storage: { total: 494384795648, available: 13573525504 }, | ||||
|       database: { | ||||
|         type: 'sqlite', | ||||
|         url: '/paperless-ngx/data/db.sqlite3', | ||||
|         status: SystemStatusItemStatus.ERROR, | ||||
|         error: null, | ||||
|         migration_status: { | ||||
|           latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data', | ||||
|           unapplied_migrations: [], | ||||
|         }, | ||||
|       }, | ||||
|       tasks: { | ||||
|         redis_url: 'redis://localhost:6379', | ||||
|         redis_status: SystemStatusItemStatus.ERROR, | ||||
|         redis_error: | ||||
|           'Error 61 connecting to localhost:6379. Connection refused.', | ||||
|         celery_status: SystemStatusItemStatus.ERROR, | ||||
|         index_status: SystemStatusItemStatus.OK, | ||||
|         index_last_modified: new Date().toISOString(), | ||||
|         index_error: null, | ||||
|         classifier_status: SystemStatusItemStatus.OK, | ||||
|         classifier_last_trained: new Date().toISOString(), | ||||
|         classifier_error: null, | ||||
|       }, | ||||
|     } | ||||
|     jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status)) | ||||
|     completeSetup() | ||||
|     expect(component['systemStatus']).toEqual(status) // private | ||||
|     expect(component.systemStatusHasErrors).toBeTruthy() | ||||
|     // coverage | ||||
|     component['systemStatus'].database.status = SystemStatusItemStatus.OK | ||||
|     component['systemStatus'].tasks.redis_status = SystemStatusItemStatus.OK | ||||
|     component['systemStatus'].tasks.celery_status = SystemStatusItemStatus.OK | ||||
|     expect(component.systemStatusHasErrors).toBeFalsy() | ||||
|   }) | ||||
|  | ||||
|   it('should open system status dialog', () => { | ||||
|     const modalOpenSpy = jest.spyOn(modalService, 'open') | ||||
|     completeSetup() | ||||
|     component.showSystemStatus() | ||||
|     expect(modalOpenSpy).toHaveBeenCalledWith(SystemStatusDialogComponent, { | ||||
|       size: 'xl', | ||||
|     }) | ||||
|   }) | ||||
| }) | ||||
|   | ||||
| @@ -9,7 +9,11 @@ import { | ||||
| } from '@angular/core' | ||||
| import { FormGroup, FormControl } from '@angular/forms' | ||||
| import { ActivatedRoute, Router } from '@angular/router' | ||||
| import { NgbNavChangeEvent } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { | ||||
|   NgbModal, | ||||
|   NgbModalRef, | ||||
|   NgbNavChangeEvent, | ||||
| } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms' | ||||
| import { TourService } from 'ngx-ui-tour-ng-bootstrap' | ||||
| import { | ||||
| @@ -40,6 +44,12 @@ import { | ||||
| } from 'src/app/services/settings.service' | ||||
| import { ToastService, Toast } from 'src/app/services/toast.service' | ||||
| import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' | ||||
| import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component' | ||||
| import { SystemStatusService } from 'src/app/services/system-status.service' | ||||
| import { | ||||
|   SystemStatusItemStatus, | ||||
|   SystemStatus, | ||||
| } from 'src/app/data/system-status' | ||||
|  | ||||
| enum SettingsNavIDs { | ||||
|   General = 1, | ||||
| @@ -111,6 +121,18 @@ export class SettingsComponent | ||||
|   users: User[] | ||||
|   groups: Group[] | ||||
|  | ||||
|   private systemStatus: SystemStatus | ||||
|  | ||||
|   get systemStatusHasErrors(): boolean { | ||||
|     return ( | ||||
|       this.systemStatus.database.status === SystemStatusItemStatus.ERROR || | ||||
|       this.systemStatus.tasks.redis_status === SystemStatusItemStatus.ERROR || | ||||
|       this.systemStatus.tasks.celery_status === SystemStatusItemStatus.ERROR || | ||||
|       this.systemStatus.tasks.index_status === SystemStatusItemStatus.ERROR || | ||||
|       this.systemStatus.tasks.classifier_status === SystemStatusItemStatus.ERROR | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   get computedDateLocale(): string { | ||||
|     return ( | ||||
|       this.settingsForm.value.dateLocale || | ||||
| @@ -131,7 +153,9 @@ export class SettingsComponent | ||||
|     private usersService: UserService, | ||||
|     private groupsService: GroupService, | ||||
|     private router: Router, | ||||
|     public permissionsService: PermissionsService | ||||
|     public permissionsService: PermissionsService, | ||||
|     private modalService: NgbModal, | ||||
|     private systemStatusService: SystemStatusService | ||||
|   ) { | ||||
|     super() | ||||
|     this.settings.settingsSaved.subscribe(() => { | ||||
| @@ -360,6 +384,17 @@ export class SettingsComponent | ||||
|       // prevents loss of unsaved changes | ||||
|       this.settingsForm.patchValue(currentFormValue) | ||||
|     } | ||||
|  | ||||
|     if ( | ||||
|       this.permissionsService.currentUserCan( | ||||
|         PermissionAction.View, | ||||
|         PermissionType.Admin | ||||
|       ) | ||||
|     ) { | ||||
|       this.systemStatusService.get().subscribe((status) => { | ||||
|         this.systemStatus = status | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private emptyGroup(group: FormGroup) { | ||||
| @@ -565,4 +600,14 @@ export class SettingsComponent | ||||
|   clearThemeColor() { | ||||
|     this.settingsForm.get('themeColor').patchValue('') | ||||
|   } | ||||
|  | ||||
|   showSystemStatus() { | ||||
|     const modal: NgbModalRef = this.modalService.open( | ||||
|       SystemStatusDialogComponent, | ||||
|       { | ||||
|         size: 'xl', | ||||
|       } | ||||
|     ) | ||||
|     modal.componentInstance.status = this.systemStatus | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,154 @@ | ||||
| <div class="modal-header"> | ||||
|   <h5 class="modal-title" id="modal-basic-title" i18n>System Status</h5> | ||||
|   <button type="button" class="btn-close" aria-label="Close" (click)="close()"></button> | ||||
| </div> | ||||
| <div class="modal-body"> | ||||
|   @if (!status) { | ||||
|     <div class="w-100 h-100 d-flex align-items-center justify-content-center"> | ||||
|       <div> | ||||
|         <div class="spinner-border spinner-border-sm me-2" role="status"></div> | ||||
|         <ng-container i18n>Loading...</ng-container> | ||||
|       </div> | ||||
|     </div> | ||||
|   } @else { | ||||
|     <div class="row row-cols-1 row-cols-md-3 g-3"> | ||||
|       <div class="col"> | ||||
|         <div class="card bg-light h-100"> | ||||
|           <div class="card-header"> | ||||
|             <h5 class="card-title mb-0" i18n>Environment</h5> | ||||
|           </div> | ||||
|           <div class="card-body"> | ||||
|             <dl class="card-text"> | ||||
|               <dt i18n>Paperless-ngx Version</dt> | ||||
|               <dd>{{status.pngx_version}}</dd> | ||||
|               <dt i18n>Install Type</dt> | ||||
|               <dd>{{status.install_type}}</dd> | ||||
|               <dt i18n>Server OS</dt> | ||||
|               <dd>{{status.server_os}}</dd> | ||||
|               <dt i18n>Media Storage</dt> | ||||
|               <dd> | ||||
|                 <ngb-progressbar style="height: 4px;" class="mt-2 mb-1" type="primary" [max]="status.storage.total" [value]="status.storage.total - status.storage.available"></ngb-progressbar> | ||||
|                 <span class="small">{{status.storage.available | filesize}} <ng-container i18n>available</ng-container> ({{status.storage.total | filesize}} <ng-container i18n>total</ng-container>)</span> | ||||
|               </dd> | ||||
|             </dl> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="col"> | ||||
|         <div class="card bg-light h-100"> | ||||
|           <div class="card-header"> | ||||
|             <h5 class="card-title mb-0" i18n>Database</h5> | ||||
|           </div> | ||||
|           <div class="card-body"> | ||||
|             <dl class="card-text"> | ||||
|               <dt i18n>Type</dt> | ||||
|               <dd>{{status.database.type}}</dd> | ||||
|               <dt i18n>Status</dt> | ||||
|               <dd class="d-flex align-items-center"> | ||||
|                 {{status.database.status}} | ||||
|                 @if (status.database.status === 'OK') { | ||||
|                   <i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" ngbPopover="{{status.database.url}}" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } @else { | ||||
|                   <i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.database.url}}: {{status.database.error}}" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } | ||||
|               </dd> | ||||
|               <dt i18n>Migration Status</dt> | ||||
|               <dd class="d-flex align-items-center"> | ||||
|                 @if (status.database.migration_status.unapplied_migrations.length === 0) { | ||||
|                   <ng-container>Up to date</ng-container><i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="migrationStatus" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } @else { | ||||
|                   <ng-container>{{status.database.migration_status.unapplied_migrations.length}} Pending</ng-container><i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="migrationStatus" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } | ||||
|                 <ng-template #migrationStatus> | ||||
|                   <h6><ng-container i18n>Latest Migration</ng-container>:</h6> <span class="font-monospace small">{{status.database.migration_status.latest_migration}}</span> | ||||
|                   @if (status.database.migration_status.unapplied_migrations.length > 0) { | ||||
|                     <h6 class="mt-3"><ng-container i18n>Pending Migrations</ng-container>:</h6> | ||||
|                     <ul> | ||||
|                       @for (migration of status.database.migration_status.unapplied_migrations; track migration) { | ||||
|                         <li class="font-monospace small">{{migration}}</li> | ||||
|                       } | ||||
|                     </ul> | ||||
|                   } | ||||
|                 </ng-template> | ||||
|               </dd> | ||||
|             </dl> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="col"> | ||||
|         <div class="card bg-light h-100"> | ||||
|           <div class="card-header"> | ||||
|             <h5 class="card-title mb-0" i18n>Tasks</h5> | ||||
|           </div> | ||||
|           <div class="card-body"> | ||||
|             <dl class="card-text"> | ||||
|               <dt i18n>Redis Status</dt> | ||||
|               <dd class="d-flex align-items-center"> | ||||
|                 {{status.tasks.redis_status}} | ||||
|                 @if (status.tasks.redis_status === 'OK') { | ||||
|                   <i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" ngbPopover="{{status.tasks.redis_url}}" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } @else { | ||||
|                   <i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.redis_url}}: {{status.tasks.redis_error}}" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } | ||||
|               </dd> | ||||
|               <dt i18n>Celery Status</dt> | ||||
|               <dd class="d-flex align-items-center"> | ||||
|                 {{status.tasks.celery_status}} | ||||
|                 @if (status.tasks.celery_status === 'OK') { | ||||
|                   <i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs> | ||||
|                 } @else { | ||||
|                   <i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs> | ||||
|                 } | ||||
|               </dd> | ||||
|               <dt i18n>Search Index</dt> | ||||
|               <dd class="d-flex align-items-center"> | ||||
|                 {{status.tasks.index_status}} | ||||
|                 @if (status.tasks.index_status === 'OK') { | ||||
|                   @if (isStale(status.tasks.index_last_modified)) { | ||||
|                     <i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="indexStatus" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                   } @else { | ||||
|                     <i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="indexStatus" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                   } | ||||
|                 } @else { | ||||
|                   <i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.index_error}}" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } | ||||
|               </dd> | ||||
|               <ng-template #indexStatus> | ||||
|                 <h6><ng-container i18n>Last Updated</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.index_last_modified | customDate:'medium'}}</span> | ||||
|               </ng-template> | ||||
|               <dt i18n>Classifier</dt> | ||||
|               <dd class="d-flex align-items-center"> | ||||
|                 {{status.tasks.classifier_status}} | ||||
|                 @if (status.tasks.classifier_status === 'OK') { | ||||
|                   @if (isStale(status.tasks.classifier_last_trained)) { | ||||
|                     <i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="classifierStatus" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                   } @else { | ||||
|                     <i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="classifierStatus" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                   } | ||||
|                 } @else { | ||||
|                   <i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.classifier_error}}" triggers="mouseenter:mouseleave"></i-bs> | ||||
|                 } | ||||
|               </dd> | ||||
|               <ng-template #classifierStatus> | ||||
|                 <h6><ng-container i18n>Last Trained</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.classifier_last_trained | customDate:'medium'}}</span> | ||||
|               </ng-template> | ||||
|             </dl> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   } | ||||
| </div> | ||||
| <div class="modal-footer"> | ||||
|   <button class="btn btn-sm btn-outline-secondary" (click)="copy()"> | ||||
|     @if (!copied) { | ||||
|       <i-bs name="clipboard-fill"></i-bs>  | ||||
|     } | ||||
|     @if (copied) { | ||||
|       <i-bs name="clipboard-check-fill"></i-bs>  | ||||
|     } | ||||
|     <ng-container i18n>Copy</ng-container> | ||||
|   </button> | ||||
| </div> | ||||
| @@ -0,0 +1,103 @@ | ||||
| import { | ||||
|   ComponentFixture, | ||||
|   TestBed, | ||||
|   fakeAsync, | ||||
|   tick, | ||||
| } from '@angular/core/testing' | ||||
| import { | ||||
|   NgbActiveModal, | ||||
|   NgbModalModule, | ||||
|   NgbPopoverModule, | ||||
|   NgbProgressbarModule, | ||||
| } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard' | ||||
| import { SystemStatusDialogComponent } from './system-status-dialog.component' | ||||
| import { | ||||
|   SystemStatusItemStatus, | ||||
|   InstallType, | ||||
|   SystemStatus, | ||||
| } from 'src/app/data/system-status' | ||||
| import { HttpClientTestingModule } from '@angular/common/http/testing' | ||||
| import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' | ||||
| import { NgxFilesizeModule } from 'ngx-filesize' | ||||
|  | ||||
| const status: SystemStatus = { | ||||
|   pngx_version: '2.4.3', | ||||
|   server_os: 'macOS-14.1.1-arm64-arm-64bit', | ||||
|   install_type: InstallType.BareMetal, | ||||
|   storage: { total: 494384795648, available: 13573525504 }, | ||||
|   database: { | ||||
|     type: 'sqlite', | ||||
|     url: '/paperless-ngx/data/db.sqlite3', | ||||
|     status: SystemStatusItemStatus.ERROR, | ||||
|     error: null, | ||||
|     migration_status: { | ||||
|       latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data', | ||||
|       unapplied_migrations: [], | ||||
|     }, | ||||
|   }, | ||||
|   tasks: { | ||||
|     redis_url: 'redis://localhost:6379', | ||||
|     redis_status: SystemStatusItemStatus.ERROR, | ||||
|     redis_error: 'Error 61 connecting to localhost:6379. Connection refused.', | ||||
|     celery_status: SystemStatusItemStatus.ERROR, | ||||
|     index_status: SystemStatusItemStatus.OK, | ||||
|     index_last_modified: new Date().toISOString(), | ||||
|     index_error: null, | ||||
|     classifier_status: SystemStatusItemStatus.OK, | ||||
|     classifier_last_trained: new Date().toISOString(), | ||||
|     classifier_error: null, | ||||
|   }, | ||||
| } | ||||
|  | ||||
| describe('SystemStatusDialogComponent', () => { | ||||
|   let component: SystemStatusDialogComponent | ||||
|   let fixture: ComponentFixture<SystemStatusDialogComponent> | ||||
|   let clipboard: Clipboard | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     await TestBed.configureTestingModule({ | ||||
|       declarations: [SystemStatusDialogComponent], | ||||
|       providers: [NgbActiveModal], | ||||
|       imports: [ | ||||
|         NgbModalModule, | ||||
|         ClipboardModule, | ||||
|         HttpClientTestingModule, | ||||
|         NgxBootstrapIconsModule.pick(allIcons), | ||||
|         NgxFilesizeModule, | ||||
|         NgbPopoverModule, | ||||
|         NgbProgressbarModule, | ||||
|       ], | ||||
|     }).compileComponents() | ||||
|  | ||||
|     fixture = TestBed.createComponent(SystemStatusDialogComponent) | ||||
|     component = fixture.componentInstance | ||||
|     component.status = status | ||||
|     clipboard = TestBed.inject(Clipboard) | ||||
|     fixture.detectChanges() | ||||
|   }) | ||||
|  | ||||
|   it('should close the active modal', () => { | ||||
|     const closeSpy = jest.spyOn(component.activeModal, 'close') | ||||
|     component.close() | ||||
|     expect(closeSpy).toHaveBeenCalled() | ||||
|   }) | ||||
|  | ||||
|   it('should copy the system status to clipboard', fakeAsync(() => { | ||||
|     jest.spyOn(clipboard, 'copy') | ||||
|     component.copy() | ||||
|     expect(clipboard.copy).toHaveBeenCalledWith( | ||||
|       JSON.stringify(component.status) | ||||
|     ) | ||||
|     expect(component.copied).toBeTruthy() | ||||
|     tick(3000) | ||||
|     expect(component.copied).toBeFalsy() | ||||
|   })) | ||||
|  | ||||
|   it('should calculate if date is stale', () => { | ||||
|     const date = new Date() | ||||
|     date.setHours(date.getHours() - 25) | ||||
|     expect(component.isStale(date.toISOString())).toBeTruthy() | ||||
|     expect(component.isStale(date.toISOString(), 26)).toBeFalsy() | ||||
|   }) | ||||
| }) | ||||
| @@ -0,0 +1,39 @@ | ||||
| import { Component, Input } from '@angular/core' | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { SystemStatus } from 'src/app/data/system-status' | ||||
| import { SystemStatusService } from 'src/app/services/system-status.service' | ||||
| import { Clipboard } from '@angular/cdk/clipboard' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'pngx-system-status-dialog', | ||||
|   templateUrl: './system-status-dialog.component.html', | ||||
|   styleUrl: './system-status-dialog.component.scss', | ||||
| }) | ||||
| export class SystemStatusDialogComponent { | ||||
|   public status: SystemStatus | ||||
|  | ||||
|   public copied: boolean = false | ||||
|  | ||||
|   constructor( | ||||
|     public activeModal: NgbActiveModal, | ||||
|     private clipboard: Clipboard | ||||
|   ) {} | ||||
|  | ||||
|   public close() { | ||||
|     this.activeModal.close() | ||||
|   } | ||||
|  | ||||
|   public copy() { | ||||
|     this.clipboard.copy(JSON.stringify(this.status)) | ||||
|     this.copied = true | ||||
|     setTimeout(() => { | ||||
|       this.copied = false | ||||
|     }, 3000) | ||||
|   } | ||||
|  | ||||
|   public isStale(dateStr: string, hours: number = 24): boolean { | ||||
|     const date = new Date(dateStr) | ||||
|     const now = new Date() | ||||
|     return now.getTime() - date.getTime() > hours * 60 * 60 * 1000 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										41
									
								
								src-ui/src/app/data/system-status.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src-ui/src/app/data/system-status.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| export enum InstallType { | ||||
|   Containerized = 'containerized', | ||||
|   BareMetal = 'bare-metal', | ||||
| } | ||||
|  | ||||
| export enum SystemStatusItemStatus { | ||||
|   OK = 'OK', | ||||
|   ERROR = 'ERROR', | ||||
| } | ||||
|  | ||||
| export interface SystemStatus { | ||||
|   pngx_version: string | ||||
|   server_os: string | ||||
|   install_type: InstallType | ||||
|   storage: { | ||||
|     total: number | ||||
|     available: number | ||||
|   } | ||||
|   database: { | ||||
|     type: string | ||||
|     url: string | ||||
|     status: SystemStatusItemStatus | ||||
|     error?: string | ||||
|     migration_status: { | ||||
|       latest_migration: string | ||||
|       unapplied_migrations: string[] | ||||
|     } | ||||
|   } | ||||
|   tasks: { | ||||
|     redis_url: string | ||||
|     redis_status: SystemStatusItemStatus | ||||
|     redis_error: string | ||||
|     celery_status: SystemStatusItemStatus | ||||
|     index_status: SystemStatusItemStatus | ||||
|     index_last_modified: string // ISO date string | ||||
|     index_error: string | ||||
|     classifier_status: SystemStatusItemStatus | ||||
|     classifier_last_trained: string // ISO date string | ||||
|     classifier_error: string | ||||
|   } | ||||
| } | ||||
							
								
								
									
										35
									
								
								src-ui/src/app/services/system-status.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src-ui/src/app/services/system-status.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import { TestBed } from '@angular/core/testing' | ||||
|  | ||||
| import { SystemStatusService } from './system-status.service' | ||||
| import { | ||||
|   HttpClientTestingModule, | ||||
|   HttpTestingController, | ||||
| } from '@angular/common/http/testing' | ||||
| import { environment } from 'src/environments/environment' | ||||
|  | ||||
| describe('SystemStatusService', () => { | ||||
|   let httpTestingController: HttpTestingController | ||||
|   let service: SystemStatusService | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     TestBed.configureTestingModule({ | ||||
|       providers: [SystemStatusService], | ||||
|       imports: [HttpClientTestingModule], | ||||
|     }) | ||||
|  | ||||
|     httpTestingController = TestBed.inject(HttpTestingController) | ||||
|     service = TestBed.inject(SystemStatusService) | ||||
|   }) | ||||
|  | ||||
|   afterEach(() => { | ||||
|     httpTestingController.verify() | ||||
|   }) | ||||
|  | ||||
|   it('calls get status endpoint', () => { | ||||
|     service.get().subscribe() | ||||
|     const req = httpTestingController.expectOne( | ||||
|       `${environment.apiBaseUrl}status/` | ||||
|     ) | ||||
|     expect(req.request.method).toEqual('GET') | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										20
									
								
								src-ui/src/app/services/system-status.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src-ui/src/app/services/system-status.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import { HttpClient } from '@angular/common/http' | ||||
| import { Injectable } from '@angular/core' | ||||
| import { Observable } from 'rxjs' | ||||
| import { SystemStatus } from '../data/system-status' | ||||
| import { environment } from 'src/environments/environment' | ||||
|  | ||||
| @Injectable({ | ||||
|   providedIn: 'root', | ||||
| }) | ||||
| export class SystemStatusService { | ||||
|   private endpoint = 'status' | ||||
|  | ||||
|   constructor(private http: HttpClient) {} | ||||
|  | ||||
|   get(): Observable<SystemStatus> { | ||||
|     return this.http.get<SystemStatus>( | ||||
|       `${environment.apiBaseUrl}${this.endpoint}/` | ||||
|     ) | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon