]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Enhancement: improved loading visuals (#8435)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Fri, 6 Dec 2024 04:26:28 +0000 (20:26 -0800)
committerGitHub <noreply@github.com>
Fri, 6 Dec 2024 04:26:28 +0000 (20:26 -0800)
35 files changed:
src-ui/messages.xlf
src-ui/src/app/components/admin/trash/trash.component.html
src-ui/src/app/components/admin/trash/trash.component.scss
src-ui/src/app/components/admin/trash/trash.component.spec.ts
src-ui/src/app/components/admin/trash/trash.component.ts
src-ui/src/app/components/dashboard/dashboard.component.html
src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html
src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss
src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts
src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts
src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html
src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html
src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.scss
src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.spec.ts
src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts
src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html
src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html
src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts
src-ui/src/app/components/manage/custom-fields/custom-fields.component.html
src-ui/src/app/components/manage/custom-fields/custom-fields.component.scss
src-ui/src/app/components/manage/custom-fields/custom-fields.component.spec.ts
src-ui/src/app/components/manage/custom-fields/custom-fields.component.ts
src-ui/src/app/components/manage/mail/mail.component.html
src-ui/src/app/components/manage/mail/mail.component.scss
src-ui/src/app/components/manage/mail/mail.component.spec.ts
src-ui/src/app/components/manage/mail/mail.component.ts
src-ui/src/app/components/manage/management-list/management-list.component.html
src-ui/src/app/components/manage/management-list/management-list.component.scss
src-ui/src/app/components/manage/management-list/management-list.component.spec.ts
src-ui/src/app/components/manage/management-list/management-list.component.ts
src-ui/src/app/components/manage/workflows/workflows.component.html
src-ui/src/app/components/manage/workflows/workflows.component.scss
src-ui/src/app/components/manage/workflows/workflows.component.spec.ts
src-ui/src/app/components/manage/workflows/workflows.component.ts
src-ui/src/theme.scss

index 61722b5ada3540838914b9a402ed00ce55cfe110..999413c35cec9575175218c545d9e2e6ae8909a6 100644 (file)
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">118</context>
+          <context context-type="linenumber">127</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">37</context>
+          <context context-type="linenumber">43</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">40</context>
+          <context context-type="linenumber">46</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">43</context>
+          <context context-type="linenumber">49</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">54</context>
+          <context context-type="linenumber">61</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
           <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>
-        </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html</context>
           <context context-type="linenumber">14</context>
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
           <context context-type="linenumber">110</context>
         </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
+          <context context-type="linenumber">26</context>
+        </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">164</context>
+          <context context-type="linenumber">40</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
+          <context context-type="linenumber">114</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
+          <context context-type="linenumber">178</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
           <context context-type="sourcefile">src/app/components/manage/saved-views/saved-views.component.html</context>
           <context context-type="linenumber">68</context>
         </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
+          <context context-type="linenumber">28</context>
+        </context-group>
       </trans-unit>
       <trans-unit id="1255048712725285892" datatype="html">
         <source>Options to customize appearance, notifications and more. Settings apply to the &lt;strong&gt;current user only&lt;/strong&gt;.</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">59</context>
+          <context context-type="linenumber">66</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">69</context>
+          <context context-type="linenumber">76</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">126</context>
+          <context context-type="linenumber">140</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">138</context>
+          <context context-type="linenumber">152</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">96</context>
+          <context context-type="linenumber">103</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">100</context>
+          <context context-type="linenumber">107</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">59</context>
+          <context context-type="linenumber">68</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">88</context>
+          <context context-type="linenumber">97</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">36</context>
+          <context context-type="linenumber">43</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">55</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">60</context>
+          <context context-type="linenumber">67</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">72</context>
+          <context context-type="linenumber">79</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">127</context>
+          <context context-type="linenumber">141</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">141</context>
+          <context context-type="linenumber">155</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">210</context>
+          <context context-type="linenumber">225</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/saved-views/saved-views.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">55</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">59</context>
+          <context context-type="linenumber">66</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2308646316372333720" datatype="html">
         <source>Confirm delete</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">55</context>
+          <context context-type="linenumber">64</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">82</context>
+          <context context-type="linenumber">91</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">206</context>
+          <context context-type="linenumber">221</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">323</context>
+          <context context-type="linenumber">338</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1373208150912772963" datatype="html">
         <source>This operation will permanently delete this document.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">56</context>
+          <context context-type="linenumber">65</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5641451190833696892" datatype="html">
         <source>This operation cannot be undone.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">57</context>
+          <context context-type="linenumber">66</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">86</context>
+          <context context-type="linenumber">95</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">83</context>
+          <context context-type="linenumber">93</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">158</context>
+          <context context-type="linenumber">179</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">238</context>
+          <context context-type="linenumber">259</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">325</context>
+          <context context-type="linenumber">340</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">115</context>
+          <context context-type="linenumber">124</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1980187861066369604" datatype="html">
         <source>Document deleted</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">66</context>
+          <context context-type="linenumber">75</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7295637485862454066" datatype="html">
         <source>Error deleting document</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">71</context>
+          <context context-type="linenumber">80</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
         <source>This operation will permanently delete the selected documents.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">84</context>
+          <context context-type="linenumber">93</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6804051092296228130" datatype="html">
         <source>This operation will permanently delete all documents in the trash.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">85</context>
+          <context context-type="linenumber">94</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6996183233986182894" datatype="html">
         <source>Document(s) deleted</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">96</context>
+          <context context-type="linenumber">105</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6962724852893361467" datatype="html">
         <source>Error deleting document(s)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">103</context>
+          <context context-type="linenumber">112</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7534569062269274401" datatype="html">
         <source>Document restored</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">116</context>
+          <context context-type="linenumber">125</context>
         </context-group>
       </trans-unit>
       <trans-unit id="9136016619414048201" datatype="html">
         <source>Error restoring document</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">126</context>
+          <context context-type="linenumber">135</context>
         </context-group>
       </trans-unit>
       <trans-unit id="960063472770266304" datatype="html">
         <source>Document(s) restored</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">136</context>
+          <context context-type="linenumber">145</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8405416976953346141" datatype="html">
         <source>Error restoring document(s)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/admin/trash/trash.component.ts</context>
-          <context context-type="linenumber">142</context>
+          <context context-type="linenumber">151</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8119815638230251386" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">35</context>
+          <context context-type="linenumber">42</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">45</context>
+          <context context-type="linenumber">52</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">58</context>
+          <context context-type="linenumber">65</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">66</context>
+          <context context-type="linenumber">73</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">125</context>
+          <context context-type="linenumber">139</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">135</context>
+          <context context-type="linenumber">149</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">47</context>
+          <context context-type="linenumber">54</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">56</context>
+          <context context-type="linenumber">63</context>
         </context-group>
       </trans-unit>
       <trans-unit id="9124347207158517893" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">85</context>
+          <context context-type="linenumber">95</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">160</context>
+          <context context-type="linenumber">181</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">240</context>
+          <context context-type="linenumber">261</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">327</context>
+          <context context-type="linenumber">342</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">117</context>
+          <context context-type="linenumber">126</context>
         </context-group>
       </trans-unit>
       <trans-unit id="857903183180440990" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">72</context>
+          <context context-type="linenumber">106</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7886570921510760899" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">64</context>
+          <context context-type="linenumber">93</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">119</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5421255270838137624" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">88</context>
+          <context context-type="linenumber">132</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3188389494264426470" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">57</context>
+          <context context-type="linenumber">72</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">129</context>
+          <context context-type="linenumber">143</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6329940072345709724" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">74</context>
+          <context context-type="linenumber">80</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">63</context>
+          <context context-type="linenumber">78</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">135</context>
+          <context context-type="linenumber">149</context>
         </context-group>
       </trans-unit>
       <trans-unit id="searchResults.noResults" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">60</context>
+          <context context-type="linenumber">75</context>
         </context-group>
       </trans-unit>
       <trans-unit id="9195188695728229921" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">65</context>
+          <context context-type="linenumber">75</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1841172489943868696" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">73</context>
+          <context context-type="linenumber">83</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4465085913683915434" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">98</context>
+          <context context-type="linenumber">105</context>
         </context-group>
       </trans-unit>
       <trans-unit id="220550782947016929" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">114</context>
+          <context context-type="linenumber">128</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">41</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4348351765075925931" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">128</context>
+          <context context-type="linenumber">142</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">146</context>
+          <context context-type="linenumber">160</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">49</context>
+          <context context-type="linenumber">56</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">64</context>
+          <context context-type="linenumber">71</context>
         </context-group>
       </trans-unit>
       <trans-unit id="595732867213154214" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">99</context>
+          <context context-type="linenumber">106</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
         <source>Filter by correspondent</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">47</context>
+          <context context-type="linenumber">53</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">20</context>
+          <context context-type="linenumber">25</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         <source>Filter by document type</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">57</context>
+          <context context-type="linenumber">63</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">74</context>
+          <context context-type="linenumber">96</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         <source>Filter by storage path</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">62</context>
+          <context context-type="linenumber">68</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">102</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         <source>No documents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
-          <context context-type="linenumber">116</context>
+          <context context-type="linenumber">125</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1069523139277190436" datatype="html">
         <source>Go to inbox</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">5</context>
+          <context context-type="linenumber">28</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3497361602348932709" datatype="html">
         <source>Documents in inbox</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">6</context>
+          <context context-type="linenumber">29</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8809281703097241399" datatype="html">
         <source>Go to documents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">10</context>
+          <context context-type="linenumber">33</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3823413855067727192" datatype="html">
         <source>Total documents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">11</context>
+          <context context-type="linenumber">34</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6503529145162789855" datatype="html">
         <source>Total characters</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">15</context>
+          <context context-type="linenumber">38</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3047655754312785383" datatype="html">
         <source>Current ASN</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
-          <context context-type="linenumber">20</context>
+          <context context-type="linenumber">43</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8693603235657020323" datatype="html">
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">54</context>
+          <context context-type="linenumber">69</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2434944824726929798" datatype="html">
         <source>Filter by tag</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">31</context>
+          <context context-type="linenumber">36</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
         <source>View notes</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">69</context>
+          <context context-type="linenumber">91</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3727324658595204357" datatype="html">
         <source>Created: <x id="INTERPOLATION" equiv-text="{{ document.created_date | customDate }}"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">93,94</context>
+          <context context-type="linenumber">115,116</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">66,67</context>
+          <context context-type="linenumber">76,77</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">81,82</context>
+          <context context-type="linenumber">91,92</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2030261243264601523" datatype="html">
         <source>Added: <x id="INTERPOLATION" equiv-text="{{ document.added | customDate }}"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">94,95</context>
+          <context context-type="linenumber">116,117</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">67,68</context>
+          <context context-type="linenumber">77,78</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">82,83</context>
+          <context context-type="linenumber">92,93</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4235671847487610290" datatype="html">
         <source>Modified: <x id="INTERPOLATION" equiv-text="{{ document.modified | customDate }}"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">95,96</context>
+          <context context-type="linenumber">117,118</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">68,69</context>
+          <context context-type="linenumber">78,79</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">83,84</context>
+          <context context-type="linenumber">93,94</context>
         </context-group>
       </trans-unit>
       <trans-unit id="197162226430950645" datatype="html">
         <source>{VAR_PLURAL, plural, =1 {1 page} other {<x id="INTERPOLATION"/> pages}}</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">112</context>
+          <context context-type="linenumber">134</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">96</context>
+          <context context-type="linenumber">106</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5739581984228459958" datatype="html">
         <source>Shared</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">122</context>
+          <context context-type="linenumber">144</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">115</context>
+          <context context-type="linenumber">125</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/data/document.ts</context>
         <source>Score:</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
-          <context context-type="linenumber">127</context>
+          <context context-type="linenumber">149</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3661756380991326939" datatype="html">
         <source>Toggle tag filter</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">16</context>
+          <context context-type="linenumber">20</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4648526799630820486" datatype="html">
         <source>Toggle correspondent filter</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">38</context>
+          <context context-type="linenumber">43</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5319701482646590642" datatype="html">
         <source>Toggle document type filter</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">49</context>
+          <context context-type="linenumber">59</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8950368321707344185" datatype="html">
         <source>Toggle storage path filter</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
-          <context context-type="linenumber">56</context>
+          <context context-type="linenumber">66</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5145213156408463657" datatype="html">
         <source>correspondent</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.ts</context>
-          <context context-type="linenumber">40</context>
+          <context context-type="linenumber">39</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1612355304340685070" datatype="html">
         <source>correspondents</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.ts</context>
-          <context context-type="linenumber">41</context>
+          <context context-type="linenumber">40</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6360600151505327572" datatype="html">
         <source>Last used</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.ts</context>
-          <context context-type="linenumber">46</context>
+          <context context-type="linenumber">45</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7427874343955308724" datatype="html">
         <source>Do you really want to delete the correspondent &quot;<x id="PH" equiv-text="object.name"/>&quot;?</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.ts</context>
-          <context context-type="linenumber">89</context>
+          <context context-type="linenumber">71</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8384138406252790442" datatype="html">
         <source>Filter Documents (<x id="INTERPOLATION" equiv-text="{{ field.document_count }}"/>)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">38</context>
+          <context context-type="linenumber">45</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
         <source>No fields defined.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
-          <context context-type="linenumber">63</context>
+          <context context-type="linenumber">70</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3032792139967609806" datatype="html">
         <source>Confirm delete field</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">81</context>
+          <context context-type="linenumber">91</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2939457975223185057" datatype="html">
         <source>This operation will permanently delete this field.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">82</context>
+          <context context-type="linenumber">92</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5137089475515834162" datatype="html">
         <source>Deleted field</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">91</context>
+          <context context-type="linenumber">101</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6352403551920829405" datatype="html">
         <source>Error deleting field.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
-          <context context-type="linenumber">97</context>
+          <context context-type="linenumber">107</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8084492669582894778" datatype="html">
         <source>No mail accounts defined.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">80</context>
+          <context context-type="linenumber">87</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5364020217520256833" datatype="html">
         <source>Mail rules</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">88</context>
+          <context context-type="linenumber">95</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1372022816709469401" datatype="html">
         <source>Add Rule</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">90</context>
+          <context context-type="linenumber">97</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2535466903620876415" datatype="html">
         <source>Sort Order</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">97</context>
+          <context context-type="linenumber">104</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5769292297914455214" datatype="html">
         <source>Disabled</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">114</context>
+          <context context-type="linenumber">128</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">41</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6751234988479444294" datatype="html">
         <source>No mail rules defined.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
-          <context context-type="linenumber">155</context>
+          <context context-type="linenumber">169</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3178554336792037159" datatype="html">
         <source>Error retrieving mail accounts</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">79</context>
+          <context context-type="linenumber">92</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5241231471117657636" datatype="html">
         <source>Error retrieving mail rules</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">93</context>
+          <context context-type="linenumber">114</context>
         </context-group>
       </trans-unit>
       <trans-unit id="763945516325093575" datatype="html">
         <source>OAuth2 authentication success</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">101</context>
+          <context context-type="linenumber">122</context>
         </context-group>
       </trans-unit>
       <trans-unit id="9022978370268070156" datatype="html">
         <source>OAuth2 authentication failed, see logs for details</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">112</context>
+          <context context-type="linenumber">133</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6327501535846658797" datatype="html">
         <source>Saved account &quot;<x id="PH" equiv-text="newMailAccount.name"/>&quot;.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">136</context>
+          <context context-type="linenumber">157</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8067594003836508139" datatype="html">
         <source>Error saving account.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">148</context>
+          <context context-type="linenumber">169</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5641934153807844674" datatype="html">
         <source>Confirm delete mail account</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">156</context>
+          <context context-type="linenumber">177</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7176985344323395435" datatype="html">
         <source>This operation will permanently delete this mail account.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">157</context>
+          <context context-type="linenumber">178</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4233826387148482123" datatype="html">
         <source>Deleted mail account</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">166</context>
+          <context context-type="linenumber">187</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6202503362522392111" datatype="html">
         <source>Error deleting mail account.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">176</context>
+          <context context-type="linenumber">197</context>
         </context-group>
       </trans-unit>
       <trans-unit id="123368655395433699" datatype="html">
         <source>Saved rule &quot;<x id="PH" equiv-text="newMailRule.name"/>&quot;.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">195</context>
+          <context context-type="linenumber">216</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8951124554918814321" datatype="html">
         <source>Error saving rule.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">206</context>
+          <context context-type="linenumber">227</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3574401690710711341" datatype="html">
         <source>Rule &quot;<x id="PH" equiv-text="rule.name"/>&quot; enabled.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">222</context>
+          <context context-type="linenumber">243</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7171685227222299542" datatype="html">
         <source>Rule &quot;<x id="PH" equiv-text="rule.name"/>&quot; disabled.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">223</context>
+          <context context-type="linenumber">244</context>
         </context-group>
       </trans-unit>
       <trans-unit id="684458488797860482" datatype="html">
         <source>Error toggling rule.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">227</context>
+          <context context-type="linenumber">248</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3896080636020672118" datatype="html">
         <source>Confirm delete mail rule</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">236</context>
+          <context context-type="linenumber">257</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2250372580580310337" datatype="html">
         <source>This operation will permanently delete this mail rule.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">237</context>
+          <context context-type="linenumber">258</context>
         </context-group>
       </trans-unit>
       <trans-unit id="9077981247971516916" datatype="html">
         <source>Deleted mail rule</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">246</context>
+          <context context-type="linenumber">267</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2033194641751367552" datatype="html">
         <source>Error deleting mail rule.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">255</context>
+          <context context-type="linenumber">276</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3061362835271417984" datatype="html">
         <source>Permissions updated</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">277</context>
+          <context context-type="linenumber">298</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4639647950943944112" datatype="html">
         <source>Error updating permissions</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
-          <context context-type="linenumber">282</context>
+          <context context-type="linenumber">303</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">310</context>
+          <context context-type="linenumber">325</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4010735610815226758" datatype="html">
         <source>Automatic</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">117</context>
+          <context context-type="linenumber">125</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/data/matching-model.ts</context>
         <source>None</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">119</context>
+          <context context-type="linenumber">127</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/data/matching-model.ts</context>
         <source>Successfully created <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">163</context>
+          <context context-type="linenumber">178</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3928835053823658072" datatype="html">
         <source>Error occurred while creating <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">168</context>
+          <context context-type="linenumber">183</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2541368547549828690" datatype="html">
         <source>Successfully updated <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">183</context>
+          <context context-type="linenumber">198</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6442673774206210733" datatype="html">
         <source>Error occurred while saving <x id="PH" equiv-text="this.typeName"/>.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">188</context>
+          <context context-type="linenumber">203</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8371896857609524947" datatype="html">
         <source>Associated documents will not be deleted.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">208</context>
+          <context context-type="linenumber">223</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6639207128255974941" datatype="html">
         <source>Error while deleting element</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">224</context>
+          <context context-type="linenumber">239</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4863024195229581844" datatype="html">
         <source>Permissions updated successfully</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">303</context>
+          <context context-type="linenumber">318</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1464476612812630086" datatype="html">
         <source>This operation will permanently delete all objects.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">324</context>
+          <context context-type="linenumber">339</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5897787932098828336" datatype="html">
         <source>Objects deleted successfully</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">338</context>
+          <context context-type="linenumber">353</context>
         </context-group>
       </trans-unit>
       <trans-unit id="8273353839648035634" datatype="html">
         <source>Error deleting objects</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
-          <context context-type="linenumber">344</context>
+          <context context-type="linenumber">359</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1930477323485553035" datatype="html">
         <source>No workflows defined.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
-          <context context-type="linenumber">73</context>
+          <context context-type="linenumber">80</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4200688335642457098" datatype="html">
         <source>Saved workflow &quot;<x id="PH" equiv-text="newWorkflow.name"/>&quot;.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">78</context>
+          <context context-type="linenumber">87</context>
         </context-group>
       </trans-unit>
       <trans-unit id="7593065565369163325" datatype="html">
         <source>Error saving workflow.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">86</context>
+          <context context-type="linenumber">95</context>
         </context-group>
       </trans-unit>
       <trans-unit id="563460864902055482" datatype="html">
         <source>Confirm delete workflow</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">113</context>
+          <context context-type="linenumber">122</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6874008462443189248" datatype="html">
         <source>This operation will permanently delete this workflow.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">114</context>
+          <context context-type="linenumber">123</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1848226135059921165" datatype="html">
         <source>Deleted workflow</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">123</context>
+          <context context-type="linenumber">132</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3177411222429626224" datatype="html">
         <source>Error deleting workflow.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">128</context>
+          <context context-type="linenumber">137</context>
         </context-group>
       </trans-unit>
       <trans-unit id="5459159218551862653" datatype="html">
         <source>Enabled workflow</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">139</context>
+          <context context-type="linenumber">148</context>
         </context-group>
       </trans-unit>
       <trans-unit id="6035681056091592756" datatype="html">
         <source>Disabled workflow</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">140</context>
+          <context context-type="linenumber">149</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1376040678213338380" datatype="html">
         <source>Error toggling workflow.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.ts</context>
-          <context context-type="linenumber">146</context>
+          <context context-type="linenumber">155</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2649252321173430744" datatype="html">
index 3dbf3db2be5a98e2ddda456067077664e7ba47b0..a148f203391301bb45c63d3df5d7cd65356bef82 100644 (file)
@@ -47,7 +47,7 @@
                 </tr>
             }
             @for (document of documentsInTrash; track document.id) {
-                <tr (click)="toggleSelected(document); $event.stopPropagation();" (mouseleave)="popupPreview.close()">
+                <tr (click)="toggleSelected(document); $event.stopPropagation();" (mouseleave)="popupPreview.close()" class="data-row" [class.reveal]="reveal">
                     <td>
                     <div class="form-check m-0 ms-2 me-n2">
                         <input type="checkbox" class="form-check-input" id="{{document.id}}" [checked]="selectedDocuments.has(document.id)" (click)="toggleSelected(document); $event.stopPropagation();">
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..648aa7f4d77dfa9b3ee96908ef794c570d1d06ae 100644 (file)
@@ -0,0 +1,8 @@
+.data-row {
+    opacity: 0;
+    transition: opacity .2s;
+}
+
+.reveal {
+    opacity: 1;
+}
index 9ac89d9a57a4c2f76f41d1d62eff280d4f6a0dd7..066311726da93980656d9194d3b15f8133ae433e 100644 (file)
@@ -69,6 +69,7 @@ describe('TrashComponent', () => {
   })
 
   it('should call correct service method on reload', () => {
+    jest.useFakeTimers()
     const trashSpy = jest.spyOn(trashService, 'getTrash')
     trashSpy.mockReturnValue(
       of({
@@ -78,6 +79,7 @@ describe('TrashComponent', () => {
       })
     )
     component.reload()
+    jest.advanceTimersByTime(100)
     expect(trashSpy).toHaveBeenCalled()
     expect(component.documentsInTrash).toEqual(documentsInTrash)
   })
index 9364d4cce80a624cc2410a38ce6935eea0dee428..055c5fa97bda7db761c503162f2580d6b9c2593e 100644 (file)
@@ -4,7 +4,7 @@ import { Document } from 'src/app/data/document'
 import { ToastService } from 'src/app/services/toast.service'
 import { TrashService } from 'src/app/services/trash.service'
 import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
-import { Subject, takeUntil } from 'rxjs'
+import { delay, Subject, takeUntil, tap } from 'rxjs'
 import { SettingsService } from 'src/app/services/settings.service'
 import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
 import { Router } from '@angular/router'
@@ -21,6 +21,7 @@ export class TrashComponent implements OnDestroy {
   public page: number = 1
   public totalDocuments: number
   public isLoading: boolean = false
+  public reveal: boolean = false
   unsubscribeNotifier: Subject<void> = new Subject()
 
   constructor(
@@ -40,12 +41,20 @@ export class TrashComponent implements OnDestroy {
 
   reload() {
     this.isLoading = true
-    this.trashService.getTrash(this.page).subscribe((r) => {
-      this.documentsInTrash = r.results
-      this.totalDocuments = r.count
-      this.isLoading = false
-      this.selectedDocuments.clear()
-    })
+    this.trashService
+      .getTrash(this.page)
+      .pipe(
+        tap((r) => {
+          this.documentsInTrash = r.results
+          this.totalDocuments = r.count
+          this.selectedDocuments.clear()
+        }),
+        delay(100)
+      )
+      .subscribe(() => {
+        this.reveal = true
+        this.isLoading = false
+      })
   }
 
   delete(document: Document) {
index 4b217f8c393130798cafe19d97025cc2ed836eaa..27f19475c56e2c4663c11a3cf1d91726706d7635 100644 (file)
       >
       @if (savedViewService.loading) {
         <div class="col">
-          <div class="spinner-border spinner-border-sm me-2" role="status"></div>
-          <ng-container i18n>Loading...</ng-container>
+          <div class="card shadow-sm bg-light">
+            <div class="card-header">
+              <div class="d-flex justify-content-between align-items-center">
+                <div class="d-flex">
+                  <div class="ms-n2 me-1">
+                    <i-bs name="grip-vertical"></i-bs>
+                  </div>
+                  <h6 class="card-title mb-0" i18n></h6>
+                </div>
+              </div>
+            </div>
+            <div class="card-body">&nbsp;</div>
+          </div>
         </div>
       }
 
index 2846e0d04f9569d802145e1226224bddd3672e0a..2cce1a848033ca182c21a0312e407d43fb18c14a 100644 (file)
@@ -9,8 +9,9 @@
     <a class="btn-link text-decoration-none" header-buttons [routerLink]="[]" (click)="showAll()" i18n>Show all</a>
   }
 
-  @if (documents.length && displayMode === DisplayMode.TABLE) {
-    <table content class="table table-hover mb-0 mt-n2 align-middle">
+  <div content class="wrapper" [class.reveal]="reveal">
+  @if (displayMode === DisplayMode.TABLE) {
+    <table class="table table-hover mb-0 mt-n2 align-middle">
       <thead>
         <tr>
           @for (field of displayFields; track field; let i = $index) {
         </tr>
       </thead>
       <tbody>
-        @for (doc of documents; track doc.id) {
+        @for (doc of documents; track doc.id; let i = $index) {
           <tr>
-            @for (field of displayFields; track field; let i = $index) {
-              <td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': i > 1 }">
-                @switch (field) {
-                  @case (DisplayField.ADDED) {
-                    <a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3" title="Open document" i18n-title>{{doc.added | customDate}}</a>
-                  }
-                  @case (DisplayField.CREATED) {
-                    <a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3" title="Open document" i18n-title>{{doc.created_date | customDate}}</a>
-                  }
-                  @case (DisplayField.TITLE) {
-                    <a routerLink="/documents/{{doc.id}}" title="Open document" i18n-title class="btn-link text-dark text-decoration-none py-2 py-md-3">{{doc.title | documentTitle}}</a>
-                  }
-                  @case (DisplayField.CORRESPONDENT) {
-                    @if (doc.correspondent) {
-                      <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickCorrespondent(doc.correspondent, $event)" title="Filter by correspondent" i18n-title>{{(doc.correspondent$ | async)?.name}}</a>
+            @for (field of displayFields; track field; let j = $index) {
+              <td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': j > 1 }">
+                @if (loading && reveal) {
+                  <div class="placeholder-glow text-start">
+                    <span class="placeholder bg-secondary w-50" [ngStyle]="{ opacity: 1 - (i * 1/documents.length) }"></span>
+                  </div>
+                } @else {
+                  @switch (field) {
+                    @case (DisplayField.ADDED) {
+                      <a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3" title="Open document" i18n-title>{{doc.added | customDate}}</a>
                     }
-                  }
-                  @case (DisplayField.TAGS) {
-                    @for (t of doc.tags$ | async; track t) {
-                      <pngx-tag [tag]="t" class="ms-1" (click)="clickTag(t.id, $event)" [clickable]="true" linkTitle="Filter by tag" i18n-title></pngx-tag>
+                    @case (DisplayField.CREATED) {
+                      <a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3" title="Open document" i18n-title>{{doc.created_date | customDate}}</a>
                     }
-                  }
-                  @case (DisplayField.DOCUMENT_TYPE) {
-                    @if (doc.document_type) {
-                      <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickDocType(doc.document_type, $event)" title="Filter by document type" i18n-title>{{(doc.document_type$ | async)?.name}}</a>
+                    @case (DisplayField.TITLE) {
+                      <a routerLink="/documents/{{doc.id}}" title="Open document" i18n-title class="btn-link text-dark text-decoration-none py-2 py-md-3">{{doc.title | documentTitle}}</a>
                     }
-                  }
-                  @case (DisplayField.STORAGE_PATH) {
-                    @if (doc.storage_path) {
-                      <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickStoragePath(doc.storage_path, $event)" title="Filter by storage path" i18n-title>{{(doc.storage_path$ | async)?.name}}</a>
+                    @case (DisplayField.CORRESPONDENT) {
+                      @if (doc.correspondent) {
+                        <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickCorrespondent(doc.correspondent, $event)" title="Filter by correspondent" i18n-title>{{(doc.correspondent$ | async)?.name}}</a>
+                      }
+                    }
+                    @case (DisplayField.TAGS) {
+                      @for (t of doc.tags$ | async; track t) {
+                        <pngx-tag [tag]="t" class="ms-1" (click)="clickTag(t.id, $event)" [clickable]="true" linkTitle="Filter by tag" i18n-title></pngx-tag>
+                      }
+                    }
+                    @case (DisplayField.DOCUMENT_TYPE) {
+                      @if (doc.document_type) {
+                        <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickDocType(doc.document_type, $event)" title="Filter by document type" i18n-title>{{(doc.document_type$ | async)?.name}}</a>
+                      }
+                    }
+                    @case (DisplayField.STORAGE_PATH) {
+                      @if (doc.storage_path) {
+                        <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickStoragePath(doc.storage_path, $event)" title="Filter by storage path" i18n-title>{{(doc.storage_path$ | async)?.name}}</a>
+                      }
                     }
                   }
-                }
-                @if (field.startsWith(DisplayField.CUSTOM_FIELD)) {
-                  <pngx-custom-field-display [document]="doc" [fieldDisplayKey]="field"></pngx-custom-field-display>
-                }
-                @if (i === displayFields.length - 1) {
-                  <div class="btn-group position-absolute top-50 end-0 translate-middle-y" (mouseleave)="popupPreview.close()">
-                    <pngx-preview-popup [document]="doc" linkClasses="btn px-4 btn-dark border-dark-subtle" #popupPreview>
-                      <i-bs width="0.8em" height="0.8em" name="eye"></i-bs>
-                    </pngx-preview-popup>
-                    <a [href]="getDownloadUrl(doc)" class="btn px-4 btn-dark border-dark-subtle" title="Download" i18n-title (click)="$event.stopPropagation()">
-                      <i-bs width="0.8em" height="0.8em" name="download"></i-bs>
-                    </a>
-                  </div>
+                  @if (field.startsWith(DisplayField.CUSTOM_FIELD)) {
+                    <pngx-custom-field-display [document]="doc" [fieldDisplayKey]="field"></pngx-custom-field-display>
+                  }
+                  @if (j === displayFields.length - 1) {
+                    <div class="btn-group position-absolute top-50 end-0 translate-middle-y" (mouseleave)="popupPreview.close()">
+                      <pngx-preview-popup [document]="doc" linkClasses="btn px-4 btn-dark border-dark-subtle" #popupPreview>
+                        <i-bs width="0.8em" height="0.8em" name="eye"></i-bs>
+                      </pngx-preview-popup>
+                      <a [href]="getDownloadUrl(doc)" class="btn px-4 btn-dark border-dark-subtle" title="Download" i18n-title (click)="$event.stopPropagation()">
+                        <i-bs width="0.8em" height="0.8em" name="download"></i-bs>
+                      </a>
+                    </div>
+                  }
                 }
               </td>
             }
         }
       </tbody>
     </table>
-  } @else if (documents.length && displayMode === DisplayMode.SMALL_CARDS) {
-    <div content class="row row-cols-paperless-cards my-n2">
-      @for (d of documents; track d.id) {
+  } @else if (displayMode === DisplayMode.SMALL_CARDS) {
+    <div class="row row-cols-paperless-cards my-n2">
+      @for (d of documents; track d.id; let i = $index) {
         <pngx-document-card-small
           class="p-0"
+          [ngStyle]="{ opacity: !loading && reveal ? 1 : 1 - (i * 1/documents.length) }"
           (dblClickDocument)="openDocumentDetail(d)"
-          [document]="d"
+          [document]="!loading && reveal ? d : null"
           [displayFields]="displayFields"
           (clickTag)="clickTag($event)"
           (clickCorrespondent)="clickCorrespondent($event)"
         </pngx-document-card-small>
       }
     </div>
-  } @else if (documents.length && displayMode === DisplayMode.LARGE_CARDS) {
-    <div content class="row my-n2">
-      @for (d of documents; track d.id) {
+  } @else if (displayMode === DisplayMode.LARGE_CARDS) {
+    <div class="row my-n2">
+      @for (d of documents; track d.id; let i = $index) {
         <pngx-document-card-large
           (dblClickDocument)="openDocumentDetail(d)"
-          [document]="d"
+          [document]="!loading && reveal ? d : null"
+          [ngStyle]="{ opacity: !loading && reveal ? 1 : 1 - (i * 1/documents.length) }"
           [displayFields]="displayFields"
           (clickTag)="clickTag($event)"
           (clickCorrespondent)="clickCorrespondent($event)"
       }
     </div>
   } @else {
-    <p content i18n class="text-center text-muted mb-0 fst-italic">No documents</p>
+    <p i18n class="text-center text-muted mb-0 fst-italic">No documents</p>
   }
 
-
+  </div>
 </pngx-widget-frame>
index 8c445f18e23c01781386eda62c314bbde119f5dc..54156b448f01a6b8224d2e317e13f140a0828887 100644 (file)
@@ -1,3 +1,17 @@
+.wrapper {
+  transition: all .3s ease-out;
+  overflow: hidden;
+  max-height: 0;
+  opacity: .1;
+  width: 100%;
+}
+
+.reveal {
+  max-height: 1000px;
+  opacity: 1;
+  overflow: visible;
+}
+
 table {
   overflow-wrap: anywhere;
   table-layout: fixed;
index b4958b9b9c4a2c7a3810d50f915d35f0d5fe2fd4..aa82cf765ca658b7f19b77ed8857660f947c4563 100644 (file)
@@ -187,7 +187,7 @@ describe('SavedViewWidgetComponent', () => {
     fixture.detectChanges()
   })
 
-  it('should show a list of documents', () => {
+  it('should show a list of documents', fakeAsync(() => {
     jest.spyOn(documentService, 'listFiltered').mockReturnValue(
       of({
         all: [2, 3],
@@ -196,6 +196,7 @@ describe('SavedViewWidgetComponent', () => {
       })
     )
     component.ngOnInit()
+    tick(500)
     fixture.detectChanges()
     expect(fixture.debugElement.nativeElement.textContent).toContain('doc2')
     expect(fixture.debugElement.nativeElement.textContent).toContain('doc3')
@@ -206,7 +207,7 @@ describe('SavedViewWidgetComponent', () => {
     expect(
       fixture.debugElement.queryAll(By.css('td a.btn'))[1].attributes['href']
     ).toEqual(component.getDownloadUrl(documentResults[0]))
-  })
+  }))
 
   it('should call api endpoint and load results', () => {
     const listAllSpy = jest.spyOn(documentService, 'listFiltered')
index f788726d044d6415d417ccc3864b11a6eeb37bc1..74c3af5200265df44f0825d4d0a9088b8e86be5d 100644 (file)
@@ -7,7 +7,7 @@ import {
   ViewChildren,
 } from '@angular/core'
 import { Router } from '@angular/router'
-import { Subject, takeUntil } from 'rxjs'
+import { delay, Subject, takeUntil, tap } from 'rxjs'
 import {
   DEFAULT_DASHBOARD_DISPLAY_FIELDS,
   DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
@@ -52,7 +52,8 @@ export class SavedViewWidgetComponent
   public DisplayField = DisplayField
   public CustomFieldDataType = CustomFieldDataType
 
-  loading: boolean = true
+  public loading: boolean = true
+  public reveal: boolean = false
 
   private customFields: CustomField[] = []
 
@@ -133,16 +134,22 @@ export class SavedViewWidgetComponent
     this.documentService
       .listFiltered(
         1,
-        this.savedView.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
+        this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
         this.savedView.sort_field,
         this.savedView.sort_reverse,
         this.savedView.filter_rules,
         { truncate_content: true }
       )
-      .pipe(takeUntil(this.unsubscribeNotifier))
+      .pipe(
+        takeUntil(this.unsubscribeNotifier),
+        tap((result) => {
+          this.reveal = true
+          this.documents = result.results
+        }),
+        delay(500)
+      )
       .subscribe((result) => {
         this.loading = false
-        this.documents = result.results
       })
   }
 
index d89fc972c1c57cf0619528e73a81a9b840e39ace..718edf4eaba9e0a49c7dc0f1fd286611e0760f3b 100644 (file)
@@ -1,25 +1,49 @@
 <pngx-widget-frame title="Statistics" [loading]="loading" i18n-title>
   <ng-container content>
-    <div class="list-group border-light">
-      @if (statistics?.documents_inbox !== null) {
-        <a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void(0)" (click)="goToInbox()">
-          <ng-container i18n>Documents in inbox</ng-container>:
-          <span class="badge rounded-pill" [class.bg-primary]="statistics?.documents_inbox > 0" [class.bg-muted]="statistics?.documents_inbox === 0">{{statistics?.documents_inbox}}</span>
+    <div class="list-group border-light placeholder-glow">
+      @if (loading) {
+        <div class="list-group-item d-flex">
+          <div class="placeholder w-50"></div>
+          <span class="placeholder badge rounded-pill ms-auto" style="width: 25px;">&nbsp;</span>
+        </div>
+        <div class="list-group-item d-flex">
+          <div class="placeholder w-25"></div>
+          <span class="placeholder badge rounded-pill ms-auto" style="width: 25px;">&nbsp;</span>
+        </div>
+        <div class="list-group-item d-flex">
+          <div class="placeholder w-25"></div>
+          <span class="placeholder badge rounded-pill ms-auto" style="width: 25px;">&nbsp;</span>
+        </div>
+        <div class="list-group-item d-flex">
+          <div class="placeholder w-25"></div>
+          <span class="placeholder badge rounded-pill ms-auto" style="width: 25px;">&nbsp;</span>
+        </div>
+        <div class="list-group-item filetypes">
+          <div class="placeholder w-100 d-block mb-2"></div>
+          <div class="placeholder w-100 d-block mb-2"></div>
+          <div class="placeholder w-100 d-block"></div>
+        </div>
+      } @else {
+        @if (statistics?.documents_inbox !== null) {
+          <a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void(0)" (click)="goToInbox()">
+            <ng-container i18n>Documents in inbox</ng-container>:
+            <span class="badge rounded-pill" [class.bg-primary]="statistics?.documents_inbox > 0" [class.bg-muted]="statistics?.documents_inbox === 0">{{statistics?.documents_inbox}}</span>
+          </a>
+        }
+        <a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to documents" i18n-title routerLink="/documents/">
+          <ng-container i18n>Total documents</ng-container>:
+          <span class="badge bg-primary rounded-pill">{{statistics?.documents_total}}</span>
         </a>
-      }
-      <a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to documents" i18n-title routerLink="/documents/">
-        <ng-container i18n>Total documents</ng-container>:
-        <span class="badge bg-primary rounded-pill">{{statistics?.documents_total}}</span>
-      </a>
-      <div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
-        <ng-container i18n>Total characters</ng-container>:
-        <span class="badge bg-secondary text-light rounded-pill">{{statistics?.character_count | number}}</span>
-      </div>
-      @if (statistics?.current_asn) {
         <div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
-          <ng-container i18n>Current ASN</ng-container>:
-          <span class="badge bg-secondary text-light rounded-pill">{{statistics?.current_asn}}</span>
+          <ng-container i18n>Total characters</ng-container>:
+          <span class="badge bg-secondary text-light rounded-pill">{{statistics?.character_count | number}}</span>
         </div>
+        @if (statistics?.current_asn) {
+          <div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
+            <ng-container i18n>Current ASN</ng-container>:
+            <span class="badge bg-secondary text-light rounded-pill">{{statistics?.current_asn}}</span>
+          </div>
+        }
       }
       @if (statistics?.document_file_type_counts?.length > 1) {
         <div class="list-group-item filetypes">
 
     <div class="list-group border-light mt-3">
       <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }">
+        @if (loading) {
+          <div class="placeholder-glow list-group-item">
+            <span class="placeholder w-100"></span>
+          </div>
+        }
         @if (statistics?.tag_count > 0) {
           <a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/tags/">
             <ng-container i18n>Tags</ng-container>:
         }
       </ng-container>
       <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
+        @if (loading) {
+          <div class="placeholder-glow list-group-item">
+            <span class="placeholder w-100"></span>
+          </div>
+        }
         @if (statistics?.correspondent_count > 0) {
           <a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/correspondents/">
             <ng-container i18n>Correspondents</ng-container>:
         }
       </ng-container>
       <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
+        @if (loading) {
+          <div class="placeholder-glow list-group-item">
+            <span class="placeholder w-100"></span>
+          </div>
+        }
         @if (statistics?.document_type_count > 0) {
           <a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documenttypes/">
             <ng-container i18n>Document Types</ng-container>:
         }
       </ng-container>
       <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
+        @if (loading) {
+          <div class="placeholder-glow list-group-item">
+            <span class="placeholder w-100"></span>
+          </div>
+        }
         @if (statistics?.storage_path_count > 0) {
           <a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/storagepaths/">
             <ng-container i18n>Storage Paths</ng-container>:
index b64d5e5674400d16332dd4b70019e04d6a4e740f..5f0091dc5df6057d7dc510401865210932c277c5 100644 (file)
@@ -1,4 +1,4 @@
-<div class="card shadow-sm bg-light" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
+<div class="card shadow-sm bg-light fade" [class.reveal]="reveal" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
   <div class="card-header">
     <div class="d-flex justify-content-between align-items-center">
       <div class="d-flex">
index 4efdb0876bf868a0ca351e30240b337771b0970d..521a97c6b7270ade5e1f16c267a5432e42c6626c 100644 (file)
@@ -1,3 +1,12 @@
 i-bs {
     cursor: move;
 }
+
+.card {
+    opacity: 0;
+    transition: opacity .2s;
+}
+
+.reveal {
+    opacity: 1;
+}
index c4e27ecc726b0107881a38af5494881ede0f1b49..cfd7edb3da933fd7aa0fd2a78003b02d6df87342 100644 (file)
@@ -35,6 +35,7 @@ describe('WidgetFrameComponent', () => {
 
     fixture = TestBed.createComponent(WidgetFrameComponent)
     component = fixture.componentInstance
+    jest.useFakeTimers()
 
     fixture.detectChanges()
   })
@@ -51,4 +52,10 @@ describe('WidgetFrameComponent', () => {
     fixture.detectChanges()
     expect(fixture.debugElement.query(By.css('.spinner-border'))).not.toBeNull()
   })
+
+  it('should reveal', () => {
+    expect(component.reveal).toBeFalsy()
+    jest.advanceTimersByTime(100)
+    expect(component.reveal).toBeTruthy()
+  })
 })
index a710a6a9d315c21f8f8e6da111244bfb74ca9782..2a141be7c9ed7e271d548dd30c4e42079e125b43 100644 (file)
@@ -1,11 +1,11 @@
-import { Component, Input } from '@angular/core'
+import { AfterViewInit, Component, Input } from '@angular/core'
 
 @Component({
   selector: 'pngx-widget-frame',
   templateUrl: './widget-frame.component.html',
   styleUrls: ['./widget-frame.component.scss'],
 })
-export class WidgetFrameComponent {
+export class WidgetFrameComponent implements AfterViewInit {
   constructor() {}
 
   @Input()
@@ -16,4 +16,12 @@ export class WidgetFrameComponent {
 
   @Input()
   draggable: any
+
+  public reveal: boolean = false
+
+  ngAfterViewInit(): void {
+    setTimeout(() => {
+      this.reveal = true
+    }, 100)
+  }
 }
index 34557be311bbcd89fe6c280dd956f206ab9ea170..810fea0294e06fad7751346da76c991089e3a95d 100644 (file)
@@ -1,70 +1,92 @@
-<div class="card mb-3 shadow-sm bg-light" [class.card-selected]="selected" [class.document-card]="selectable" (mouseleave)="mouseLeaveCard()">
+<div class="card document-card-large mb-3 shadow-sm bg-light placeholder-glow" [class.card-selected]="selected" [class.document-card]="selectable" (mouseleave)="mouseLeaveCard()">
   <div class="row g-0">
     <div class="col-md-2 doc-img-container rounded-start" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit()">
-      <img [src]="getThumbUrl()" class="card-img doc-img border-end rounded-start" [class.inverted]="getIsThumbInverted()">
+      @if (document) {
+        <img [src]="getThumbUrl()" class="card-img doc-img border-end rounded-start" [class.inverted]="getIsThumbInverted()">
 
-      <div class="border-end border-bottom bg-light document-card-check">
-        <div class="form-check">
-          <input type="checkbox" class="form-check-input" id="smallCardCheck{{document.id}}" [checked]="selected" (click)="this.toggleSelected.emit($event)">
-          <label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
+        <div class="border-end border-bottom bg-light document-card-check">
+          <div class="form-check">
+            <input type="checkbox" class="form-check-input" id="smallCardCheck{{document.id}}" [checked]="selected" (click)="this.toggleSelected.emit($event)">
+            <label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
+          </div>
         </div>
-      </div>
+      } @else {
+        <div class="placeholder bg-secondary w-100 card-img doc-img border-end rounded-start"></div>
+      }
 
     </div>
     <div class="col">
       <div class="card-body">
         <div class="d-flex justify-content-between align-items-center">
-          <h5 class="card-title">
-            @if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
-              @if (clickCorrespondent.observers.length ) {
-                <a title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name}}</a>
-              } @else {
-                {{(document.correspondent$ | async)?.name}}
+          <h5 class="card-title w-100">
+            @if (document) {
+              @if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
+                @if (clickCorrespondent.observers.length ) {
+                  <a title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name}}</a>
+                } @else {
+                  {{(document.correspondent$ | async)?.name}}
+                }
+                @if (displayFields.includes(DisplayField.TITLE)) {:}
               }
-              @if (displayFields.includes(DisplayField.TITLE)) {:}
-            }
-            @if (displayFields.includes(DisplayField.TITLE)) {
-              {{document.title | documentTitle}}
-            }
-            @if (displayFields.includes(DisplayField.TAGS)) {
-              @for (t of document.tags$ | async; track t) {
-                <pngx-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle class="ms-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></pngx-tag>
+              @if (displayFields.includes(DisplayField.TITLE)) {
+                {{document.title | documentTitle}}
               }
+              @if (displayFields.includes(DisplayField.TAGS)) {
+                @for (t of document.tags$ | async; track t) {
+                  <pngx-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle class="ms-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></pngx-tag>
+                }
+              }
+            } @else {
+              <div class="placeholder bg-secondary w-75 mb-3">&nbsp;</div>
             }
           </h5>
         </div>
         <p class="card-text">
-          @if (document.__search_hit__ && document.__search_hit__.highlights) {
-            <span [innerHtml]="document.__search_hit__.highlights"></span>
-          }
-          @for (highlight of searchNoteHighlights; track highlight) {
-            <span class="d-block">
-              <i-bs name="chat-left-text"></i-bs>
-              <span [innerHtml]="highlight"></span>
-            </span>
-          }
-          @if (!document.__search_hit__?.score) {
-            <span class="result-content">{{contentTrimmed}}</span>
+          @if (document) {
+            @if (document.__search_hit__ && document.__search_hit__.highlights) {
+              <span [innerHtml]="document.__search_hit__.highlights"></span>
+            }
+            @for (highlight of searchNoteHighlights; track highlight) {
+              <span class="d-block">
+                <i-bs name="chat-left-text"></i-bs>
+                <span [innerHtml]="highlight"></span>
+              </span>
+            }
+            @if (!document.__search_hit__?.score) {
+              <span class="result-content">{{contentTrimmed}}</span>
+            }
+          } @else {
+            <div class="placeholder bg-secondary w-100 d-block mb-2"></div>
+            <div class="placeholder bg-secondary w-75 d-block mb-2"></div>
+            <div class="placeholder bg-secondary w-25 d-block"></div>
           }
         </p>
 
         <div class="d-flex flex-column flex-md-row align-items-md-center">
           <div class="btn-group">
-            <a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()">
-              <i-bs name="diagram-3"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>More like this</span>
-            </a>
-            <a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }">
-              <i-bs name="file-earmark-richtext"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>Open</span>
-            </a>
-            <pngx-preview-popup [document]="document" #popupPreview>
-              <i-bs name="eye"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>View</span>
-            </pngx-preview-popup>
-            <a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
-              <i-bs name="download"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>Download</span>
+            @if (document) {
+              <a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()">
+                <i-bs name="diagram-3"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>More like this</span>
+              </a>
+              <a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }">
+                <i-bs name="file-earmark-richtext"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>Open</span>
               </a>
-            </div>
+              <pngx-preview-popup [document]="document" #popupPreview>
+                <i-bs name="eye"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>View</span>
+              </pngx-preview-popup>
+              <a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
+                <i-bs name="download"></i-bs>&nbsp;<span class="d-none d-md-inline" i18n>Download</span>
+              </a>
+            } @else {
+              <div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;">&nbsp;</div>
+              <div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;">&nbsp;</div>
+              <div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;">&nbsp;</div>
+              <div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;">&nbsp;</div>
+            }
+          </div>
 
-            <div class="list-group list-group-horizontal border-0 card-info ms-md-auto mt-2 mt-md-0">
+          <div class="list-group list-group-horizontal border-0 card-info ms-md-auto mt-2 mt-md-0">
+            @if (document) {
               @if (displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
                 <button routerLink="/documents/{{document.id}}/notes" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="View notes" i18n-title>
                   <i-bs width=".9em" height=".9em" class="me-2 text-muted" name="chat-left-text"></i-bs><small>{{document.notes.length}} Notes</small>
                   </div>
                 }
               }
-            </div>
+            } @else {
+              <div class="placeholder list-group-item bg-secondary w-25">&nbsp;</div>
+              <div class="placeholder list-group-item bg-secondary w-25">&nbsp;</div>
+              <div class="placeholder list-group-item bg-secondary w-25">&nbsp;</div>
+              <div class="placeholder list-group-item bg-secondary w-25">&nbsp;</div>
+              <div class="placeholder list-group-item bg-secondary w-25">&nbsp;</div>
+            }
           </div>
         </div>
       </div>
     </div>
   </div>
+</div>
index 60713ef0298e02c090a56e65a46518b80d411715..7b1b9bc936640577839b4fc94f60ee3a4b6f0d40 100644 (file)
@@ -1,16 +1,20 @@
 <div class="col p-2 h-100">
-  <div class="card h-100 shadow-sm document-card" [class.card-selected]="selected" (mouseleave)="mouseLeaveCard()">
+  <div class="card h-100 shadow-sm document-card" [class.placeholder-glow]="!document" [class.card-selected]="selected" (mouseleave)="mouseLeaveCard()">
     <div class="border-bottom doc-img-container rounded-top" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit(this)">
-      <img class="card-img doc-img" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
+      @if (document) {
+        <img class="card-img doc-img" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
 
-      <div class="border-end border-bottom bg-light py-1 px-2 document-card-check">
-        <div class="form-check">
-          <input type="checkbox" class="form-check-input" id="smallCardCheck{{document.id}}" [checked]="selected" (click)="this.toggleSelected.emit($event)">
-          <label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
+        <div class="border-end border-bottom bg-light py-1 px-2 document-card-check">
+          <div class="form-check">
+            <input type="checkbox" class="form-check-input" id="smallCardCheck{{document.id}}" [checked]="selected" (click)="this.toggleSelected.emit($event)">
+            <label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
+          </div>
         </div>
-      </div>
+      } @else {
+        <div class="placeholder bg-secondary w-100 card-img doc-img"></div>
+      }
 
-      @if (displayFields?.includes(DisplayField.TAGS)) {
+      @if (document && displayFields?.includes(DisplayField.TAGS)) {
         <div class="tags d-flex flex-column text-end position-absolute me-1 fs-6">
           @for (t of getTagsLimited$() | async; track t) {
             <pngx-tag [tag]="t" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="true" linkTitle="Toggle tag filter" i18n-linkTitle></pngx-tag>
@@ -24,7 +28,7 @@
       }
     </div>
 
-    @if (displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
+    @if (document && displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
       <a routerLink="/documents/{{document.id}}/notes" class="document-card-notes py-2 px-1">
         <span class="badge rounded-pill bg-light border text-primary">
           <i-bs width="1.2em" height="1.2em" class="ms-1 me-1" name="chat-left-text"></i-bs>
 
     <div class="card-body bg-light p-2">
       <p class="card-text">
-        @if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
-          <a title="Toggle correspondent filter" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name ?? privateName}}</a>
-          @if (displayFields.includes(DisplayField.TITLE)) {:}
-        }
-        @if (displayFields.includes(DisplayField.TITLE)) {
-          {{document.title | documentTitle}}
+        @if (document) {
+          @if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
+            <a title="Toggle correspondent filter" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name ?? privateName}}</a>
+            @if (displayFields.includes(DisplayField.TITLE)) {:}
+          }
+          @if (displayFields.includes(DisplayField.TITLE)) {
+            {{document.title | documentTitle}}
+          }
+        } @else {
+          <div class="placeholder bg-secondary w-100"></div>
+          <div class="placeholder bg-secondary w-50"></div>
         }
       </p>
     </div>
     <div class="card-footer pt-0 pb-2 px-2">
       <div class="list-group list-group-flush border-0 pt-1 pb-2 card-info">
-        @if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) {
-          <button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle document type filter" i18n-title
-            (click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
-            <i-bs width="1em" height="1em" class="me-2 text-muted" name="file-earmark"></i-bs>
-            <small>{{(document.document_type$ | async)?.name ?? privateName}}</small>
-          </button>
-        }
-        @if (displayFields.includes(DisplayField.STORAGE_PATH) && document.storage_path) {
-          <button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle storage path filter" i18n-title
-            (click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
-            <i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
-            <small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
-          </button>
-        }
-        @if (displayFields.includes(DisplayField.CREATED)) {
-          <div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
-            <ng-template #dateTooltip>
-              <div class="d-flex flex-column text-light">
-                <span i18n>Created: {{ document.created_date | customDate }}</span>
-                <span i18n>Added: {{ document.added | customDate }}</span>
-                <span i18n>Modified: {{ document.modified | customDate }}</span>
+        @if (document) {
+          @if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) {
+            <button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle document type filter" i18n-title
+              (click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
+              <i-bs width="1em" height="1em" class="me-2 text-muted" name="file-earmark"></i-bs>
+              <small>{{(document.document_type$ | async)?.name ?? privateName}}</small>
+            </button>
+          }
+          @if (displayFields.includes(DisplayField.STORAGE_PATH) && document.storage_path) {
+            <button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle storage path filter" i18n-title
+              (click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
+              <i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
+              <small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
+            </button>
+          }
+          @if (displayFields.includes(DisplayField.CREATED)) {
+            <div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
+              <ng-template #dateTooltip>
+                <div class="d-flex flex-column text-light">
+                  <span i18n>Created: {{ document.created_date | customDate }}</span>
+                  <span i18n>Added: {{ document.added | customDate }}</span>
+                  <span i18n>Modified: {{ document.modified | customDate }}</span>
+                </div>
+              </ng-template>
+              <div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
+                <i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
+                <small>{{document.created_date | customDate:'mediumDate'}}</small>
               </div>
-            </ng-template>
-            <div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
-              <i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
-              <small>{{document.created_date | customDate:'mediumDate'}}</small>
             </div>
-          </div>
-        }
-        @if (displayFields.includes(DisplayField.ADDED)) {
-          <div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
-            <ng-template #dateTooltip>
-              <div class="d-flex flex-column text-light">
-                <span i18n>Created: {{ document.created | customDate }}</span>
-                <span i18n>Added: {{ document.added | customDate }}</span>
-                <span i18n>Modified: {{ document.modified | customDate }}</span>
+          }
+          @if (displayFields.includes(DisplayField.ADDED)) {
+            <div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
+              <ng-template #dateTooltip>
+                <div class="d-flex flex-column text-light">
+                  <span i18n>Created: {{ document.created | customDate }}</span>
+                  <span i18n>Added: {{ document.added | customDate }}</span>
+                  <span i18n>Modified: {{ document.modified | customDate }}</span>
+                </div>
+              </ng-template>
+              <div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
+                <i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
+                <small>{{document.added | customDate:'mediumDate'}}</small>
               </div>
-            </ng-template>
-            <div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
-              <i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
-              <small>{{document.added | customDate:'mediumDate'}}</small>
             </div>
-          </div>
-        }
-        @if (displayFields.includes(DisplayField.PAGE_COUNT) && document.page_count) {
-          <div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
-            <div class="ps-0 p-1" placement="top">
-              <i-bs width="1em" height="1em" class="me-2 text-muted" name="files"></i-bs>
-              <small i18n>{document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}}</small>
+          }
+          @if (displayFields.includes(DisplayField.PAGE_COUNT) && document.page_count) {
+            <div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
+              <div class="ps-0 p-1" placement="top">
+                <i-bs width="1em" height="1em" class="me-2 text-muted" name="files"></i-bs>
+                <small i18n>{document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}}</small>
+              </div>
             </div>
-          </div>
-        }
-        @if (displayFields.includes(DisplayField.ASN) && document.archive_serial_number | isNumber) {
-          <div class="ps-0 p-1">
-            <i-bs width="1em" height="1em" class="me-2 text-muted" name="upc-scan"></i-bs>
-            <small>#{{document.archive_serial_number}}</small>
-          </div>
-        }
-        @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
-          <div class="ps-0 p-1">
-            <i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs>
-            <small>{{document.owner | username}}</small>
-          </div>
-        }
-        @if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) {
-          <div class="ps-0 p-1">
-            <i-bs width="1em" height="1em" class="me-2 text-muted" name="people-fill"></i-bs>
-            <small i18n>Shared</small>
-          </div>
-        }
-        @for (field of document.custom_fields; track field.field) {
-          @if (displayFields.includes(DisplayField.CUSTOM_FIELD + field.field)) {
-            <div class="ps-0 p-1 d-flex align-items-center overflow-hidden">
-              <i-bs width="1em" height="1em" class="me-2 text-muted" name="ui-radios"></i-bs>
-              <small><pngx-custom-field-display [document]="document" [fieldId]="field.field" showNameIfEmpty="true"></pngx-custom-field-display></small>
+          }
+          @if (displayFields.includes(DisplayField.ASN) && document.archive_serial_number | isNumber) {
+            <div class="ps-0 p-1">
+              <i-bs width="1em" height="1em" class="me-2 text-muted" name="upc-scan"></i-bs>
+              <small>#{{document.archive_serial_number}}</small>
+            </div>
+          }
+          @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
+            <div class="ps-0 p-1">
+              <i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs>
+              <small>{{document.owner | username}}</small>
+            </div>
+          }
+          @if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) {
+            <div class="ps-0 p-1">
+              <i-bs width="1em" height="1em" class="me-2 text-muted" name="people-fill"></i-bs>
+              <small i18n>Shared</small>
             </div>
           }
+          @for (field of document.custom_fields; track field.field) {
+            @if (displayFields.includes(DisplayField.CUSTOM_FIELD + field.field)) {
+              <div class="ps-0 p-1 d-flex align-items-center overflow-hidden">
+                <i-bs width="1em" height="1em" class="me-2 text-muted" name="ui-radios"></i-bs>
+                <small><pngx-custom-field-display [document]="document" [fieldId]="field.field" showNameIfEmpty="true"></pngx-custom-field-display></small>
+              </div>
+            }
+          }
+        } @else {
+          <div class="placeholder bg-secondary w-100"></div>
         }
       </div>
       <div class="d-flex justify-content-between align-items-center">
         <div class="btn-group w-100">
-          <a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n-title>
-            <i-bs name="file-earmark-richtext"></i-bs>
-          </a>
-          <pngx-preview-popup [document]="document" #popupPreview>
-            <i-bs name="eye"></i-bs>
-          </pngx-preview-popup>
-          <a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" i18n-title (click)="$event.stopPropagation()">
-            <i-bs name="download"></i-bs>
-          </a>
+          @if (document) {
+            <a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n-title>
+              <i-bs name="file-earmark-richtext"></i-bs>
+            </a>
+            <pngx-preview-popup [document]="document" #popupPreview>
+              <i-bs name="eye"></i-bs>
+            </pngx-preview-popup>
+            <a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" i18n-title (click)="$event.stopPropagation()">
+              <i-bs name="download"></i-bs>
+            </a>
+          } @else {
+            <button class="btn btn-sm btn-outline-secondary placeholder bg-secondary"></button>
+            <button class="btn btn-sm btn-outline-secondary placeholder bg-secondary"></button>
+            <button class="btn btn-sm btn-outline-secondary placeholder bg-secondary"></button>
+          }
         </div>
       </div>
     </div>
index 224b9c8a3855280c96f9c2f7c7a70c562684d0da..94e12cd4835ea2f163c8a199a44cae392d57741e 100644 (file)
@@ -12,7 +12,6 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
 import { ToastService } from 'src/app/services/toast.service'
 import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
 import { ManagementListComponent } from '../management-list/management-list.component'
-import { takeUntil } from 'rxjs'
 
 @Component({
   selector: 'pngx-correspondent-list',
@@ -65,24 +64,7 @@ export class CorrespondentListComponent extends ManagementListComponent<Correspo
   }
 
   public reloadData(): void {
-    this.isLoading = true
-    this.clearSelection()
-    this.service
-      .listFiltered(
-        this.page,
-        null,
-        this.sortField,
-        this.sortReverse,
-        this._nameFilter,
-        true,
-        { last_correspondence: true }
-      )
-      .pipe(takeUntil(this.unsubscribeNotifier))
-      .subscribe((c) => {
-        this.data = c.results
-        this.collectionSize = c.count
-        this.isLoading = false
-      })
+    super.reloadData({ last_correspondence: true })
   }
 
   getDeleteMessage(object: Correspondent) {
index 8439cd1a749be7976c80ad7e14c4aec55896b23a..291520b831de4d3c7a4e34bb2d77b355f32f4c69 100644 (file)
 <ul class="list-group">
 
   <li class="list-group-item">
-    <div class="row">
+    <div class="row reveal">
       <div class="col" i18n>Name</div>
       <div class="col" i18n>Data Type</div>
       <div class="col" i18n>Actions</div>
     </div>
   </li>
 
+  @if (loading) {
+    <li class="list-group-item">
+      <div class="spinner-border spinner-border-sm me-2" role="status"></div>
+      <ng-container i18n>Loading...</ng-container>
+    </li>
+  }
+
   @for (field of fields; track field) {
     <li class="list-group-item">
-      <div class="row">
+      <div class="row" [class.reveal]="reveal">
         <div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editField(field)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.CustomField)">{{field.name}}</button></div>
         <div class="col d-flex align-items-center">{{getDataType(field)}}</div>
         <div class="col">
@@ -59,7 +66,7 @@
       </div>
     </li>
   }
-  @if (fields.length === 0) {
+  @if (!loading && fields.length === 0) {
     <li class="list-group-item" i18n>No fields defined.</li>
   }
 </ul>
index dfdd204330e1b4ef8f9e4daf70003f480c4902d4..22a61847c453dcc3de58413f773dbb598716f384 100644 (file)
@@ -2,3 +2,12 @@
 .d-block.d-sm-none .dropdown-toggle::after {
     display: none;
 }
+
+.list-group-item .row {
+    opacity: 0;
+    transition: opacity .2s;
+}
+
+.list-group-item .reveal {
+    opacity: 1;
+}
index 5feb17055e4a4ce07ba933b7ce036561621a42e5..1be5484c11e07b7d946a365a4b7e123d6b02b4eb 100644 (file)
@@ -95,6 +95,8 @@ describe('CustomFieldsComponent', () => {
     fixture = TestBed.createComponent(CustomFieldsComponent)
     component = fixture.componentInstance
     fixture.detectChanges()
+    jest.useFakeTimers()
+    jest.advanceTimersByTime(100)
   })
 
   it('should support create, show notification on error / success', () => {
@@ -119,6 +121,7 @@ describe('CustomFieldsComponent', () => {
     editDialog.succeeded.emit(fields[0])
     expect(toastInfoSpy).toHaveBeenCalled()
     expect(reloadSpy).toHaveBeenCalled()
+    jest.advanceTimersByTime(100)
   })
 
   it('should support edit, show notification on error / success', () => {
index 0933cd38f2054f125ba41178eb87a1b0b887a7ff..9300a0c2c7b28ffe737787545f2ba29e1f87e7c4 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, OnInit } from '@angular/core'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
-import { Subject, takeUntil } from 'rxjs'
+import { delay, Subject, takeUntil, tap } from 'rxjs'
 import { DATA_TYPE_LABELS, CustomField } from 'src/app/data/custom-field'
 import { PermissionsService } from 'src/app/services/permissions.service'
 import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
@@ -28,6 +28,9 @@ export class CustomFieldsComponent
 {
   public fields: CustomField[] = []
 
+  public loading: boolean = true
+  public reveal: boolean = false
+
   private unsubscribeNotifier: Subject<any> = new Subject()
   constructor(
     private customFieldsService: CustomFieldsService,
@@ -47,9 +50,16 @@ export class CustomFieldsComponent
   reload() {
     this.customFieldsService
       .listAll()
-      .pipe(takeUntil(this.unsubscribeNotifier))
-      .subscribe((r) => {
-        this.fields = r.results
+      .pipe(
+        takeUntil(this.unsubscribeNotifier),
+        tap((r) => {
+          this.fields = r.results
+        }),
+        delay(100)
+      )
+      .subscribe(() => {
+        this.reveal = true
+        this.loading = false
       })
   }
 
index 9058e688444dd8265a2a02a971dcec8d38830ff7..090afe0cda5f7602e20bd35b0001f99ac55e9147 100644 (file)
@@ -26,7 +26,7 @@
   </h4>
   <ul class="list-group">
     <li class="list-group-item">
-      <div class="row">
+      <div class="row reveal">
         <div class="col" i18n>Name</div>
         <div class="col" i18n>Server</div>
         <div class="col d-none d-sm-block" i18n>Username</div>
       </div>
     </li>
 
+    @if (loadingAccounts) {
+      <li class="list-group-item">
+        <div class="spinner-border spinner-border-sm me-2" role="status"></div>
+        <ng-container i18n>Loading...</ng-container>
+      </li>
+    }
+
     @for (account of mailAccounts; track account) {
       <li class="list-group-item">
-        <div class="row">
+        <div class="row" [class.reveal]="revealAccounts">
           <div class="col d-flex align-items-center">
             <button class="btn btn-link p-0 text-start" type="button" (click)="editMailAccount(account)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.MailAccount)">
               {{account.name}}@switch (account.account_type) {
@@ -76,7 +83,7 @@
         </div>
       </li>
     }
-    @if (mailAccounts.length === 0) {
+    @if (!loadingAccounts && mailAccounts.length === 0) {
       <li class="list-group-item" i18n>No mail accounts defined.</li>
     }
   </ul>
@@ -92,7 +99,7 @@
   </h4>
   <ul class="list-group">
     <li class="list-group-item">
-      <div class="row">
+      <div class="row reveal">
         <div class="col" i18n>Name</div>
         <div class="col d-none d-sm-block" i18n>Sort Order</div>
         <div class="col" i18n>Account</div>
       </div>
     </li>
 
+    @if (loadingRules) {
+      <li class="list-group-item">
+        <div class="spinner-border spinner-border-sm me-2" role="status"></div>
+        <ng-container i18n>Loading...</ng-container>
+      </li>
+    }
+
     @for (rule of mailRules; track rule) {
       <li class="list-group-item">
-        <div class="row">
+        <div class="row" [class.reveal]="revealRules">
           <div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editMailRule(rule)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.MailRule)">{{rule.name}}</button></div>
           <div class="col d-flex align-items-center d-none d-sm-flex">{{rule.order}}</div>
           <div class="col d-flex align-items-center">{{(mailAccountService.getCached(rule.account) | async)?.name}}</div>
         </div>
       </li>
     }
-    @if (mailRules.length === 0) {
+    @if (!loadingRules && mailRules.length === 0) {
       <li class="list-group-item" i18n>No mail rules defined.</li>
     }
   </ul>
index 0c1f432aaf8107cb2c09708b1142be86863604b7..d18ed3d30b1e5530be6f532fe66fcdd4abe8f445 100644 (file)
@@ -2,3 +2,12 @@
 .d-block.d-sm-none .dropdown-toggle::after {
   display: none;
 }
+
+.list-group-item .row {
+  opacity: 0;
+  transition: opacity .2s;
+}
+
+.list-group-item .reveal {
+  opacity: 1;
+}
index 34db62b7e7535ec66a161727d4eb096be0a16670..d3194459fdf3a67b07fe71b169a65c499a549034 100644 (file)
@@ -129,6 +129,8 @@ describe('MailComponent', () => {
     fixture = TestBed.createComponent(MailComponent)
     component = fixture.componentInstance
     fixture.detectChanges()
+    jest.useFakeTimers()
+    jest.advanceTimersByTime(100)
   })
 
   function completeSetup(excludeService = null) {
@@ -386,6 +388,7 @@ describe('MailComponent', () => {
     component.oAuthAccountId = 3
     const editSpy = jest.spyOn(component, 'editMailAccount')
     component.ngOnInit()
+    jest.advanceTimersByTime(200)
     expect(editSpy).toHaveBeenCalled()
   })
 })
index f1c30674baec611e72339ba56a093a14e96accd3..d4bbf77c4209c79c58dc48de2671e2172ba6cb95 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, OnInit, OnDestroy } from '@angular/core'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
-import { Subject, first, takeUntil } from 'rxjs'
+import { Subject, delay, first, takeUntil, tap } from 'rxjs'
 import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
 import { MailAccount, MailAccountType } from 'src/app/data/mail-account'
 import { MailRule } from 'src/app/data/mail-rule'
@@ -47,6 +47,11 @@ export class MailComponent
     return this.settingsService.get(SETTINGS_KEYS.OUTLOOK_OAUTH_URL)
   }
 
+  public loadingRules: boolean = true
+  public revealRules: boolean = false
+  public loadingAccounts: boolean = true
+  public revealAccounts: boolean = false
+
   constructor(
     public mailAccountService: MailAccountService,
     public mailRuleService: MailRuleService,
@@ -62,9 +67,10 @@ export class MailComponent
   ngOnInit(): void {
     this.mailAccountService
       .listAll(null, null, { full_perms: true })
-      .pipe(first(), takeUntil(this.unsubscribeNotifier))
-      .subscribe({
-        next: (r) => {
+      .pipe(
+        first(),
+        takeUntil(this.unsubscribeNotifier),
+        tap((r) => {
           this.mailAccounts = r.results
           if (this.oAuthAccountId) {
             this.editMailAccount(
@@ -73,6 +79,13 @@ export class MailComponent
               )
             )
           }
+        }),
+        delay(100)
+      )
+      .subscribe({
+        next: () => {
+          this.loadingAccounts = false
+          this.revealAccounts = true
         },
         error: (e) => {
           this.toastService.showError(
@@ -84,10 +97,18 @@ export class MailComponent
 
     this.mailRuleService
       .listAll(null, null, { full_perms: true })
-      .pipe(first(), takeUntil(this.unsubscribeNotifier))
+      .pipe(
+        first(),
+        takeUntil(this.unsubscribeNotifier),
+        tap((r) => {
+          this.mailRules = r.results
+        }),
+        delay(100)
+      )
       .subscribe({
         next: (r) => {
-          this.mailRules = r.results
+          this.loadingRules = false
+          this.revealRules = true
         },
         error: (e) => {
           this.toastService.showError($localize`Error retrieving mail rules`, e)
index da04208b4a331785d6d90f4757b51a0cf930de6a..6dbe590c76767eef42746fa937b3403d117b0657 100644 (file)
@@ -53,7 +53,7 @@
         </tr>
       }
       @for (object of data; track object) {
-        <tr (click)="toggleSelected(object); $event.stopPropagation();">
+        <tr (click)="toggleSelected(object); $event.stopPropagation();" class="data-row" [class.reveal]="reveal">
           <td>
             <div class="form-check m-0 ms-2 me-n2">
               <input type="checkbox" class="form-check-input" id="{{typeName}}{{object.id}}" [checked]="selectedObjects.has(object.id)" (click)="toggleSelected(object); $event.stopPropagation();">
index aa2871d68abbd4a435409a7b8d2084cd3267177f..79db7e74fff1a956a55d066069d77ad350460a47 100644 (file)
@@ -10,3 +10,12 @@ tbody tr:last-child td {
 .form-check {
     min-height: 0;
 }
+
+.data-row {
+    opacity: 0;
+    transition: opacity .2s;
+}
+
+.reveal {
+    opacity: 1;
+}
index 9aa876da20fd0e12eab190ef0ace28e6409c13bb..02dec4b5944bc799526ccc29c5b52cf013a26fb2 100644 (file)
@@ -150,6 +150,7 @@ describe('ManagementListComponent', () => {
     fixture.detectChanges()
     expect(component.nameFilter).toBeNull()
     expect(component.data).toEqual(tags)
+    tick(100) // load
   }))
 
   it('should support create, show notification on error / success', () => {
index 27165a8fbd9392cc584df59c3d37f01e105bce97..50ae1c86b58a52f570e69f9588d35f2dc56d7b6c 100644 (file)
@@ -7,7 +7,13 @@ import {
 } from '@angular/core'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 import { Subject } from 'rxjs'
-import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'
+import {
+  debounceTime,
+  delay,
+  distinctUntilChanged,
+  takeUntil,
+  tap,
+} from 'rxjs/operators'
 import {
   MatchingModel,
   MATCHING_ALGORITHMS,
@@ -89,6 +95,8 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
   public selectedObjects: Set<number> = new Set()
   public togggleAll: boolean = false
 
+  public reveal: boolean = false
+
   ngOnInit(): void {
     this.reloadData()
 
@@ -132,7 +140,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
     this.reloadData()
   }
 
-  reloadData() {
+  reloadData(extraParams: { [key: string]: any } = null) {
     this.isLoading = true
     this.clearSelection()
     this.service
@@ -142,12 +150,19 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
         this.sortField,
         this.sortReverse,
         this._nameFilter,
-        true
+        true,
+        extraParams
+      )
+      .pipe(
+        takeUntil(this.unsubscribeNotifier),
+        tap((c) => {
+          this.data = c.results
+          this.collectionSize = c.count
+        }),
+        delay(100)
       )
-      .pipe(takeUntil(this.unsubscribeNotifier))
-      .subscribe((c) => {
-        this.data = c.results
-        this.collectionSize = c.count
+      .subscribe(() => {
+        this.reveal = true
         this.isLoading = false
       })
   }
index ddb8a8654660897fd10bfccf6b7992e4dd7d02af..10c0cc80d74636f72d77684ad3a9f0fb619b51ad 100644 (file)
@@ -13,7 +13,7 @@
 <ul class="list-group">
 
   <li class="list-group-item">
-    <div class="row">
+    <div class="row reveal">
       <div class="col" i18n>Name</div>
       <div class="col d-none d-sm-flex" i18n>Sort order</div>
       <div class="col" i18n>Status</div>
     </div>
   </li>
 
+  @if (loading) {
+    <li class="list-group-item">
+      <div class="spinner-border spinner-border-sm me-2" role="status"></div>
+      <ng-container i18n>Loading...</ng-container>
+    </li>
+  }
+
   @for (workflow of workflows; track workflow.id) {
     <li class="list-group-item">
-      <div class="row">
+      <div class="row" [class.reveal]="reveal">
         <div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editWorkflow(workflow)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.Workflow)">{{workflow.name}}</button></div>
         <div class="col d-flex align-items-center d-none d-sm-flex"><code>{{workflow.order}}</code></div>
         <div class="col d-flex align-items-center">
@@ -69,7 +76,7 @@
       </div>
     </li>
   }
-  @if (workflows.length === 0) {
-    <li class="list-group-item" i18n>No workflows defined.</li>
+  @if (!loading && workflows.length === 0) {
+    <li class="list-group-item" [class.reveal]="reveal" i18n>No workflows defined.</li>
   }
 </ul>
index 0c1f432aaf8107cb2c09708b1142be86863604b7..d18ed3d30b1e5530be6f532fe66fcdd4abe8f445 100644 (file)
@@ -2,3 +2,12 @@
 .d-block.d-sm-none .dropdown-toggle::after {
   display: none;
 }
+
+.list-group-item .row {
+  opacity: 0;
+  transition: opacity .2s;
+}
+
+.list-group-item .reveal {
+  opacity: 1;
+}
index 9d92d9ba76451dd76b3c32bf489be89448e88e77..a268d964cb834520c4f623012dbc1663d93bff25 100644 (file)
@@ -119,10 +119,11 @@ describe('WorkflowsComponent', () => {
     )
     modalService = TestBed.inject(NgbModal)
     toastService = TestBed.inject(ToastService)
-
+    jest.useFakeTimers()
     fixture = TestBed.createComponent(WorkflowsComponent)
     component = fixture.componentInstance
     fixture.detectChanges()
+    jest.advanceTimersByTime(100)
   })
 
   it('should support create, show notification on error / success', () => {
index 592dd3efe32721e875bee3b624a2c99fda766cae..106b99485580b824db616bf2b0745abba4a88ca2 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, OnInit } from '@angular/core'
 import { WorkflowService } from 'src/app/services/rest/workflow.service'
 import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
-import { Subject, takeUntil } from 'rxjs'
+import { delay, Subject, takeUntil, tap } from 'rxjs'
 import { Workflow } from 'src/app/data/workflow'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 import { ToastService } from 'src/app/services/toast.service'
@@ -26,6 +26,9 @@ export class WorkflowsComponent
 
   private unsubscribeNotifier: Subject<any> = new Subject()
 
+  public loading: boolean = false
+  public reveal: boolean = false
+
   constructor(
     private workflowService: WorkflowService,
     public permissionsService: PermissionsService,
@@ -40,11 +43,17 @@ export class WorkflowsComponent
   }
 
   reload() {
+    this.loading = true
     this.workflowService
       .listAll()
-      .pipe(takeUntil(this.unsubscribeNotifier))
-      .subscribe((r) => {
-        this.workflows = r.results
+      .pipe(
+        takeUntil(this.unsubscribeNotifier),
+        tap((r) => (this.workflows = r.results)),
+        delay(100)
+      )
+      .subscribe(() => {
+        this.reveal = true
+        this.loading = false
       })
   }
 
index 228d78f047a5df2e0898dd6d9ac4a3a7d1583910..91b045dbcfe2b6dea195b7790c454c4ea9f86bde 100644 (file)
@@ -203,19 +203,23 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
 
   @supports (hanging-punctuation: first) and (font: -apple-system-body) and (-webkit-appearance: none) {
     // Safari does not like the filters on the image, see https://github.com/paperless-ngx/paperless-ngx/pull/8121
-    .doc-img-container {
-      background-color: #ffffff !important;
-    }
+    .document-card:not(.placeholder-glow),
+    .document-card-large:not(.placeholder-glow) {
+      .doc-img-container {
+        background-color: #ffffff !important;
+        width: 101%;
+      }
 
-    .doc-img {
-      filter: none !important;
-      box-shadow: inset 0px 0px 0px 10px rgba(0,0,0,1);
-    }
+      .doc-img {
+        filter: none !important;
+        box-shadow: inset 0px 0px 0px 10px rgba(0,0,0,1);
+      }
 
-    .doc-img.inverted {
-      filter: none !important;
-      mix-blend-mode: difference;
-      opacity: 0.95;
+      .doc-img.inverted {
+        filter: none !important;
+        mix-blend-mode: difference;
+        opacity: 0.95;
+      }
     }
   }