Dave Borowitz | 8cdc76b | 2018-03-26 10:04:27 -0400 | [diff] [blame] | 1 | /** |
| 2 | * @license |
| 3 | * Copyright (C) 2017 The Android Open Source Project |
| 4 | * |
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | */ |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 17 | |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 18 | import '../../shared/gr-dialog/gr-dialog'; |
| 19 | import '../../shared/gr-list-view/gr-list-view'; |
| 20 | import '../../shared/gr-overlay/gr-overlay'; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 21 | import '../gr-create-group-dialog/gr-create-group-dialog'; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 22 | import {GerritNav} from '../../core/gr-navigation/gr-navigation'; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 23 | import {AppElementAdminParams} from '../../gr-app-types'; |
| 24 | import {GrOverlay} from '../../shared/gr-overlay/gr-overlay'; |
Dmitrii Filippov | 5561654 | 2020-10-05 19:07:27 +0200 | [diff] [blame] | 25 | import {GroupId, GroupInfo, GroupName} from '../../../types/common'; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 26 | import {GrCreateGroupDialog} from '../gr-create-group-dialog/gr-create-group-dialog'; |
Milutin Kristofic | 6015013 | 2020-11-23 20:15:23 +0100 | [diff] [blame] | 27 | import {fireTitleChange} from '../../../utils/event-util'; |
Chris Poucet | c6e880b | 2021-11-15 19:57:06 +0100 | [diff] [blame] | 28 | import {getAppContext} from '../../../services/app-context'; |
Frank Borden | 949a17d | 2021-09-28 11:30:41 +0000 | [diff] [blame] | 29 | import {SHOWN_ITEMS_COUNT} from '../../../constants/constants'; |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 30 | import {tableStyles} from '../../../styles/gr-table-styles'; |
| 31 | import {sharedStyles} from '../../../styles/shared-styles'; |
| 32 | import {LitElement, PropertyValues, html} from 'lit'; |
| 33 | import {customElement, query, property, state} from 'lit/decorators'; |
| 34 | import {assertIsDefined} from '../../../utils/common-util'; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 35 | |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 36 | declare global { |
| 37 | interface HTMLElementTagNameMap { |
| 38 | 'gr-admin-group-list': GrAdminGroupList; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 39 | } |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 40 | } |
| 41 | |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 42 | @customElement('gr-admin-group-list') |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 43 | export class GrAdminGroupList extends LitElement { |
| 44 | readonly path = '/admin/groups'; |
| 45 | |
| 46 | @query('#createOverlay') private createOverlay?: GrOverlay; |
| 47 | |
| 48 | @query('#createNewModal') private createNewModal?: GrCreateGroupDialog; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 49 | |
| 50 | @property({type: Object}) |
| 51 | params?: AppElementAdminParams; |
| 52 | |
| 53 | /** |
| 54 | * Offset of currently visible query results. |
| 55 | */ |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 56 | @state() private offset = 0; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 57 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 58 | @state() private hasNewGroupName = false; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 59 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 60 | @state() private createNewCapability = false; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 61 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 62 | // private but used in test |
| 63 | @state() groups: GroupInfo[] = []; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 64 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 65 | @state() private groupsPerPage = 25; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 66 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 67 | // private but used in test |
| 68 | @state() loading = true; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 69 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 70 | @state() private filter = ''; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 71 | |
Chris Poucet | c6e880b | 2021-11-15 19:57:06 +0100 | [diff] [blame] | 72 | private readonly restApiService = getAppContext().restApiService; |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 73 | |
Chris Poucet | 59dad57 | 2021-08-20 15:25:36 +0000 | [diff] [blame] | 74 | override connectedCallback() { |
Ben Rohlfs | 5f520da | 2021-03-10 14:58:43 +0100 | [diff] [blame] | 75 | super.connectedCallback(); |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 76 | this.getCreateGroupCapability(); |
Milutin Kristofic | 6015013 | 2020-11-23 20:15:23 +0100 | [diff] [blame] | 77 | fireTitleChange(this, 'Groups'); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 78 | } |
| 79 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 80 | static override get styles() { |
| 81 | return [tableStyles, sharedStyles]; |
| 82 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 83 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 84 | override render() { |
| 85 | return html` |
| 86 | <gr-list-view |
| 87 | .createNew=${this.createNewCapability} |
| 88 | .filter=${this.filter} |
| 89 | .items=${this.groups} |
| 90 | .itemsPerPage=${this.groupsPerPage} |
| 91 | .loading=${this.loading} |
| 92 | .offset=${this.offset} |
| 93 | .path=${this.path} |
| 94 | @create-clicked=${() => this.handleCreateClicked()} |
| 95 | > |
| 96 | <table id="list" class="genericList"> |
| 97 | <tbody> |
| 98 | <tr class="headerRow"> |
| 99 | <th class="name topHeader">Group Name</th> |
| 100 | <th class="description topHeader">Group Description</th> |
| 101 | <th class="visibleToAll topHeader">Visible To All</th> |
| 102 | </tr> |
| 103 | <tr |
| 104 | id="loading" |
| 105 | class="loadingMsg ${this.loading ? 'loading' : ''}" |
| 106 | > |
| 107 | <td>Loading...</td> |
| 108 | </tr> |
| 109 | </tbody> |
| 110 | <tbody class=${this.loading ? 'loading' : ''}> |
| 111 | ${this.groups |
| 112 | .slice(0, SHOWN_ITEMS_COUNT) |
| 113 | .map(group => this.renderGroupList(group))} |
| 114 | </tbody> |
| 115 | </table> |
| 116 | </gr-list-view> |
| 117 | <gr-overlay id="createOverlay" with-backdrop> |
| 118 | <gr-dialog |
| 119 | id="createDialog" |
| 120 | class="confirmDialog" |
| 121 | ?disabled=${!this.hasNewGroupName} |
| 122 | confirm-label="Create" |
| 123 | confirm-on-enter |
| 124 | @confirm=${() => this.handleCreateGroup()} |
| 125 | @cancel=${() => this.handleCloseCreate()} |
| 126 | > |
| 127 | <div class="header" slot="header">Create Group</div> |
| 128 | <div class="main" slot="main"> |
| 129 | <gr-create-group-dialog |
| 130 | id="createNewModal" |
| 131 | @has-new-group-name=${this.handleHasNewGroupName} |
| 132 | ></gr-create-group-dialog> |
| 133 | </div> |
| 134 | </gr-dialog> |
| 135 | </gr-overlay> |
| 136 | `; |
| 137 | } |
| 138 | |
| 139 | private renderGroupList(group: GroupInfo) { |
| 140 | return html` |
| 141 | <tr class="table"> |
| 142 | <td class="name"> |
| 143 | <a href=${this.computeGroupUrl(group.id)}>${group.name}</a> |
| 144 | </td> |
| 145 | <td class="description">${group.description}</td> |
| 146 | <td class="visibleToAll"> |
| 147 | ${group.options?.visible_to_all === true ? 'Y' : 'N'} |
| 148 | </td> |
| 149 | </tr> |
| 150 | `; |
| 151 | } |
| 152 | |
| 153 | override willUpdate(changedProperties: PropertyValues) { |
| 154 | if (changedProperties.has('params')) { |
| 155 | this.paramsChanged(); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // private but used in test |
| 160 | paramsChanged() { |
| 161 | this.filter = this.params?.filter ?? ''; |
| 162 | this.offset = Number(this.params?.offset ?? 0); |
| 163 | this.maybeOpenCreateOverlay(this.params); |
| 164 | |
| 165 | return this.getGroups(this.filter, this.groupsPerPage, this.offset); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 166 | } |
Paladox none | fea8d95 | 2017-05-28 15:48:40 +0000 | [diff] [blame] | 167 | |
Dmitrii Filippov | 3fd2b10 | 2019-11-15 16:16:46 +0100 | [diff] [blame] | 168 | /** |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 169 | * Opens the create overlay if the route has a hash 'create' |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 170 | * |
| 171 | * private but used in test |
Tao Zhou | 9a07681 | 2019-12-17 09:59:28 +0100 | [diff] [blame] | 172 | */ |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 173 | maybeOpenCreateOverlay(params?: AppElementAdminParams) { |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 174 | if (params?.openCreateModal) { |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 175 | assertIsDefined(this.createOverlay, 'createOverlay'); |
| 176 | this.createOverlay.open(); |
Dmitrii Filippov | 3fd2b10 | 2019-11-15 16:16:46 +0100 | [diff] [blame] | 177 | } |
Dmitrii Filippov | 3fd2b10 | 2019-11-15 16:16:46 +0100 | [diff] [blame] | 178 | } |
| 179 | |
David Pursehouse | f7d7df4 | 2020-05-21 14:28:11 +0900 | [diff] [blame] | 180 | /** |
| 181 | * Generates groups link (/admin/groups/<uuid>) |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 182 | * |
| 183 | * private but used in test |
David Pursehouse | f7d7df4 | 2020-05-21 14:28:11 +0900 | [diff] [blame] | 184 | */ |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 185 | computeGroupUrl(id: string) { |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 186 | return GerritNav.getUrlForGroup(decodeURIComponent(id) as GroupId); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 187 | } |
| 188 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 189 | private getCreateGroupCapability() { |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 190 | return this.restApiService.getAccount().then(account => { |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 191 | if (!account) return; |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 192 | return this.restApiService |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 193 | .getAccountCapabilities(['createGroup']) |
| 194 | .then(capabilities => { |
| 195 | if (capabilities?.createGroup) { |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 196 | this.createNewCapability = true; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 197 | } |
| 198 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 199 | }); |
| 200 | } |
| 201 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 202 | private getGroups(filter: string, groupsPerPage: number, offset?: number) { |
| 203 | this.groups = []; |
| 204 | this.loading = true; |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 205 | return this.restApiService |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 206 | .getGroups(filter, groupsPerPage, offset) |
| 207 | .then(groups => { |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 208 | if (!groups) return; |
| 209 | this.groups = Object.keys(groups).map(key => { |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 210 | const group = groups[key]; |
Dmitrii Filippov | 5561654 | 2020-10-05 19:07:27 +0200 | [diff] [blame] | 211 | group.name = key as GroupName; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 212 | return group; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 213 | }); |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 214 | }) |
| 215 | .finally(() => { |
| 216 | this.loading = false; |
Tao Zhou | 7d334b4 | 2020-08-31 10:56:33 +0200 | [diff] [blame] | 217 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 218 | } |
| 219 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 220 | private refreshGroupsList() { |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 221 | this.restApiService.invalidateGroupsCache(); |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 222 | return this.getGroups(this.filter, this.groupsPerPage, this.offset); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 223 | } |
| 224 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 225 | // private but used in test |
| 226 | handleCreateGroup() { |
| 227 | assertIsDefined(this.createNewModal, 'createNewModal'); |
| 228 | this.createNewModal.handleCreateGroup().then(() => { |
| 229 | this.refreshGroupsList(); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 230 | }); |
| 231 | } |
| 232 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 233 | // private but used in test |
| 234 | handleCloseCreate() { |
| 235 | assertIsDefined(this.createOverlay, 'createOverlay'); |
| 236 | this.createOverlay.close(); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 237 | } |
| 238 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 239 | // private but used in test |
| 240 | handleCreateClicked() { |
| 241 | assertIsDefined(this.createOverlay, 'createOverlay'); |
| 242 | this.createOverlay.open().then(() => { |
| 243 | assertIsDefined(this.createNewModal, 'createNewModal'); |
| 244 | this.createNewModal.focus(); |
Dhruv Srivastava | 2632075 | 2021-02-02 12:43:27 +0100 | [diff] [blame] | 245 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 246 | } |
| 247 | |
Paladox none | a662999 | 2021-11-14 22:05:24 +0000 | [diff] [blame] | 248 | private handleHasNewGroupName() { |
| 249 | assertIsDefined(this.createNewModal, 'createNewModal'); |
| 250 | this.hasNewGroupName = !!this.createNewModal.name; |
Paladox none | 65d320c | 2021-11-14 15:20:09 +0000 | [diff] [blame] | 251 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 252 | } |