refactor(rbac): migrate roles table to react [EE-4711] (#10772)

pull/10792/head
Chaim Lev-Ari 2024-04-09 08:11:29 +03:00 committed by GitHub
parent 7e53d01d0f
commit 48aab77058
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 72 additions and 136 deletions

View File

@ -1,14 +0,0 @@
import controller from './roles-datatable.controller';
import './roles-datatable.css';
export const rolesDatatable = {
templateUrl: './roles-datatable.html',
controller,
bindings: {
titleText: '@',
dataset: '<',
tableKey: '@',
orderBy: '@',
reverseOrder: '<',
},
};

View File

@ -1,15 +0,0 @@
import angular from 'angular';
import { RoleTypes } from '../../models/role';
export default class RolesDatatableController {
/* @ngInject */
constructor($controller, $scope) {
this.limitedFeature = 'rbac-roles';
angular.extend(this, $controller('GenericDatatableController', { $scope }));
}
isDefaultItem(item) {
return item.ID === RoleTypes.STANDARD;
}
}

View File

@ -1,7 +0,0 @@
th.be-visual-indicator-col {
width: 300px;
}
td.be-visual-indicator-col {
text-align: center;
}

View File

@ -1,92 +0,0 @@
<div class="datatable">
<rd-widget>
<rd-widget-body classes="no-padding">
<div class="toolBar vertical-center">
<div class="toolBarTitle vertical-center">
<div class="widget-icon space-right">
<pr-icon icon="'file-code'"></pr-icon>
</div>
{{ $ctrl.titleText }}
</div>
<div class="searchBar vertical-center">
<pr-icon icon="'search'" class="vertical-center"></pr-icon>
<input
type="text"
class="searchInput ml-1"
ng-model="$ctrl.state.textFilter"
ng-change="$ctrl.onTextFilterChange()"
placeholder="Search..."
auto-focus
ng-model-options="{ debounce: 300 }"
/>
</div>
</div>
<div class="table-responsive">
<table class="table-hover nowrap-cells table">
<thead>
<tr>
<th>
<table-column-header
col-title="'Name'"
can-sort="true"
is-sorted="$ctrl.state.orderBy === 'Name'"
is-sorted-desc="$ctrl.state.orderBy === 'Name' && $ctrl.state.reverseOrder"
ng-click="$ctrl.changeOrderBy('Name')"
></table-column-header>
</th>
<th>
<table-column-header
col-title="'Description'"
can-sort="true"
is-sorted="$ctrl.state.orderBy === 'Description'"
is-sorted-desc="$ctrl.state.orderBy === 'Description' && $ctrl.state.reverseOrder"
ng-click="$ctrl.changeOrderBy('Description')"
></table-column-header>
</th>
<th class="be-visual-indicator-col"></th>
</tr>
</thead>
<tbody>
<tr
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
ng-class="{ active: item.Checked }"
>
<td>{{ item.Name }}</td>
<td>{{ item.Description }}</td>
<td class="be-visual-indicator-col" ng-switch on="$ctrl.isDefaultItem(item)">
<span ng-switch-when="false">
<be-feature-indicator feature="$ctrl.limitedFeature"></be-feature-indicator>
</span>
<b ng-switch-when="true">Default</b>
</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="3" class="text-muted text-center">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="3" class="text-muted text-center">No role available.</td>
</tr>
</tbody>
</table>
</div>
<div class="footer" ng-if="$ctrl.dataset">
<div class="infoBar" ng-if="$ctrl.state.selectedItemCount !== 0"> {{ $ctrl.state.selectedItemCount }} item(s) selected </div>
<div class="paginationControls">
<form class="form-inline">
<span class="limitSelector">
<span style="margin-right: 5px"> Items per page </span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</span>
<dir-pagination-controls max-size="5"></dir-pagination-controls>
</form>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>

View File

@ -1,7 +1,6 @@
import { AccessHeaders } from '../authorization-guard';
import { rolesView } from './views/roles';
import { accessViewer } from './components/access-viewer';
import { rolesDatatable } from './components/roles-datatable';
import { RoleService } from './services/role.service';
import { RolesFactory } from './services/role.rest';
@ -10,7 +9,6 @@ angular
.module('portainer.rbac', ['ngResource'])
.constant('API_ENDPOINT_ROLES', 'api/roles')
.component('accessViewer', accessViewer)
.component('rolesDatatable', rolesDatatable)
.component('rolesView', rolesView)
.factory('RoleService', RoleService)
.factory('Roles', RolesFactory)

View File

@ -1,10 +1,6 @@
<page-header title="'Roles'" breadcrumbs="['Role management']" reload="true"> </page-header>
<div class="row">
<div class="col-sm-12">
<roles-datatable title-text="Roles" dataset="$ctrl.roles" table-key="roles"></roles-datatable>
</div>
</div>
<rbac-roles-datatable dataset="$ctrl.roles"></rbac-roles-datatable>
<div class="row">
<access-viewer> </access-viewer>

View File

@ -5,6 +5,7 @@ import { withUIRouter } from '@/react-tools/withUIRouter';
import { UsersDatatable } from '@/react/portainer/users/ListView/UsersDatatable/UsersDatatable';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { EffectiveAccessViewerDatatable } from '@/react/portainer/users/RolesView/AccessViewer/EffectiveAccessViewerDatatable';
import { RbacRolesDatatable } from '@/react/portainer/users/RolesView/RbacRolesDatatable';
export const usersModule = angular
.module('portainer.app.react.components.users', [])
@ -17,4 +18,5 @@ export const usersModule = angular
r2a(withUIRouter(withCurrentUser(EffectiveAccessViewerDatatable)), [
'dataset',
])
).name;
)
.component('rbacRolesDatatable', r2a(RbacRolesDatatable, ['dataset'])).name;

View File

@ -0,0 +1,68 @@
import { FileCode } from 'lucide-react';
import { createColumnHelper } from '@tanstack/react-table';
import _ from 'lodash';
import { RoleTypes } from '@/portainer/rbac/models/role';
import { Datatable } from '@@/datatables';
import { createPersistedStore } from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
import { BEFeatureIndicator } from '@@/BEFeatureIndicator';
import { isBE } from '../../feature-flags/feature-flags.service';
import { FeatureId } from '../../feature-flags/enums';
import { RbacRole } from './types';
const tableKey = 'rbac-roles-table';
const store = createPersistedStore(tableKey);
const columns = getColumns();
export function RbacRolesDatatable({
dataset,
}: {
dataset: Array<RbacRole> | undefined;
}) {
const tableState = useTableState(store, tableKey);
return (
<Datatable
title="Roles"
titleIcon={FileCode}
dataset={dataset || []}
columns={columns}
isLoading={!dataset}
emptyContentLabel="No role available."
settingsManager={tableState}
disableSelect
/>
);
}
function getColumns() {
const columnHelper = createColumnHelper<RbacRole>();
return _.compact([
columnHelper.accessor('Name', {
header: 'Name',
}),
columnHelper.accessor('Description', {
header: 'Description',
}),
!isBE &&
columnHelper.display({
id: 'be-indicator',
cell: ({ row: { original: item } }) =>
item.Id === RoleTypes.STANDARD ? (
<b>Default</b>
) : (
<BEFeatureIndicator featureId={FeatureId.RBAC_ROLES} />
),
meta: {
className: 'text-center',
},
}),
]);
}