diff options
Diffstat (limited to 'client/src/app/+admin')
6 files changed, 70 insertions, 68 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 8fdced1c7..b82281c6c 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html | |||
@@ -48,11 +48,12 @@ | |||
48 | <label i18n for="instanceCategories">Main instance categories</label> | 48 | <label i18n for="instanceCategories">Main instance categories</label> |
49 | 49 | ||
50 | <div> | 50 | <div> |
51 | <p-multiSelect | 51 | <my-select-checkbox |
52 | inputId="instanceCategories" [options]="categoryItems" formControlName="categories" [showToggleAll]="false" | 52 | id="instanceCategories" |
53 | [defaultLabel]="getDefaultCategoryLabel()" [selectedItemsLabel]="getSelectedCategoryLabel()" | 53 | formControlName="categories" [availableItems]="categoryItems" |
54 | emptyFilterMessage="No results found" i18n-emptyFilterMessage | 54 | [selectableGroup]="false" |
55 | ></p-multiSelect> | 55 | > |
56 | </my-select-checkbox> | ||
56 | </div> | 57 | </div> |
57 | </div> | 58 | </div> |
58 | 59 | ||
@@ -60,11 +61,12 @@ | |||
60 | <label i18n for="instanceLanguages">Main languages you/your moderators speak</label> | 61 | <label i18n for="instanceLanguages">Main languages you/your moderators speak</label> |
61 | 62 | ||
62 | <div> | 63 | <div> |
63 | <p-multiSelect | 64 | <my-select-checkbox |
64 | inputId="instanceLanguages" [options]="languageItems" formControlName="languages" [showToggleAll]="false" | 65 | id="instanceLanguages" |
65 | [defaultLabel]="getDefaultLanguageLabel()" [selectedItemsLabel]="getSelectedLanguageLabel()" | 66 | formControlName="languages" [availableItems]="languageItems" |
66 | emptyFilterMessage="No results found" i18n-emptyFilterMessage | 67 | [selectableGroup]="false" |
67 | ></p-multiSelect> | 68 | > |
69 | </my-select-checkbox> | ||
68 | </div> | 70 | </div> |
69 | </div> | 71 | </div> |
70 | 72 | ||
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss index 9618100b5..f8f2d5fdc 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss | |||
@@ -30,6 +30,10 @@ input[type=checkbox] { | |||
30 | @include peertube-select-container($form-base-input-width); | 30 | @include peertube-select-container($form-base-input-width); |
31 | } | 31 | } |
32 | 32 | ||
33 | my-select-checkbox { | ||
34 | @include ng-select($form-base-input-width); | ||
35 | } | ||
36 | |||
33 | input[type=submit] { | 37 | input[type=submit] { |
34 | @include peertube-button; | 38 | @include peertube-button; |
35 | @include orange-button; | 39 | @include orange-button; |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index 69629770f..00a0bfad2 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts | |||
@@ -1,11 +1,16 @@ | |||
1 | import { SelectItem } from 'primeng/api' | ||
2 | import { forkJoin } from 'rxjs' | 1 | import { forkJoin } from 'rxjs' |
3 | import { ViewportScroller } from '@angular/common' | 2 | import { ViewportScroller } from '@angular/common' |
4 | import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core' | 3 | import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core' |
5 | import { ConfigService } from '@app/+admin/config/shared/config.service' | 4 | import { ConfigService } from '@app/+admin/config/shared/config.service' |
6 | import { Notifier } from '@app/core' | 5 | import { Notifier } from '@app/core' |
7 | import { ServerService } from '@app/core/server/server.service' | 6 | import { ServerService } from '@app/core/server/server.service' |
8 | import { CustomConfigValidatorsService, FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' | 7 | import { |
8 | CustomConfigValidatorsService, | ||
9 | FormReactive, | ||
10 | FormValidatorService, | ||
11 | SelectOptionsItem, | ||
12 | UserValidatorsService | ||
13 | } from '@app/shared/shared-forms' | ||
9 | import { NgbNav } from '@ng-bootstrap/ng-bootstrap' | 14 | import { NgbNav } from '@ng-bootstrap/ng-bootstrap' |
10 | import { I18n } from '@ngx-translate/i18n-polyfill' | 15 | import { I18n } from '@ngx-translate/i18n-polyfill' |
11 | import { CustomConfig, ServerConfig } from '@shared/models' | 16 | import { CustomConfig, ServerConfig } from '@shared/models' |
@@ -25,8 +30,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A | |||
25 | resolutions: { id: string, label: string, description?: string }[] = [] | 30 | resolutions: { id: string, label: string, description?: string }[] = [] |
26 | transcodingThreadOptions: { label: string, value: number }[] = [] | 31 | transcodingThreadOptions: { label: string, value: number }[] = [] |
27 | 32 | ||
28 | languageItems: SelectItem[] = [] | 33 | languageItems: SelectOptionsItem[] = [] |
29 | categoryItems: SelectItem[] = [] | 34 | categoryItems: SelectOptionsItem[] = [] |
30 | 35 | ||
31 | private serverConfig: ServerConfig | 36 | private serverConfig: ServerConfig |
32 | 37 | ||
@@ -290,22 +295,6 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A | |||
290 | ) | 295 | ) |
291 | } | 296 | } |
292 | 297 | ||
293 | getSelectedLanguageLabel () { | ||
294 | return this.i18n('{{\'{0} languages selected') | ||
295 | } | ||
296 | |||
297 | getDefaultLanguageLabel () { | ||
298 | return this.i18n('No language') | ||
299 | } | ||
300 | |||
301 | getSelectedCategoryLabel () { | ||
302 | return this.i18n('{{\'{0} categories selected') | ||
303 | } | ||
304 | |||
305 | getDefaultCategoryLabel () { | ||
306 | return this.i18n('No category') | ||
307 | } | ||
308 | |||
309 | gotoAnchor () { | 298 | gotoAnchor () { |
310 | const hashToNav = { | 299 | const hashToNav = { |
311 | 'customizations': 'advanced-configuration' | 300 | 'customizations': 'advanced-configuration' |
@@ -331,8 +320,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A | |||
331 | ([ config, languages, categories ]) => { | 320 | ([ config, languages, categories ]) => { |
332 | this.customConfig = config | 321 | this.customConfig = config |
333 | 322 | ||
334 | this.languageItems = languages.map(l => ({ label: l.label, value: l.id })) | 323 | this.languageItems = languages.map(l => ({ label: l.label, id: l.id })) |
335 | this.categoryItems = categories.map(l => ({ label: l.label, value: l.id })) | 324 | this.categoryItems = categories.map(l => ({ label: l.label, id: l.id + '' })) |
336 | 325 | ||
337 | this.updateForm() | 326 | this.updateForm() |
338 | // Force form validation | 327 | // Force form validation |
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html index 3090247e5..d02f6526f 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.html +++ b/client/src/app/+admin/users/user-list/user-list.component.html | |||
@@ -57,12 +57,12 @@ | |||
57 | <div role="menu" class="dropdown-menu" ngbDropdownMenu> | 57 | <div role="menu" class="dropdown-menu" ngbDropdownMenu> |
58 | <div class="dropdown-header" i18n>Table parameters</div> | 58 | <div class="dropdown-header" i18n>Table parameters</div> |
59 | <div ngbDropdownItem class="dropdown-item"> | 59 | <div ngbDropdownItem class="dropdown-item"> |
60 | <p-multiSelect | 60 | <my-select-checkbox |
61 | [options]="columns" [showToggleAll]="true" [(ngModel)]="selectedColumns" optionLabel="label" | 61 | name="columns" |
62 | emptyFilterMessage="No matching column found" i18n-emptyFilterMessage [filter]="false" | 62 | [availableItems]="columns" |
63 | selectedItemsLabel="{0} columns displayed" i18n-emptyFilterMessage [showHeader]="false" | 63 | [selectableGroup]="false" [(ngModel)]="selectedColumns" |
64 | [maxSelectedLabels]="4" | 64 | > |
65 | ></p-multiSelect> | 65 | </my-select-checkbox> |
66 | </div> | 66 | </div> |
67 | <div ngbDropdownItem class="dropdown-item"> | 67 | <div ngbDropdownItem class="dropdown-item"> |
68 | <my-peertube-checkbox inputName="highlightBannedUsers" [(ngModel)]="highlightBannedUsers" | 68 | <my-peertube-checkbox inputName="highlightBannedUsers" [(ngModel)]="highlightBannedUsers" |
@@ -71,14 +71,14 @@ | |||
71 | </div> | 71 | </div> |
72 | </div> | 72 | </div> |
73 | </th> | 73 | </th> |
74 | <th *ngIf="getColumn('username')" pResizableColumn i18n pSortableColumn="username">{{ getColumn('username').label }} <p-sortIcon field="username"></p-sortIcon></th> | 74 | <th *ngIf="isSelected('username')" pResizableColumn i18n pSortableColumn="username">{{ getColumn('username').label }} <p-sortIcon field="username"></p-sortIcon></th> |
75 | <th *ngIf="getColumn('email')" i18n>{{ getColumn('email').label }}</th> | 75 | <th *ngIf="isSelected('email')" i18n>{{ getColumn('email').label }}</th> |
76 | <th *ngIf="getColumn('quota')" style="width: 160px;" i18n pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th> | 76 | <th *ngIf="isSelected('quota')" style="width: 160px;" i18n pSortableColumn="videoQuotaUsed">{{ getColumn('quota').label }} <p-sortIcon field="videoQuotaUsed"></p-sortIcon></th> |
77 | <th *ngIf="getColumn('quotaDaily')" style="width: 160px;" i18n>{{ getColumn('quotaDaily').label }}</th> | 77 | <th *ngIf="isSelected('quotaDaily')" style="width: 160px;" i18n>{{ getColumn('quotaDaily').label }}</th> |
78 | <th *ngIf="getColumn('role')" style="width: 120px;" i18n pSortableColumn="role">{{ getColumn('role').label }} <p-sortIcon field="role"></p-sortIcon></th> | 78 | <th *ngIf="isSelected('role')" style="width: 120px;" i18n pSortableColumn="role">{{ getColumn('role').label }} <p-sortIcon field="role"></p-sortIcon></th> |
79 | <th *ngIf="getColumn('pluginAuth')" style="width: 140px;" pResizableColumn i18n>{{ getColumn('pluginAuth').label }}</th> | 79 | <th *ngIf="isSelected('pluginAuth')" style="width: 140px;" pResizableColumn i18n>{{ getColumn('pluginAuth').label }}</th> |
80 | <th *ngIf="getColumn('createdAt')" style="width: 150px;" i18n pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th> | 80 | <th *ngIf="isSelected('createdAt')" style="width: 150px;" i18n pSortableColumn="createdAt">{{ getColumn('createdAt').label }} <p-sortIcon field="createdAt"></p-sortIcon></th> |
81 | <th *ngIf="getColumn('lastLoginDate')" style="width: 150px;" i18n pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th> | 81 | <th *ngIf="isSelected('lastLoginDate')" style="width: 150px;" i18n pSortableColumn="lastLoginDate">{{ getColumn('lastLoginDate').label }} <p-sortIcon field="lastLoginDate"></p-sortIcon></th> |
82 | </tr> | 82 | </tr> |
83 | </ng-template> | 83 | </ng-template> |
84 | 84 | ||
@@ -101,7 +101,7 @@ | |||
101 | </my-user-moderation-dropdown> | 101 | </my-user-moderation-dropdown> |
102 | </td> | 102 | </td> |
103 | 103 | ||
104 | <td *ngIf="getColumn('username')"> | 104 | <td *ngIf="isSelected('username')"> |
105 | <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]"> | 105 | <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]"> |
106 | <div class="chip two-lines"> | 106 | <div class="chip two-lines"> |
107 | <img | 107 | <img |
@@ -118,7 +118,7 @@ | |||
118 | </a> | 118 | </a> |
119 | </td> | 119 | </td> |
120 | 120 | ||
121 | <td *ngIf="getColumn('email')" [title]="user.email"> | 121 | <td *ngIf="isSelected('email')" [title]="user.email"> |
122 | <ng-container *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus"> | 122 | <ng-container *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus"> |
123 | <a class="table-email" [href]="'mailto:' + user.email">{{ user.email }}</a> | 123 | <a class="table-email" [href]="'mailto:' + user.email">{{ user.email }}</a> |
124 | </ng-container> | 124 | </ng-container> |
@@ -135,7 +135,7 @@ | |||
135 | </ng-template> | 135 | </ng-template> |
136 | </ng-template> | 136 | </ng-template> |
137 | 137 | ||
138 | <td *ngIf="getColumn('quota')"> | 138 | <td *ngIf="isSelected('quota')"> |
139 | <div class="progress" i18n-title title="Total video quota"> | 139 | <div class="progress" i18n-title title="Total video quota"> |
140 | <div class="progress-bar" role="progressbar" [style]="{ width: getUserVideoQuotaPercentage(user) + '%' }" | 140 | <div class="progress-bar" role="progressbar" [style]="{ width: getUserVideoQuotaPercentage(user) + '%' }" |
141 | [attr.aria-valuenow]="user.rawVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuota"> | 141 | [attr.aria-valuenow]="user.rawVideoQuotaUsed" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuota"> |
@@ -145,7 +145,7 @@ | |||
145 | </div> | 145 | </div> |
146 | </td> | 146 | </td> |
147 | 147 | ||
148 | <td *ngIf="getColumn('quotaDaily')"> | 148 | <td *ngIf="isSelected('quotaDaily')"> |
149 | <div class="progress" i18n-title title="Total daily video quota"> | 149 | <div class="progress" i18n-title title="Total daily video quota"> |
150 | <div class="progress-bar secondary" role="progressbar" [style]="{ width: getUserVideoQuotaDailyPercentage(user) + '%' }" | 150 | <div class="progress-bar secondary" role="progressbar" [style]="{ width: getUserVideoQuotaDailyPercentage(user) + '%' }" |
151 | [attr.aria-valuenow]="user.rawVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuotaDaily"> | 151 | [attr.aria-valuenow]="user.rawVideoQuotaUsedDaily" aria-valuemin="0" [attr.aria-valuemax]="user.rawVideoQuotaDaily"> |
@@ -155,18 +155,18 @@ | |||
155 | </div> | 155 | </div> |
156 | </td> | 156 | </td> |
157 | 157 | ||
158 | <td *ngIf="getColumn('role')"> | 158 | <td *ngIf="isSelected('role')"> |
159 | <span *ngIf="user.blocked" class="badge badge-banned" i18n-title title="The user was banned">{{ user.roleLabel }}</span> | 159 | <span *ngIf="user.blocked" class="badge badge-banned" i18n-title title="The user was banned">{{ user.roleLabel }}</span> |
160 | <span *ngIf="!user.blocked" class="badge" [ngClass]="getRoleClass(user.role)">{{ user.roleLabel }}</span> | 160 | <span *ngIf="!user.blocked" class="badge" [ngClass]="getRoleClass(user.role)">{{ user.roleLabel }}</span> |
161 | </td> | 161 | </td> |
162 | 162 | ||
163 | <td *ngIf="getColumn('pluginAuth')"> | 163 | <td *ngIf="isSelected('pluginAuth')"> |
164 | <ng-container *ngIf="user.pluginAuth">{{ user.pluginAuth }}</ng-container> | 164 | <ng-container *ngIf="user.pluginAuth">{{ user.pluginAuth }}</ng-container> |
165 | </td> | 165 | </td> |
166 | 166 | ||
167 | <td *ngIf="getColumn('createdAt')" [title]="user.createdAt">{{ user.createdAt | date: 'short' }}</td> | 167 | <td *ngIf="isSelected('createdAt')" [title]="user.createdAt">{{ user.createdAt | date: 'short' }}</td> |
168 | 168 | ||
169 | <td *ngIf="getColumn('lastLoginDate')" [title]="user.lastLoginDate">{{ user.lastLoginDate | date: 'short' }}</td> | 169 | <td *ngIf="isSelected('lastLoginDate')" [title]="user.lastLoginDate">{{ user.lastLoginDate | date: 'short' }}</td> |
170 | </tr> | 170 | </tr> |
171 | </ng-template> | 171 | </ng-template> |
172 | 172 | ||
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss index 98fc4d027..50080bad6 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.scss +++ b/client/src/app/+admin/users/user-list/user-list.component.scss | |||
@@ -61,6 +61,7 @@ my-global-icon { | |||
61 | 61 | ||
62 | .input-group { | 62 | .input-group { |
63 | @include peertube-input-group(300px); | 63 | @include peertube-input-group(300px); |
64 | |||
64 | input { | 65 | input { |
65 | flex: 1; | 66 | flex: 1; |
66 | } | 67 | } |
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts index 699b2a6da..69d4e917d 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.ts +++ b/client/src/app/+admin/users/user-list/user-list.component.ts | |||
@@ -30,9 +30,9 @@ export class UserListComponent extends RestTable implements OnInit { | |||
30 | 30 | ||
31 | selectedUsers: User[] = [] | 31 | selectedUsers: User[] = [] |
32 | bulkUserActions: DropdownAction<User[]>[][] = [] | 32 | bulkUserActions: DropdownAction<User[]>[][] = [] |
33 | columns: { key: string, label: string }[] | 33 | columns: { id: string, label: string }[] |
34 | 34 | ||
35 | private _selectedColumns: { key: string, label: string }[] | 35 | private _selectedColumns: string[] |
36 | private serverConfig: ServerConfig | 36 | private serverConfig: ServerConfig |
37 | 37 | ||
38 | constructor ( | 38 | constructor ( |
@@ -60,7 +60,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
60 | return this._selectedColumns | 60 | return this._selectedColumns |
61 | } | 61 | } |
62 | 62 | ||
63 | set selectedColumns (val) { | 63 | set selectedColumns (val: string[]) { |
64 | this._selectedColumns = val | 64 | this._selectedColumns = val |
65 | } | 65 | } |
66 | 66 | ||
@@ -112,16 +112,18 @@ export class UserListComponent extends RestTable implements OnInit { | |||
112 | ] | 112 | ] |
113 | 113 | ||
114 | this.columns = [ | 114 | this.columns = [ |
115 | { key: 'username', label: 'Username' }, | 115 | { id: 'username', label: 'Username' }, |
116 | { key: 'email', label: 'Email' }, | 116 | { id: 'email', label: 'Email' }, |
117 | { key: 'quota', label: 'Video quota' }, | 117 | { id: 'quota', label: 'Video quota' }, |
118 | { key: 'role', label: 'Role' }, | 118 | { id: 'role', label: 'Role' }, |
119 | { key: 'createdAt', label: 'Created' } | 119 | { id: 'createdAt', label: 'Created' } |
120 | ] | 120 | ] |
121 | this.selectedColumns = [ ...this.columns ] // make a full copy of the array | 121 | |
122 | this.columns.push({ key: 'quotaDaily', label: 'Daily quota' }) | 122 | this.selectedColumns = this.columns.map(c => c.id) |
123 | this.columns.push({ key: 'pluginAuth', label: 'Auth plugin' }) | 123 | |
124 | this.columns.push({ key: 'lastLoginDate', label: 'Last login' }) | 124 | this.columns.push({ id: 'quotaDaily', label: 'Daily quota' }) |
125 | this.columns.push({ id: 'pluginAuth', label: 'Auth plugin' }) | ||
126 | this.columns.push({ id: 'lastLoginDate', label: 'Last login' }) | ||
125 | } | 127 | } |
126 | 128 | ||
127 | getIdentifier () { | 129 | getIdentifier () { |
@@ -139,8 +141,12 @@ export class UserListComponent extends RestTable implements OnInit { | |||
139 | } | 141 | } |
140 | } | 142 | } |
141 | 143 | ||
142 | getColumn (key: string) { | 144 | isSelected (id: string) { |
143 | return this.selectedColumns.find((col: { key: string }) => col.key === key) | 145 | return this.selectedColumns.find(c => c === id) |
146 | } | ||
147 | |||
148 | getColumn (id: string) { | ||
149 | return this.columns.find(c => c.id === id) | ||
144 | } | 150 | } |
145 | 151 | ||
146 | getUserVideoQuotaPercentage (user: UserForList) { | 152 | getUserVideoQuotaPercentage (user: UserForList) { |