mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Feature: support sorting sidebar saved views (#4381)
This commit is contained in:
		| @@ -421,11 +421,11 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">207</context> | ||||
|           <context context-type="linenumber">218</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">210</context> | ||||
|           <context context-type="linenumber">221</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3894950702316166331" datatype="html"> | ||||
| @@ -511,11 +511,11 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">185</context> | ||||
|           <context context-type="linenumber">196</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">188</context> | ||||
|           <context context-type="linenumber">199</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1685061484835793745" datatype="html"> | ||||
| @@ -1355,67 +1355,67 @@ | ||||
|         <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">362</context> | ||||
|           <context context-type="linenumber">370</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3891152409365583719" datatype="html"> | ||||
|         <source>Settings saved</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">484</context> | ||||
|           <context context-type="linenumber">492</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">485</context> | ||||
|           <context context-type="linenumber">493</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">489</context> | ||||
|           <context context-type="linenumber">497</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">490</context> | ||||
|           <context context-type="linenumber">498</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">500</context> | ||||
|           <context context-type="linenumber">508</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">104</context> | ||||
|           <context context-type="linenumber">114</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">508</context> | ||||
|           <context context-type="linenumber">516</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">515</context> | ||||
|           <context context-type="linenumber">523</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5260584511980773458" datatype="html"> | ||||
|         <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">538</context> | ||||
|           <context context-type="linenumber">546</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2991443309752293110" datatype="html"> | ||||
| @@ -1426,7 +1426,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">199</context> | ||||
|           <context context-type="linenumber">210</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="103921551219467537" datatype="html"> | ||||
| @@ -1635,11 +1635,11 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">192</context> | ||||
|           <context context-type="linenumber">203</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">195</context> | ||||
|           <context context-type="linenumber">206</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4555457172864212828" datatype="html"> | ||||
| @@ -1969,11 +1969,11 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">214</context> | ||||
|           <context context-type="linenumber">225</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">217</context> | ||||
|           <context context-type="linenumber">228</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6570363013146073520" datatype="html"> | ||||
| @@ -2026,36 +2026,36 @@ | ||||
|         <source>Open documents</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">107</context> | ||||
|           <context context-type="linenumber">118</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5687256342387781369" datatype="html"> | ||||
|         <source>Close all</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">123</context> | ||||
|           <context context-type="linenumber">134</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">126</context> | ||||
|           <context context-type="linenumber">137</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3897348120591552265" datatype="html"> | ||||
|         <source>Manage</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">133</context> | ||||
|           <context context-type="linenumber">144</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7437910965833684826" datatype="html"> | ||||
|         <source>Correspondents</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">137</context> | ||||
|           <context context-type="linenumber">148</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">140</context> | ||||
|           <context context-type="linenumber">151</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context> | ||||
| @@ -2066,11 +2066,11 @@ | ||||
|         <source>Tags</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">144</context> | ||||
|           <context context-type="linenumber">155</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">147</context> | ||||
|           <context context-type="linenumber">158</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/input/tags/tags.component.ts</context> | ||||
| @@ -2097,102 +2097,116 @@ | ||||
|         <source>Document types</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">151</context> | ||||
|           <context context-type="linenumber">162</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">154</context> | ||||
|           <context context-type="linenumber">165</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8835528846812581148" datatype="html"> | ||||
|         <source>Storage paths</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">158</context> | ||||
|           <context context-type="linenumber">169</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">161</context> | ||||
|           <context context-type="linenumber">172</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4462691404891390153" datatype="html"> | ||||
|         <source>Consumption templates</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">165</context> | ||||
|           <context context-type="linenumber">176</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5433675495457939071" datatype="html"> | ||||
|         <source>Templates</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">168</context> | ||||
|           <context context-type="linenumber">179</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1292737233370901804" datatype="html"> | ||||
|         <source>Mail</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">172</context> | ||||
|           <context context-type="linenumber">183</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">175</context> | ||||
|           <context context-type="linenumber">186</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7844706011418789951" datatype="html"> | ||||
|         <source>Administration</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">181</context> | ||||
|           <context context-type="linenumber">192</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5537285341303594392" datatype="html"> | ||||
|         <source>File Tasks<x id="START_TAG_SPAN_1" ctype="x-span_1" equiv-text="<span *ngIf="tasksService.failedFileTasks.length > 0">"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-danger ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.failedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">203</context> | ||||
|           <context context-type="linenumber">214</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1534029177398918729" datatype="html"> | ||||
|         <source>GitHub</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">223</context> | ||||
|           <context context-type="linenumber">234</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4112664765954374539" datatype="html"> | ||||
|         <source>is available.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">229</context> | ||||
|           <context context-type="linenumber">240</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1175891574282637937" datatype="html"> | ||||
|         <source>Click to view.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">229</context> | ||||
|           <context context-type="linenumber">240</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9811291095862612" datatype="html"> | ||||
|         <source>Paperless-ngx can automatically check for updates</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">233</context> | ||||
|           <context context-type="linenumber">244</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="894819944961861800" datatype="html"> | ||||
|         <source> How does this work? </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">240,242</context> | ||||
|           <context context-type="linenumber">251,253</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="509090351011426949" datatype="html"> | ||||
|         <source>Update available</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> | ||||
|           <context context-type="linenumber">262</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1542489069631984294" datatype="html"> | ||||
|         <source>Sidebar views updated</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">248</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3547923076537026828" datatype="html"> | ||||
|         <source>Error updating sidebar views</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">251</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
| @@ -2200,7 +2214,7 @@ | ||||
|         <source>An error occurred while saving update checking settings.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">237</context> | ||||
|           <context context-type="linenumber">272</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8700121026680200191" datatype="html"> | ||||
| @@ -3612,28 +3626,28 @@ | ||||
|         <source>Hello <x id="PH" equiv-text="this.settingsService.displayName"/>, welcome to Paperless-ngx</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">53</context> | ||||
|           <context context-type="linenumber">38</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5334686081082652461" datatype="html"> | ||||
|         <source>Welcome to Paperless-ngx</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">55</context> | ||||
|           <context context-type="linenumber">40</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1325877348738783391" datatype="html"> | ||||
|         <source>Dashboard updated</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">86</context> | ||||
|           <context context-type="linenumber">71</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3214475953924351473" datatype="html"> | ||||
|         <source>Error updating dashboard</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">89</context> | ||||
|           <context context-type="linenumber">74</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2946624699882754313" datatype="html"> | ||||
| @@ -5999,224 +6013,224 @@ | ||||
|         <source>English (US)</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">154</context> | ||||
|           <context context-type="linenumber">155</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7318555235181361185" datatype="html"> | ||||
|         <source>Afrikaans</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">160</context> | ||||
|           <context context-type="linenumber">161</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6269202464699193298" datatype="html"> | ||||
|         <source>Arabic</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">166</context> | ||||
|           <context context-type="linenumber">167</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3098941349689899577" datatype="html"> | ||||
|         <source>Belarusian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">172</context> | ||||
|           <context context-type="linenumber">173</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1001043467371963032" datatype="html"> | ||||
|         <source>Catalan</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">178</context> | ||||
|           <context context-type="linenumber">179</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2719780722934172508" datatype="html"> | ||||
|         <source>Czech</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">184</context> | ||||
|           <context context-type="linenumber">185</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2924289692679201020" datatype="html"> | ||||
|         <source>Danish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">190</context> | ||||
|           <context context-type="linenumber">191</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1858110241312746425" datatype="html"> | ||||
|         <source>German</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">196</context> | ||||
|           <context context-type="linenumber">197</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7067741492320440272" datatype="html"> | ||||
|         <source>Greek</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">202</context> | ||||
|           <context context-type="linenumber">203</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6987083569809053351" datatype="html"> | ||||
|         <source>English (GB)</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">208</context> | ||||
|           <context context-type="linenumber">209</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5190825892106392539" datatype="html"> | ||||
|         <source>Spanish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">214</context> | ||||
|           <context context-type="linenumber">215</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="861663369293303028" datatype="html"> | ||||
|         <source>Finnish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">220</context> | ||||
|           <context context-type="linenumber">221</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7633754075223722162" datatype="html"> | ||||
|         <source>French</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">226</context> | ||||
|           <context context-type="linenumber">227</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2935232983274991580" datatype="html"> | ||||
|         <source>Italian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">232</context> | ||||
|           <context context-type="linenumber">233</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1334425850005897370" datatype="html"> | ||||
|         <source>Luxembourgish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">238</context> | ||||
|           <context context-type="linenumber">239</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3071065188816255493" datatype="html"> | ||||
|         <source>Dutch</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">244</context> | ||||
|           <context context-type="linenumber">245</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8069284467804715623" datatype="html"> | ||||
|         <source>Norwegian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">250</context> | ||||
|           <context context-type="linenumber">251</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="792060551707690640" datatype="html"> | ||||
|         <source>Polish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">256</context> | ||||
|           <context context-type="linenumber">257</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9184513005098760425" datatype="html"> | ||||
|         <source>Portuguese (Brazil)</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">262</context> | ||||
|           <context context-type="linenumber">263</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="153799456510623899" datatype="html"> | ||||
|         <source>Portuguese</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">268</context> | ||||
|           <context context-type="linenumber">269</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8118856427047826368" datatype="html"> | ||||
|         <source>Romanian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">274</context> | ||||
|           <context context-type="linenumber">275</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7137419789978325708" datatype="html"> | ||||
|         <source>Russian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">280</context> | ||||
|           <context context-type="linenumber">281</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9102963095355753902" datatype="html"> | ||||
|         <source>Slovak</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">286</context> | ||||
|           <context context-type="linenumber">287</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4287008301409320881" datatype="html"> | ||||
|         <source>Slovenian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">292</context> | ||||
|           <context context-type="linenumber">293</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8608389829607915090" datatype="html"> | ||||
|         <source>Serbian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">298</context> | ||||
|           <context context-type="linenumber">299</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="499386805970351976" datatype="html"> | ||||
|         <source>Swedish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">304</context> | ||||
|           <context context-type="linenumber">305</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5682359291233237791" datatype="html"> | ||||
|         <source>Turkish</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">310</context> | ||||
|           <context context-type="linenumber">311</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3578644052206125685" datatype="html"> | ||||
|         <source>Ukrainian</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">316</context> | ||||
|           <context context-type="linenumber">317</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4689443708886954687" datatype="html"> | ||||
|         <source>Chinese Simplified</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">322</context> | ||||
|           <context context-type="linenumber">323</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4912706592792948707" datatype="html"> | ||||
|         <source>ISO 8601</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">339</context> | ||||
|           <context context-type="linenumber">340</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="313643372755303297" datatype="html"> | ||||
|         <source>Successfully completed one-time migratration of settings to the database!</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">458</context> | ||||
|           <context context-type="linenumber">459</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5558341108007064934" datatype="html"> | ||||
|         <source>Unable to migrate settings to the database, please try saving manually.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/settings.service.ts</context> | ||||
|           <context context-type="linenumber">459</context> | ||||
|           <context context-type="linenumber">460</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1168781785897678748" datatype="html"> | ||||
|   | ||||
| @@ -180,11 +180,23 @@ describe('SettingsComponent', () => { | ||||
|     activatedRoute.snapshot.fragment = '#notifications' | ||||
|     const scrollSpy = jest.spyOn(viewportScroller, 'scrollToAnchor') | ||||
|     component.ngOnInit() | ||||
|     expect(component.activeNavID).toEqual(3) // Users & Groups | ||||
|     expect(component.activeNavID).toEqual(3) // Notifications | ||||
|     component.ngAfterViewInit() | ||||
|     expect(scrollSpy).toHaveBeenCalledWith('#notifications') | ||||
|   }) | ||||
|  | ||||
|   it('should enable organizing of sidebar saved views even on direct navigation', () => { | ||||
|     completeSetup() | ||||
|     jest | ||||
|       .spyOn(activatedRoute, 'paramMap', 'get') | ||||
|       .mockReturnValue(of(convertToParamMap({ section: 'savedviews' }))) | ||||
|     activatedRoute.snapshot.fragment = '#savedviews' | ||||
|     component.ngOnInit() | ||||
|     expect(component.activeNavID).toEqual(4) // Saved Views | ||||
|     component.ngAfterViewInit() | ||||
|     expect(settingsService.organizingSidebarSavedViews).toBeTruthy() | ||||
|   }) | ||||
|  | ||||
|   it('should support save saved views, show error', () => { | ||||
|     completeSetup() | ||||
|  | ||||
|   | ||||
| @@ -194,6 +194,9 @@ export class SettingsComponent | ||||
|         if (navIDKey) { | ||||
|           this.activeNavID = SettingsNavIDs[navIDKey] | ||||
|         } | ||||
|         if (this.activeNavID === SettingsNavIDs.SavedViews) { | ||||
|           this.settings.organizingSidebarSavedViews = true | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| @@ -275,11 +278,15 @@ export class SettingsComponent | ||||
|       this.router | ||||
|         .navigate(['settings', foundNavIDkey.toLowerCase()]) | ||||
|         .then((navigated) => { | ||||
|           this.settings.organizingSidebarSavedViews = false | ||||
|           if (!navigated && this.isDirty) { | ||||
|             this.activeNavID = navChangeEvent.activeId | ||||
|           } else if (navigated && this.isDirty) { | ||||
|             this.initialize() | ||||
|           } | ||||
|           if (this.activeNavID === SettingsNavIDs.SavedViews) { | ||||
|             this.settings.organizingSidebarSavedViews = true | ||||
|           } | ||||
|         }) | ||||
|   } | ||||
|  | ||||
| @@ -352,6 +359,7 @@ export class SettingsComponent | ||||
|   ngOnDestroy() { | ||||
|     if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save | ||||
|     this.storeSub && this.storeSub.unsubscribe() | ||||
|     this.settings.organizingSidebarSavedViews = false | ||||
|   } | ||||
|  | ||||
|   deleteSavedView(savedView: PaperlessSavedView) { | ||||
|   | ||||
| @@ -87,17 +87,28 @@ | ||||
|           </li> | ||||
|         </ul> | ||||
|         <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }"> | ||||
|           <h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted" *ngIf='savedViewService.loading || savedViewService.sidebarViews.length > 0'> | ||||
|           <h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted" *ngIf='savedViewService.loading || sidebarViews?.length > 0'> | ||||
|             <span i18n>Saved views</span> | ||||
|             <div *ngIf="savedViewService.loading" class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div> | ||||
|           </h6> | ||||
|           <ul class="nav flex-column mb-2"> | ||||
|             <li class="nav-item w-100" *ngFor="let view of savedViewService.sidebarViews"> | ||||
|           <ul class="nav flex-column mb-2" cdkDropList (cdkDropListDropped)="onDrop($event)"> | ||||
|             <li class="nav-item w-100" *ngFor="let view of sidebarViews" | ||||
|               cdkDrag | ||||
|               [cdkDragDisabled]="!settingsService.organizingSidebarSavedViews" | ||||
|               cdkDragPreviewContainer="parent" | ||||
|               cdkDragPreviewClass="navItemDrag" | ||||
|               (cdkDragStarted)="onDragStart($event)" | ||||
|               (cdkDragEnded)="onDragEnd($event)"> | ||||
|               <a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}" routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name" [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> | ||||
|                 <svg class="sidebaricon" fill="currentColor"> | ||||
|                   <use xlink:href="assets/bootstrap-icons.svg#funnel"/> | ||||
|                 </svg><span> {{view.name}}</span> | ||||
|               </a> | ||||
|               <div *ngIf="settingsService.organizingSidebarSavedViews" class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle> | ||||
|                 <svg class="sidebaricon text-muted" fill="currentColor"> | ||||
|                   <use xlink:href="assets/bootstrap-icons.svg#grip-vertical"/> | ||||
|                 </svg> | ||||
|               </div> | ||||
|             </li> | ||||
|           </ul> | ||||
|         </ng-container> | ||||
|   | ||||
| @@ -169,6 +169,7 @@ main { | ||||
|  | ||||
| .nav-item { | ||||
|   position: relative; | ||||
|   list-style-type: none; | ||||
|  | ||||
|   &:hover .close { | ||||
|     display: block; | ||||
| @@ -310,3 +311,11 @@ main { | ||||
|     opacity: 100%; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .nav-item .position-absolute { | ||||
|   cursor: move; | ||||
| } | ||||
|  | ||||
| ::ng-deep .navItemDrag .position-absolute svg { | ||||
|   display: none; | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||
| import { RemoteVersionService } from 'src/app/services/rest/remote-version.service' | ||||
| import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' | ||||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms' | ||||
| import { of } from 'rxjs' | ||||
| import { of, throwError } from 'rxjs' | ||||
| import { ToastService } from 'src/app/services/toast.service' | ||||
| import { environment } from 'src/environments/environment' | ||||
| import { OpenDocumentsService } from 'src/app/services/open-documents.service' | ||||
| @@ -30,7 +30,47 @@ import { DocumentListViewService } from 'src/app/services/document-list-view.ser | ||||
| import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type' | ||||
| import { routes } from 'src/app/app-routing.module' | ||||
| import { PermissionsGuard } from 'src/app/guards/permissions.guard' | ||||
| import { CdkDragDrop } from '@angular/cdk/drag-drop' | ||||
| import { PaperlessSavedView } from 'src/app/data/paperless-saved-view' | ||||
|  | ||||
| const saved_views = [ | ||||
|   { | ||||
|     name: 'Saved View 0', | ||||
|     id: 0, | ||||
|     show_on_dashboard: true, | ||||
|     show_in_sidebar: true, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
|     filter_rules: [], | ||||
|   }, | ||||
|   { | ||||
|     name: 'Saved View 1', | ||||
|     id: 1, | ||||
|     show_on_dashboard: false, | ||||
|     show_in_sidebar: false, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
|     filter_rules: [], | ||||
|   }, | ||||
|   { | ||||
|     name: 'Saved View 2', | ||||
|     id: 2, | ||||
|     show_on_dashboard: true, | ||||
|     show_in_sidebar: true, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
|     filter_rules: [], | ||||
|   }, | ||||
|   { | ||||
|     name: 'Saved View 3', | ||||
|     id: 3, | ||||
|     show_on_dashboard: true, | ||||
|     show_in_sidebar: true, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
|     filter_rules: [], | ||||
|   }, | ||||
| ] | ||||
| const document = { id: 2, title: 'Hello world' } | ||||
|  | ||||
| describe('AppFrameComponent', () => { | ||||
| @@ -60,7 +100,19 @@ describe('AppFrameComponent', () => { | ||||
|       ], | ||||
|       providers: [ | ||||
|         SettingsService, | ||||
|         SavedViewService, | ||||
|         { | ||||
|           provide: SavedViewService, | ||||
|           useValue: { | ||||
|             initialize: () => {}, | ||||
|             listAll: () => | ||||
|               of({ | ||||
|                 all: [saved_views.map((v) => v.id)], | ||||
|                 count: saved_views.length, | ||||
|                 results: saved_views, | ||||
|               }), | ||||
|             sidebarViews: saved_views.filter((v) => v.show_in_sidebar), | ||||
|           }, | ||||
|         }, | ||||
|         PermissionsService, | ||||
|         RemoteVersionService, | ||||
|         IfPermissionsDirective, | ||||
| @@ -269,4 +321,45 @@ describe('AppFrameComponent', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|  | ||||
|   it('should disable global dropzone on start drag + drop, re-enable after', () => { | ||||
|     expect(settingsService.globalDropzoneEnabled).toBeTruthy() | ||||
|     component.onDragStart(null) | ||||
|     expect(settingsService.globalDropzoneEnabled).toBeFalsy() | ||||
|     component.onDragEnd(null) | ||||
|     expect(settingsService.globalDropzoneEnabled).toBeTruthy() | ||||
|   }) | ||||
|  | ||||
|   it('should update saved view sorting on drag + drop, show info', () => { | ||||
|     const settingsSpy = jest.spyOn(settingsService, 'updateSidebarViewsSort') | ||||
|     const toastSpy = jest.spyOn(toastService, 'showInfo') | ||||
|     jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true)) | ||||
|     component.onDrop({ previousIndex: 0, currentIndex: 1 } as CdkDragDrop< | ||||
|       PaperlessSavedView[] | ||||
|     >) | ||||
|     expect(settingsSpy).toHaveBeenCalledWith([ | ||||
|       saved_views[2], | ||||
|       saved_views[0], | ||||
|       saved_views[3], | ||||
|     ]) | ||||
|     expect(toastSpy).toHaveBeenCalled() | ||||
|   }) | ||||
|  | ||||
|   it('should update saved view sorting on drag + drop, show error', () => { | ||||
|     jest.spyOn(settingsService, 'get').mockImplementation((key) => { | ||||
|       if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return [] | ||||
|     }) | ||||
|     fixture.destroy() | ||||
|     fixture = TestBed.createComponent(AppFrameComponent) | ||||
|     component = fixture.componentInstance | ||||
|     fixture.detectChanges() | ||||
|     const toastSpy = jest.spyOn(toastService, 'showError') | ||||
|     jest | ||||
|       .spyOn(settingsService, 'storeSettings') | ||||
|       .mockReturnValue(throwError(() => new Error('unable to save'))) | ||||
|     component.onDrop({ previousIndex: 0, currentIndex: 2 } as CdkDragDrop< | ||||
|       PaperlessSavedView[] | ||||
|     >) | ||||
|     expect(toastSpy).toHaveBeenCalled() | ||||
|   }) | ||||
| }) | ||||
|   | ||||
| @@ -32,6 +32,13 @@ import { | ||||
|   PermissionsService, | ||||
|   PermissionType, | ||||
| } from 'src/app/services/permissions.service' | ||||
| import { PaperlessSavedView } from 'src/app/data/paperless-saved-view' | ||||
| import { | ||||
|   CdkDragStart, | ||||
|   CdkDragEnd, | ||||
|   CdkDragDrop, | ||||
|   moveItemInArray, | ||||
| } from '@angular/cdk/drag-drop' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'pngx-app-frame', | ||||
| @@ -42,6 +49,17 @@ export class AppFrameComponent | ||||
|   extends ComponentWithPermissions | ||||
|   implements OnInit, ComponentCanDeactivate | ||||
| { | ||||
|   versionString = `${environment.appTitle} ${environment.version}` | ||||
|   appRemoteVersion: AppRemoteVersion | ||||
|  | ||||
|   isMenuCollapsed: boolean = true | ||||
|  | ||||
|   slimSidebarAnimating: boolean = false | ||||
|  | ||||
|   searchField = new FormControl('') | ||||
|  | ||||
|   sidebarViews: PaperlessSavedView[] | ||||
|  | ||||
|   constructor( | ||||
|     public router: Router, | ||||
|     private activatedRoute: ActivatedRoute, | ||||
| @@ -63,7 +81,7 @@ export class AppFrameComponent | ||||
|         PermissionType.SavedView | ||||
|       ) | ||||
|     ) { | ||||
|       savedViewService.initialize() | ||||
|       this.savedViewService.initialize() | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -72,15 +90,12 @@ export class AppFrameComponent | ||||
|       this.checkForUpdates() | ||||
|     } | ||||
|     this.tasksService.reload() | ||||
|  | ||||
|     this.savedViewService.listAll().subscribe(() => { | ||||
|       this.sidebarViews = this.savedViewService.sidebarViews | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   versionString = `${environment.appTitle} ${environment.version}` | ||||
|   appRemoteVersion: AppRemoteVersion | ||||
|  | ||||
|   isMenuCollapsed: boolean = true | ||||
|  | ||||
|   slimSidebarAnimating: boolean = false | ||||
|  | ||||
|   toggleSlimSidebar(): void { | ||||
|     this.slimSidebarAnimating = true | ||||
|     this.slimSidebarEnabled = !this.slimSidebarEnabled | ||||
| @@ -121,8 +136,6 @@ export class AppFrameComponent | ||||
|     return !this.openDocumentsService.hasDirty() | ||||
|   } | ||||
|  | ||||
|   searchField = new FormControl('') | ||||
|  | ||||
|   get searchFieldEmpty(): boolean { | ||||
|     return this.searchField.value.trim().length == 0 | ||||
|   } | ||||
| @@ -218,6 +231,27 @@ export class AppFrameComponent | ||||
|       }) | ||||
|   } | ||||
|  | ||||
|   onDragStart(event: CdkDragStart) { | ||||
|     this.settingsService.globalDropzoneEnabled = false | ||||
|   } | ||||
|  | ||||
|   onDragEnd(event: CdkDragEnd) { | ||||
|     this.settingsService.globalDropzoneEnabled = true | ||||
|   } | ||||
|  | ||||
|   onDrop(event: CdkDragDrop<PaperlessSavedView[]>) { | ||||
|     moveItemInArray(this.sidebarViews, event.previousIndex, event.currentIndex) | ||||
|  | ||||
|     this.settingsService.updateSidebarViewsSort(this.sidebarViews).subscribe({ | ||||
|       next: () => { | ||||
|         this.toastService.showInfo($localize`Sidebar views updated`) | ||||
|       }, | ||||
|       error: (e) => { | ||||
|         this.toastService.showError($localize`Error updating sidebar views`, e) | ||||
|       }, | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   private checkForUpdates() { | ||||
|     this.remoteVersionService | ||||
|       .checkForUpdates() | ||||
|   | ||||
| @@ -1,19 +1,3 @@ | ||||
| .col-sidebar .row { | ||||
|     top: 3.5rem; | ||||
| } | ||||
|  | ||||
| :host ::ng-deep { | ||||
|     .cdk-drag-placeholder { | ||||
|       opacity: .5; | ||||
|     } | ||||
|  | ||||
|     /* Animate items as they're being sorted. */ | ||||
|     .cdk-drop-list-dragging .cdk-drag { | ||||
|       transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); | ||||
|     } | ||||
|  | ||||
|     /* Animate an item that has been dropped. */ | ||||
|     .cdk-drag-animating { | ||||
|       transition: transform 300ms cubic-bezier(0, 0, 0.2, 1); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -29,22 +29,7 @@ export class DashboardComponent extends ComponentWithPermissions { | ||||
|     super() | ||||
|  | ||||
|     this.savedViewService.listAll().subscribe(() => { | ||||
|       const sorted: number[] = this.settingsService.get( | ||||
|         SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER | ||||
|       ) | ||||
|       this.dashboardViews = | ||||
|         sorted?.length > 0 | ||||
|           ? sorted | ||||
|               .map((id) => | ||||
|                 this.savedViewService.dashboardViews.find((v) => v.id === id) | ||||
|               ) | ||||
|               .concat( | ||||
|                 this.savedViewService.dashboardViews.filter( | ||||
|                   (v) => !sorted.includes(v.id) | ||||
|                 ) | ||||
|               ) | ||||
|               .filter((v) => v) | ||||
|           : [...this.savedViewService.dashboardViews] | ||||
|       this.dashboardViews = this.savedViewService.dashboardViews | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
|         <div class="d-flex justify-content-between align-items-center my-2"> | ||||
|           <div class="progress flex-grow-1"> | ||||
|             <div *ngFor="let filetype of statistics?.document_file_type_counts; let i = index; let last = last" | ||||
|               class="progress-bar bg-primary text-primary-contrast" | ||||
|               class="progress-bar bg-primary" | ||||
|               role="progressbar" | ||||
|               [ngbPopover]="getFileTypeName(filetype)" | ||||
|               i18n-ngbPopover | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <ngb-alert class="pe-3" type="primary" [dismissible]="true" (closed)="dismiss.emit(true)"> | ||||
| <ngb-alert class="pe-3 text-primary-contrast" type="primary" [dismissible]="true" (closed)="dismiss.emit(true)"> | ||||
|   <h4 class="alert-heading"><ng-container i18n>Paperless-ngx is running!</ng-container> 🎉</h4> | ||||
|   <p i18n>You're ready to start uploading documents! Explore the various features of this web app on your own, or start a quick tour using the button below.</p> | ||||
|   <p i18n>More detail on how to use and configure Paperless-ngx is always available in the <a href="https://docs.paperless-ngx.com" target="_blank">documentation</a>.</p> | ||||
|   | ||||
| @@ -43,6 +43,8 @@ export const SETTINGS_KEYS = { | ||||
|     'general-settings:saved-views:warn-on-unsaved-change', | ||||
|   DASHBOARD_VIEWS_SORT_ORDER: | ||||
|     'general-settings:saved-views:dashboard-views-sort-order', | ||||
|   SIDEBAR_VIEWS_SORT_ORDER: | ||||
|     'general-settings:saved-views:sidebar-views-sort-order', | ||||
|   TOUR_COMPLETE: 'general-settings:tour-complete', | ||||
|   DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner', | ||||
|   DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users', | ||||
| @@ -187,4 +189,9 @@ export const SETTINGS: PaperlessUiSetting[] = [ | ||||
|     type: 'array', | ||||
|     default: [], | ||||
|   }, | ||||
|   { | ||||
|     key: SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER, | ||||
|     type: 'array', | ||||
|     default: [], | ||||
|   }, | ||||
| ] | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import { TestBed } from '@angular/core/testing' | ||||
| import { environment } from 'src/environments/environment' | ||||
| import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec' | ||||
| import { SavedViewService } from './saved-view.service' | ||||
| import { SettingsService } from '../settings.service' | ||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||
|  | ||||
| let httpTestingController: HttpTestingController | ||||
| let service: SavedViewService | ||||
| @@ -22,8 +24,8 @@ const saved_views = [ | ||||
|   { | ||||
|     name: 'Saved View 2', | ||||
|     id: 2, | ||||
|     show_on_dashboard: false, | ||||
|     show_in_sidebar: false, | ||||
|     show_on_dashboard: true, | ||||
|     show_in_sidebar: true, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
|     filter_rules: [], | ||||
| @@ -32,6 +34,15 @@ const saved_views = [ | ||||
|     name: 'Saved View 3', | ||||
|     id: 3, | ||||
|     show_on_dashboard: true, | ||||
|     show_in_sidebar: true, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
|     filter_rules: [], | ||||
|   }, | ||||
|   { | ||||
|     name: 'Saved View 4', | ||||
|     id: 4, | ||||
|     show_on_dashboard: false, | ||||
|     show_in_sidebar: false, | ||||
|     sort_field: 'name', | ||||
|     sort_reverse: true, | ||||
| @@ -43,6 +54,8 @@ const saved_views = [ | ||||
| commonAbstractPaperlessServiceTests(endpoint, SavedViewService) | ||||
|  | ||||
| describe(`Additional service tests for SavedViewService`, () => { | ||||
|   let settingsService | ||||
|  | ||||
|   it('should retrieve saved views and sort them', () => { | ||||
|     service.initialize() | ||||
|     const req = httpTestingController.expectOne( | ||||
| @@ -51,9 +64,9 @@ describe(`Additional service tests for SavedViewService`, () => { | ||||
|     req.flush({ | ||||
|       results: saved_views, | ||||
|     }) | ||||
|     expect(service.allViews).toHaveLength(3) | ||||
|     expect(service.dashboardViews).toHaveLength(2) | ||||
|     expect(service.sidebarViews).toHaveLength(1) | ||||
|     expect(service.allViews).toHaveLength(4) | ||||
|     expect(service.dashboardViews).toHaveLength(3) | ||||
|     expect(service.sidebarViews).toHaveLength(3) | ||||
|   }) | ||||
|  | ||||
|   it('should support patchMany', () => { | ||||
| @@ -67,11 +80,36 @@ describe(`Additional service tests for SavedViewService`, () => { | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   it('should sort dashboard views', () => { | ||||
|     service['savedViews'] = saved_views | ||||
|     jest.spyOn(settingsService, 'get').mockImplementation((key) => { | ||||
|       if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [3, 1, 2] | ||||
|     }) | ||||
|     expect(service.dashboardViews).toEqual([ | ||||
|       saved_views[2], | ||||
|       saved_views[0], | ||||
|       saved_views[1], | ||||
|     ]) | ||||
|   }) | ||||
|  | ||||
|   it('should sort sidebar views', () => { | ||||
|     service['savedViews'] = saved_views | ||||
|     jest.spyOn(settingsService, 'get').mockImplementation((key) => { | ||||
|       if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return [3, 1, 2] | ||||
|     }) | ||||
|     expect(service.sidebarViews).toEqual([ | ||||
|       saved_views[2], | ||||
|       saved_views[0], | ||||
|       saved_views[1], | ||||
|     ]) | ||||
|   }) | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     // Dont need to setup again | ||||
|  | ||||
|     httpTestingController = TestBed.inject(HttpTestingController) | ||||
|     service = TestBed.inject(SavedViewService) | ||||
|     settingsService = TestBed.inject(SettingsService) | ||||
|   }) | ||||
|  | ||||
|   afterEach(() => { | ||||
|   | ||||
| @@ -5,6 +5,8 @@ import { tap } from 'rxjs/operators' | ||||
| import { PaperlessSavedView } from 'src/app/data/paperless-saved-view' | ||||
| import { PermissionsService } from '../permissions.service' | ||||
| import { AbstractPaperlessService } from './abstract-paperless-service' | ||||
| import { SettingsService } from '../settings.service' | ||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||
|  | ||||
| @Injectable({ | ||||
|   providedIn: 'root', | ||||
| @@ -12,7 +14,11 @@ import { AbstractPaperlessService } from './abstract-paperless-service' | ||||
| export class SavedViewService extends AbstractPaperlessService<PaperlessSavedView> { | ||||
|   loading: boolean | ||||
|  | ||||
|   constructor(http: HttpClient, permissionService: PermissionsService) { | ||||
|   constructor( | ||||
|     http: HttpClient, | ||||
|     permissionService: PermissionsService, | ||||
|     private settingsService: SettingsService | ||||
|   ) { | ||||
|     super(http, 'saved_views') | ||||
|   } | ||||
|  | ||||
| @@ -25,6 +31,7 @@ export class SavedViewService extends AbstractPaperlessService<PaperlessSavedVie | ||||
|     this.listAll().subscribe((r) => { | ||||
|       this.savedViews = r.results | ||||
|       this.loading = false | ||||
|       this.settingsService.dashboardIsEmpty = this.dashboardViews.length === 0 | ||||
|     }) | ||||
|   } | ||||
|  | ||||
| @@ -34,12 +41,34 @@ export class SavedViewService extends AbstractPaperlessService<PaperlessSavedVie | ||||
|     return this.savedViews | ||||
|   } | ||||
|  | ||||
|   get sidebarViews() { | ||||
|     return this.savedViews.filter((v) => v.show_in_sidebar) | ||||
|   get sidebarViews(): PaperlessSavedView[] { | ||||
|     const sidebarViews = this.savedViews.filter((v) => v.show_in_sidebar) | ||||
|  | ||||
|     const sorted: number[] = this.settingsService.get( | ||||
|       SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER | ||||
|     ) | ||||
|  | ||||
|     return sorted?.length > 0 | ||||
|       ? sorted | ||||
|           .map((id) => sidebarViews.find((v) => v.id === id)) | ||||
|           .concat(sidebarViews.filter((v) => !sorted.includes(v.id))) | ||||
|           .filter((v) => v) | ||||
|       : [...sidebarViews] | ||||
|   } | ||||
|  | ||||
|   get dashboardViews() { | ||||
|     return this.savedViews.filter((v) => v.show_on_dashboard) | ||||
|   get dashboardViews(): PaperlessSavedView[] { | ||||
|     const dashboardViews = this.savedViews.filter((v) => v.show_on_dashboard) | ||||
|  | ||||
|     const sorted: number[] = this.settingsService.get( | ||||
|       SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER | ||||
|     ) | ||||
|  | ||||
|     return sorted?.length > 0 | ||||
|       ? sorted | ||||
|           .map((id) => dashboardViews.find((v) => v.id === id)) | ||||
|           .concat(dashboardViews.filter((v) => !sorted.includes(v.id))) | ||||
|           .filter((v) => v) | ||||
|       : [...dashboardViews] | ||||
|   } | ||||
|  | ||||
|   create(o: PaperlessSavedView) { | ||||
|   | ||||
| @@ -292,6 +292,14 @@ describe('SettingsService', () => { | ||||
|       SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER, | ||||
|       [1, 4] | ||||
|     ) | ||||
|     settingsService.updateSidebarViewsSort([ | ||||
|       { id: 1 } as PaperlessSavedView, | ||||
|       { id: 4 } as PaperlessSavedView, | ||||
|     ]) | ||||
|     expect(setSpy).toHaveBeenCalledWith( | ||||
|       SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER, | ||||
|       [1, 4] | ||||
|     ) | ||||
|     httpTestingController | ||||
|       .expectOne(`${environment.apiBaseUrl}ui_settings/`) | ||||
|       .flush(ui_settings) | ||||
|   | ||||
| @@ -24,7 +24,6 @@ import { | ||||
| } from '../data/paperless-uisettings' | ||||
| import { PaperlessUser } from '../data/paperless-user' | ||||
| import { PermissionsService } from './permissions.service' | ||||
| import { SavedViewService } from './rest/saved-view.service' | ||||
| import { ToastService } from './toast.service' | ||||
| import { PaperlessSavedView } from '../data/paperless-saved-view' | ||||
|  | ||||
| @@ -55,8 +54,11 @@ export class SettingsService { | ||||
|     return this._renderer | ||||
|   } | ||||
|  | ||||
|   public dashboardIsEmpty: boolean = false | ||||
|  | ||||
|   public globalDropzoneEnabled: boolean = true | ||||
|   public globalDropzoneActive: boolean = false | ||||
|   public organizingSidebarSavedViews: boolean = false | ||||
|  | ||||
|   constructor( | ||||
|     rendererFactory: RendererFactory2, | ||||
| @@ -66,7 +68,6 @@ export class SettingsService { | ||||
|     @Inject(LOCALE_ID) private localeId: string, | ||||
|     protected http: HttpClient, | ||||
|     private toastService: ToastService, | ||||
|     private savedViewService: SavedViewService, | ||||
|     private permissionsService: PermissionsService | ||||
|   ) { | ||||
|     this._renderer = rendererFactory.createRenderer(null, null) | ||||
| @@ -515,11 +516,7 @@ export class SettingsService { | ||||
|   } | ||||
|  | ||||
|   offerTour(): boolean { | ||||
|     return ( | ||||
|       !this.savedViewService.loading && | ||||
|       this.savedViewService.dashboardViews.length == 0 && | ||||
|       !this.get(SETTINGS_KEYS.TOUR_COMPLETE) | ||||
|     ) | ||||
|     return this.dashboardIsEmpty && !this.get(SETTINGS_KEYS.TOUR_COMPLETE) | ||||
|   } | ||||
|  | ||||
|   completeTour() { | ||||
| @@ -544,4 +541,11 @@ export class SettingsService { | ||||
|     ]) | ||||
|     return this.storeSettings() | ||||
|   } | ||||
|  | ||||
|   updateSidebarViewsSort(sidebarViews: PaperlessSavedView[]): Observable<any> { | ||||
|     this.set(SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER, [ | ||||
|       ...new Set(sidebarViews.map((v) => v.id)), | ||||
|     ]) | ||||
|     return this.storeSettings() | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -642,3 +642,17 @@ code { | ||||
| .me-1px { | ||||
|   margin-right: 1px !important; | ||||
| } | ||||
|  | ||||
| .cdk-drag-placeholder { | ||||
|   opacity: .5; | ||||
| } | ||||
|  | ||||
| /* Animate items as they're being sorted. */ | ||||
| .cdk-drop-list-dragging .cdk-drag { | ||||
|   transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); | ||||
| } | ||||
|  | ||||
| /* Animate an item that has been dropped. */ | ||||
| .cdk-drag-animating { | ||||
|   transition: transform 300ms cubic-bezier(0, 0, 0.2, 1); | ||||
| } | ||||
|   | ||||
| @@ -226,7 +226,7 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt | ||||
|   } | ||||
|  | ||||
|   .alert-primary { | ||||
|     --bs-alert-color: var(--pngx-primary-text-contrast); | ||||
|     --bs-alert-color: var(--bs-primary); | ||||
|     --bs-alert-bg: var(--pngx-primary-darken-27); | ||||
|     --bs-alert-border-color: var(--pngx-bg-darker); | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon