Empty states for tables
authorRigel Kent <sendmemail@rigelk.eu>
Sun, 19 Apr 2020 18:26:25 +0000 (20:26 +0200)
committerRigel Kent <par@rigelk.eu>
Fri, 1 May 2020 14:41:02 +0000 (16:41 +0200)
14 files changed:
client/src/app/+admin/follows/followers-list/followers-list.component.html
client/src/app/+admin/follows/followers-list/followers-list.component.ts
client/src/app/+admin/follows/following-list/following-list.component.html
client/src/app/+admin/follows/following-list/following-list.component.ts
client/src/app/+admin/follows/follows.component.scss
client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.html
client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts
client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html
client/src/app/+admin/moderation/moderation.component.scss
client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
client/src/sass/include/_mixins.scss

index 7455cdf2b27d5d1ce47e6fe6fbc7e9195c19a638..d4f2b8dcc9c1859c176ab8c580ac40121b8340ee 100644 (file)
       </td>
     </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="search" i18n>No follower found matching current filters.</ng-container>
+          <ng-container *ngIf="!search" i18n>Your instance doesn't have any follower.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
index 5859028276166a65ff6f25f8c6eb193e9c70246f..81a91c1d183580f26a52d77ac03d5d8be0713915 100644 (file)
@@ -9,7 +9,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
 @Component({
   selector: 'my-followers-list',
   templateUrl: './followers-list.component.html',
-  styleUrls: [ './followers-list.component.scss' ]
+  styleUrls: [ '../follows.component.scss', './followers-list.component.scss' ]
 })
 export class FollowersListComponent extends RestTable implements OnInit {
   followers: ActorFollow[] = []
index f3bb7216b28bcf5db1b5faa151d2d772cdddacc0..e4189d3341beed1586a1cdd2af49a3dc0d8c6cf8 100644 (file)
       </td>
     </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="search" i18n>No host found matching current filters.</ng-container>
+          <ng-container *ngIf="!search" i18n>Your instance is not follwing any host.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
 
 <my-batch-domains-modal #batchDomainsModal i18n-action action="Follow domains" (domains)="addFollowing($event)"></my-batch-domains-modal>
index 477a6c0d76c86405c05e266e9e1e069afe866b4d..a4dd073026527499fa47d89a2d8d9f47e1f5f193 100644 (file)
@@ -11,7 +11,7 @@ import { BatchDomainsModalComponent } from '@app/+admin/config/shared/batch-doma
 @Component({
   selector: 'my-followers-list',
   templateUrl: './following-list.component.html',
-  styleUrls: [ './following-list.component.scss' ]
+  styleUrls: [ '../follows.component.scss', './following-list.component.scss' ]
 })
 export class FollowingListComponent extends RestTable implements OnInit {
   @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent
index 766d7853b49e7a01c291854abd3d3f990682ca8f..32394f698c324abe0a10bd2efa90f7b0444de89e 100644 (file)
@@ -1,4 +1,10 @@
+@import "mixins";
+
 .form-sub-title {
   flex-grow: 0;
   margin-right: 30px;
 }
+
+.empty-table-message {
+  @include empty-state;
+}
index a8dcc69d2a2a67e1e9269b2b34eea6317d64a282..f3b9809708440a2c08365655859f35c1b155db17 100644 (file)
@@ -19,7 +19,7 @@
     <tr>
       <th style="width: 40px;"></th>
       <th style="width: 160px;" i18n *ngIf="isDisplayingRemoteVideos()">Strategy</th>
-      <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
+      <th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th >
       <th i18n>Video URL</th>
       <th style="width: 100px;" i18n *ngIf="isDisplayingRemoteVideos()">Total size</th>
       <th style="width: 80px;"></th>
       </td>
     </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="isDisplayingRemoteVideos()" i18n>Your instance doesn't mirror any video.</ng-container>
+          <ng-container *ngIf="!isDisplayingRemoteVideos()" i18n>Your instance has no mirrored videos.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
 
 
index 2b62d30a389c7bf619c7a5edb5ee716565e5d5aa..f91800175a08f4945720ab278bff4bc12b65abda 100644 (file)
@@ -13,7 +13,7 @@ import { RedundancyService } from '@app/shared/video/redundancy.service'
 @Component({
   selector: 'my-video-redundancies-list',
   templateUrl: './video-redundancies-list.component.html',
-  styleUrls: [ './video-redundancies-list.component.scss' ]
+  styleUrls: [ '../follows.component.scss', './video-redundancies-list.component.scss' ]
 })
 export class VideoRedundanciesListComponent extends RestTable implements OnInit {
   private static LOCAL_STORAGE_DISPLAY_TYPE = 'video-redundancies-list-display-type'
index d340b5e57296cf32de5b7bc7955423e75012612e..df0a8247ee5242b137b5b7e6a94cf7bb9dd705ba 100644 (file)
@@ -17,7 +17,7 @@
 
   <ng-template pTemplate="header">
     <tr>
-      <th i18n>Account</th>
+      <th style="width: 100%;" i18n>Account</th>
       <th style="width: 190px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
       <th style="width: 100px;"></th> <!-- column for action buttons --> 
     </tr>
 
   <ng-template pTemplate="body" let-accountBlock>
     <tr>
-      <td>{{ accountBlock.blockedAccount.nameWithHost }}</td>
+      <td>
+        <a [href]="accountBlock.blockedAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
+          <div class="chip two-lines">
+            <img
+              class="avatar"
+              [src]="accountBlock.blockedAccount.avatar?.path"
+              (error)="switchToDefaultAvatar($event)"
+              alt="Avatar"
+            >
+            <div>
+              {{ accountBlock.blockedAccount.displayName }}
+              <span class="text-muted">{{ accountBlock.blockedAccount.nameWithHost }}</span>
+            </div>
+          </div>
+        </a>
+      </td>
+
       <td>{{ accountBlock.createdAt }}</td>
       <td class="action-cell">
         <button class="unblock-button" (click)="unblockAccount(accountBlock)" i18n>Unmute</button>
       </td>
     </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="search" i18n>No account found matching current filters.</ng-container>
+          <ng-container *ngIf="!search" i18n>No account found.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
index 607b1cbe0f9fc08bcd56304839e8a2db7631020a..6bd0152345ff3196df8a89cfaadd0c21a806ecb3 100644 (file)
@@ -4,6 +4,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
 import { RestPagination, RestTable } from '@app/shared'
 import { SortMeta } from 'primeng/api'
 import { AccountBlock, BlocklistService } from '@app/shared/blocklist'
+import { Actor } from '@app/shared/actor/actor.model'
 
 @Component({
   selector: 'my-instance-account-blocklist',
@@ -34,6 +35,10 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
     return 'InstanceAccountBlocklistComponent'
   }
 
+  switchToDefaultAvatar ($event: Event) {
+    ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
+  }
+
   unblockAccount (accountBlock: AccountBlock) {
     const blockedAccount = accountBlock.blockedAccount
 
index b6c87fdc8ffe3a28506592af561ca4f56768e826..d4e6933afef5ab89273fd976d9c4e33a779a6c7e 100644 (file)
@@ -21,7 +21,7 @@
 
   <ng-template pTemplate="header">
     <tr>
-      <th i18n>Instance</th>
+      <th style="width: 100%;" i18n>Instance</th>
       <th style="width: 190px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
       <th style="width: 100px;"></th> <!-- column for action buttons -->
     </tr>
       </td>
     </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="search" i18n>No server found matching current filters.</ng-container>
+          <ng-container *ngIf="!search" i18n>No server found.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
 
 <my-batch-domains-modal #batchDomainsModal i18n-action action="Mute domains" (domains)="onDomainsToBlock($event)">
index 97af74541c952e597ac30ec03015982061173dcf..cf06401cf04255aac6f62e21296505c8f54a3203 100644 (file)
   }
 }
 
+.empty-table-message {
+  @include empty-state;
+}
+
 .moderation-expanded {
   font-size: 90%;
 
index e2c08f910d1d6f0ed5fe18478952dd1c342eda67..c15e98942e00faa8dc2334679e351ea5345f35c6 100644 (file)
@@ -39,7 +39,7 @@
           <div class="chip two-lines">
             <img
               class="avatar"
-              [src]="videoAbuse.reporterAccount.avatar.path"
+              [src]="videoAbuse.reporterAccount.avatar?.path"
               (error)="switchToDefaultAvatar($event)"
               alt="Avatar"
             >
         </td>
       </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="search" i18n>No video abuses found matching current filters.</ng-container>
+          <ng-container *ngIf="!search" i18n>No video abuses found.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
 
 <my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal>
index 6375dacd9378dc6e2913bc3da06854ee1ee7c7c5..eade57b71ccd7f4630bc1238d09b54f7642a8b65 100644 (file)
       </td>
     </tr>
   </ng-template>
+
+  <ng-template pTemplate="emptymessage">
+    <tr>
+      <td colspan="6">
+        <div class="empty-table-message">
+          <ng-container *ngIf="search" i18n>No blacklisted video found matching current filters.</ng-container>
+          <ng-container *ngIf="!search" i18n>No blacklisted video found.</ng-container>
+        </div>
+      </td>
+    </tr>
+  </ng-template>
 </p-table>
 
index cfb14ef65ee7208b394430ca3251fe2f77059440..adeee758eda6a0a6b022933df7860880966951b0 100644 (file)
   }
 }
 
+@mixin empty-state {
+  min-height: 40vh;
+  max-height: 500px;
+
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
 @mixin admin-sub-header-responsive ($horizontal-margins) {
   flex-direction: column;