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