refactor(registries): migrate gitlab projects table to react [EE-4709] (#10792)

pull/11374/merge
Chaim Lev-Ari 2024-04-09 08:52:44 +03:00 committed by GitHub
parent 3f3db75d85
commit 960d18998f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 116 additions and 198 deletions

View File

@ -1,104 +0,0 @@
<div class="datatable">
<rd-widget>
<rd-widget-body classes="no-padding">
<div class="toolBar vertical-center">
<div class="toolBarTitle vertical-center">
<pr-icon icon="$ctrl.titleIcon" class-name="'icon-blue'"></pr-icon>
{{ $ctrl.titleText }}
</div>
<div class="searchBar vertical-center">
<pr-icon icon="'search'" class="searchIcon"></pr-icon>
<input type="text" class="searchInput" ng-model="$ctrl.state.textFilter" ng-change="$ctrl.onTextFilterChange()" placeholder="Search..." auto-focus />
</div>
</div>
<div class="table-responsive">
<table class="table-hover nowrap-cells table">
<thead>
<tr>
<th>
<div class="vertical-center">
<span class="md-checkbox vertical-center">
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
<label for="select_all"></label>
</span>
<table-column-header
col-title="'Namespace'"
can-sort="true"
is-sorted="$ctrl.state.orderBy === 'Namespace'"
is-sorted-desc="$ctrl.state.orderBy === 'Namespace' && $ctrl.state.reverseOrder"
ng-click="$ctrl.changeOrderBy('Namespace')"
></table-column-header>
</div>
</th>
<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="'PathWithNamespace'"
can-sort="true"
is-sorted="$ctrl.state.orderBy === 'PathWithNamespace'"
is-sorted-desc="$ctrl.state.orderBy === 'PathWithNamespace' && $ctrl.state.reverseOrder"
ng-click="$ctrl.changeOrderBy('PathWithNamespace')"
></table-column-header>
</th>
<th>
<table-column-header col-title="'Description'" can-sort="false"></table-column-header>
</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>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" ng-disabled="$ctrl.disableSelection(item)" />
<label for="select_{{ $index }}"></label>
</span>
{{ item.Namespace }}
</td>
<td>
{{ item.Name }}
</td>
<td>
{{ item.PathWithNamespace }}
</td>
<td>
{{ item.Description }}
</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="3" class="text-muted text-center">No projects 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()" data-cy="component-paginationSelect">
<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,13 +0,0 @@
angular.module('portainer.app').component('gitlabProjectsDatatable', {
templateUrl: './gitlabProjectsDatatable.html',
controller: 'GitlabProjectsDatatableController',
bindings: {
titleText: '@',
titleIcon: '@',
dataset: '<',
tableKey: '@',
orderBy: '@',
reverseOrder: '<',
state: '=',
},
});

View File

@ -1,50 +0,0 @@
angular.module('portainer.app').controller('GitlabProjectsDatatableController', [
'$scope',
'$controller',
'DatatableService',
function ($scope, $controller, DatatableService) {
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
this.disableSelection = function (item) {
return !this.allowSelection(item);
};
// based on RegistryGitlabProject model
this.allowSelection = function (item) {
return item.RegistryEnabled;
};
this.$onInit = function () {
this.setDefaults();
this.prepareTableFromDataset();
this.state.orderBy = this.orderBy;
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
if (storedOrder !== null) {
this.state.reverseOrder = storedOrder.reverse;
this.state.orderBy = storedOrder.orderBy;
}
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
if (textFilter !== null) {
this.state.textFilter = textFilter;
this.onTextFilterChange();
}
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
if (storedFilters !== null) {
this.filters = storedFilters;
}
if (this.filters && this.filters.state) {
this.filters.state.open = false;
}
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
if (storedSettings !== null) {
this.settings = storedSettings;
this.settings.open = false;
}
this.onSettingsRepeaterChange();
};
},
]);

View File

@ -0,0 +1,12 @@
/* @ngInject */
export default function RegistryFormGitlab($scope) {
this.selectedRegistries = [];
this.onChangeRegistries = onChangeRegistries.bind(this);
function onChangeRegistries(value) {
$scope.$evalAsync(() => {
this.selectedRegistries = value;
});
}
}

View File

@ -130,24 +130,15 @@
If you can't select a project, make sure that registry feature is activated on it.
</span>
</div>
<div class="form-group">
<div class="col-sm-12">
<gitlab-projects-datatable
title-text="GitLab projects"
title-icon="list"
dataset="$ctrl.projects"
table-key="gitlab_projects"
state="$ctrl.state.gitlab"
order-by="Name"
></gitlab-projects-datatable>
</div>
</div>
<gitlab-project-selector dataset="$ctrl.projects" value="$ctrl.selectedRegistries" on-change="($ctrl.onChangeRegistries)"></gitlab-project-selector>
<div class="form-group">
<div class="col-sm-12">
<button
ng-click="$ctrl.createRegistries()"
ng-click="$ctrl.createRegistries($ctrl.selectedRegistries)"
class="btn btn-primary btn-sm"
ng-disabled="$ctrl.actionInProgress || !$ctrl.state.gitlab.selectedItemCount"
ng-disabled="$ctrl.actionInProgress || !$ctrl.selectedRegistries.length"
button-spinner="$ctrl.actionInProgress"
analytics-on
analytics-category="portainer"

View File

@ -1,5 +1,8 @@
import controller from './registry-form-gitlab.controller';
angular.module('portainer.app').component('registryFormGitlab', {
templateUrl: './registry-form-gitlab.html',
controller,
bindings: {
model: '=',
retrieveRegistries: '<',

View File

@ -1,8 +0,0 @@
export function RegistryGitlabProject(project) {
this.Id = project.id;
this.Description = project.description;
this.Name = project.name;
this.Namespace = project.namespace ? project.namespace.name : '';
this.PathWithNamespace = project.path_with_namespace;
this.RegistryEnabled = project.container_registry_enabled;
}

View File

@ -5,6 +5,7 @@ import { withReactQuery } from '@/react-tools/withReactQuery';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { RepositoriesDatatable } from '@/react/portainer/registries/repositories/ListView/RepositoriesDatatable';
import { TagsDatatable } from '@/react/portainer/registries/repositories/ItemView/TagsDatatable/TagsDatatable';
import { GitlabProjectTable } from '@/react/portainer/registries/CreateView/GitlabProjectsTable/GitlabProjectsTable';
export const registriesModule = angular
.module('portainer.app.react.components.registries', [])
@ -20,4 +21,8 @@ export const registriesModule = angular
'onRemove',
'onRetag',
])
)
.component(
'gitlabProjectSelector',
r2a(GitlabProjectTable, ['dataset', 'onChange', 'value'])
).name;

View File

@ -84,6 +84,10 @@ angular.module('portainer.app').factory('RegistryService', [
return Registries.create(payload).$promise;
}
/**
* @param {import('@/portainer/models/registry').RegistryCreateFormValues} model
* @param {Array<import('@/react/portainer/registries/types/gitlabProject').RegistryGitlabProject>} projects
*/
function createGitlabRegistries(model, projects) {
const promises = [];
_.forEach(projects, (p) => {

View File

@ -1,5 +1,5 @@
import _ from 'lodash-es';
import { RegistryGitlabProject } from '../models/gitlabRegistry';
import { RegistryGitlabProject } from '@/react/portainer/registries/types/gitlabProject';
import { RegistryRepositoryGitlabViewModel } from '../models/registryRepository';
angular.module('portainer.app').factory('RegistryGitlabService', [

View File

@ -12,12 +12,6 @@ class CreateRegistryController {
this.state = {
actionInProgress: false,
overrideConfiguration: false,
gitlab: {
get selectedItemCount() {
return this.selectedItems.length || 0;
},
selectedItems: [],
},
originViewReference: 'portainer.registries',
originalEndpointId: null,
};
@ -121,11 +115,11 @@ class CreateRegistryController {
});
}
createGitlabRegistries() {
createGitlabRegistries(registries) {
return this.$async(async () => {
try {
this.state.actionInProgress = true;
await this.RegistryService.createGitlabRegistries(this.model, this.state.gitlab.selectedItems);
await this.RegistryService.createGitlabRegistries(this.model, registries);
this.Notifications.success('Success', 'Registries successfully created');
this.$state.go(this.state.originViewReference, { endpointId: this.state.originalEndpointId });
} catch (err) {

View File

@ -0,0 +1,51 @@
import { createColumnHelper } from '@tanstack/react-table';
import { ListIcon } from 'lucide-react';
import { createPersistedStore } from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
import { Datatable } from '@@/datatables';
import { withControlledSelected } from '@@/datatables/extend-options/withControlledSelected';
import { RegistryGitlabProject } from '../../types/gitlabProject';
const helper = createColumnHelper<RegistryGitlabProject>();
const columns = [
helper.accessor('Namespace', {}),
helper.accessor('Name', {}),
helper.accessor('PathWithNamespace', {
header: 'Path with namespace',
}),
helper.accessor('Description', {}),
];
const tableKey = 'gitlab-projects';
const store = createPersistedStore(tableKey, 'Name');
export function GitlabProjectTable({
dataset,
value,
onChange,
}: {
dataset: RegistryGitlabProject[];
value: RegistryGitlabProject[];
onChange: (value: RegistryGitlabProject[]) => void;
}) {
const tableState = useTableState(store, tableKey);
return (
<Datatable
columns={columns}
dataset={dataset}
settingsManager={tableState}
emptyContentLabel="No projects available."
title="Gitlab projects"
titleIcon={ListIcon}
extendTableOptions={withControlledSelected(
(ids) => onChange(dataset.filter(({ Id }) => ids.includes(`${Id}`))),
value.map(({ Id }) => `${Id}`)
)}
isRowSelectable={({ original: item }) => item.RegistryEnabled}
/>
);
}

View File

@ -0,0 +1,33 @@
interface GitlabProjectResponse {
id: number;
name: string;
description: string;
namespace?: {
name: string;
};
path_with_namespace: string;
container_registry_enabled: boolean;
}
export class RegistryGitlabProject {
Id: number;
Description: string;
Name: string;
Namespace: string;
PathWithNamespace: string;
RegistryEnabled: boolean;
constructor(project: GitlabProjectResponse) {
this.Id = project.id;
this.Description = project.description;
this.Name = project.name;
this.Namespace = project.namespace ? project.namespace.name : '';
this.PathWithNamespace = project.path_with_namespace;
this.RegistryEnabled = project.container_registry_enabled;
}
}