fix(ui): ui consistency and bug fixes [r8s-1061] (#2880)

This commit is contained in:
Ali
2026-06-12 11:49:45 +12:00
committed by GitHub
parent 0da42c01b6
commit 391eb22d98
15 changed files with 51 additions and 125 deletions
-12
View File
@@ -268,17 +268,6 @@ angular
},
};
var groupAccess = {
name: 'portainer.groups.group.access',
url: '/access',
views: {
'content@': {
templateUrl: './views/groups/access/groupAccess.html',
controller: 'GroupAccessController',
},
},
};
var home = {
name: 'portainer.home',
url: '/home?redirect&environmentId&environmentName&route&groupBy&groupFilter&search&order',
@@ -485,7 +474,6 @@ angular
$stateRegistryProvider.register(edgeAutoCreateScript);
$stateRegistryProvider.register(groups);
$stateRegistryProvider.register(group);
$stateRegistryProvider.register(groupAccess);
$stateRegistryProvider.register(groupCreation);
$stateRegistryProvider.register(home);
$stateRegistryProvider.register(gitopsBase);
@@ -1,41 +0,0 @@
<page-header
title="'Environment group access'"
breadcrumbs="[
{ label:'Groups', link:'portainer.groups' },
{
label:group.Name,
link: 'portainer.groups.group',
linkParams:{id: group.Id}
}, 'Access management']"
reload="true"
>
</page-header>
<div class="row" ng-if="group">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header icon="dice-4" title-text="Group"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>Name</td>
<td>
{{ group.Name }}
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>
<por-access-management
ng-if="group"
access-controlled-entity="group"
entity-type="group"
action-in-progress="state.actionInProgress"
update-access="updateAccess"
limited-feature="limitedFeature"
></por-access-management>
@@ -1,40 +0,0 @@
import { FeatureId } from '@/react/portainer/feature-flags/enums';
angular.module('portainer.app').controller('GroupAccessController', [
'$scope',
'$state',
'$transition$',
'GroupService',
'Notifications',
function ($scope, $state, $transition$, GroupService, Notifications) {
$scope.limitedFeature = FeatureId.RBAC_ROLES;
$scope.updateAccess = function () {
$scope.state.actionInProgress = true;
GroupService.updateGroup($scope.group, $scope.group.AssociatedEndpoints)
.then(() => {
Notifications.success('Success', 'Access successfully updated');
$state.reload();
})
.catch((err) => {
$scope.state.actionInProgress = false;
Notifications.error('Failure', err, 'Unable to update accesses');
});
};
function initView() {
var groupId = $transition$.params().id;
$scope.state = { actionInProgress: false };
GroupService.group(groupId)
.then(function success(data) {
$scope.group = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load view');
});
}
initView();
},
]);
@@ -3,15 +3,19 @@ import clsx from 'clsx';
import { Button } from '@@/buttons';
type Props = Omit<ComponentProps<typeof Button>, 'color' | 'size'>;
type Props = Omit<ComponentProps<typeof Button>, 'size'>;
export function ActionBarButton({ className, ...props }: Props) {
export function ActionBarButton({
className,
color = 'none',
...props
}: Props) {
return (
<Button
color="none"
color={color}
size="small"
className={clsx(
'rounded-md !px-3 !py-1.5 transition-colors',
'!ml-0 rounded-md !px-3 !py-1.5 transition-colors',
'hover:bg-[var(--bg-blocklist-hover-color)] hover:text-[var(--text-blocklist-hover-color)]',
className
)}
@@ -94,15 +94,16 @@ function buildPageNumbers(
const btnBase = clsx(
'flex h-7 w-7 items-center justify-center rounded',
'border border-solid border-gray-4 th-dark:border-gray-7',
'text-sm text-gray-7 th-dark:text-gray-4',
'transition-colors hover:bg-gray-3 th-dark:hover:bg-gray-iron-9',
'border border-solid border-gray-4 th-highcontrast:border-white th-dark:border-gray-7',
'text-sm text-gray-7 th-highcontrast:text-white th-highcontrast:hover:text-black th-dark:text-gray-4',
'transition-colors hover:bg-gray-3 th-highcontrast:bg-black th-highcontrast:hover:bg-white th-dark:hover:bg-gray-iron-9',
'disabled:cursor-not-allowed disabled:opacity-40'
);
const btnActivePage = clsx(
'border-blue-7 bg-blue-7 text-white hover:bg-blue-6',
'th-dark:border-blue-7 th-dark:bg-blue-7 th-dark:text-white'
'border-blue-7 bg-blue-7 text-white hover:!bg-blue-6',
'th-dark:border-blue-7 th-dark:bg-blue-7 th-dark:text-white',
'th-highcontrast:bg-white th-highcontrast:!text-black hover:th-highcontrast:!bg-white'
);
interface PagerButtonProps {
@@ -123,7 +124,11 @@ function PagerButton({
return (
<button
type="button"
className={clsx(btnBase, active && btnActivePage)}
className={clsx(
btnBase,
active && btnActivePage,
disabled && 'cursor-not-allowed'
)}
disabled={disabled}
onClick={onClick}
title={title}
@@ -61,7 +61,7 @@ const tabItem = cva(
className: [
'bg-transparent text-gray-7 th-highcontrast:text-white th-dark:text-gray-6',
'hover:bg-graphite-50 hover:text-graphite-900',
'th-dark:hover:bg-graphite-600 th-dark:hover:text-gray-6',
'th-dark:hover:bg-graphite-600 th-dark:hover:text-gray-4',
'th-highcontrast:hover:bg-white th-highcontrast:hover:text-black',
],
},
@@ -69,9 +69,9 @@ const tabItem = cva(
variant: 'contained',
isActive: true,
className: [
'bg-graphite-50 text-graphite-900',
'th-dark:bg-graphite-600 th-dark:text-white',
'th-highcontrast:bg-white th-highcontrast:text-black',
'bg-graphite-50 text-graphite-900 hover:text-inherit focus:text-inherit',
'th-dark:bg-graphite-600 th-dark:text-white th-dark:hover:text-inherit th-dark:focus:text-inherit',
'th-highcontrast:bg-white th-highcontrast:text-black th-highcontrast:hover:text-black th-highcontrast:focus:text-black',
],
},
],
+2 -2
View File
@@ -72,11 +72,11 @@ export function HomeView() {
{process.env.PORTAINER_EDITION !== 'CE' && <BackupFailedPanel />}
{connectingToEdgeEndpoint ? (
<div className="flex flex-1 flex-col items-center justify-center">
<div className="mb-5 flex flex-1 flex-col items-center justify-center">
<EdgeLoadingSpinner />
</div>
) : (
<div className="mx-5 flex flex-col gap-6">
<div className="mx-5 mb-5 flex flex-col gap-6">
<EnvironmentHeader />
<EnvironmentList onClickBrowse={handleBrowseClick} />
</div>
@@ -26,7 +26,7 @@ export function PorAccessManagementUsersSelector({
>
Select user(s) and/or team(s)
</label>
<div className="col-sm-9 col-lg-4">
<div className="col-sm-9 col-lg-10">
<Select
isMulti
getOptionLabel={(option) => option.Name}
@@ -69,7 +69,6 @@ export function EditGroupView() {
return (
<>
<PageHeader
title={groupName}
breadcrumbs={[{ label: 'Groups', link: 'portainer.groups' }, groupName]}
/>
<div className="mx-4 space-y-4">
@@ -48,6 +48,7 @@ export function GroupHeader({
icon={Trash2}
onClick={() => onDelete?.()}
data-cy="group-header-delete"
color="dangerlight"
>
Delete
</ActionBarButton>
@@ -119,8 +119,8 @@ export function EnvironmentGroupRow({ group, tags }: Props) {
<Button
as={Link}
props={{
to: 'portainer.groups.group.access',
params: { id: group.Id },
to: 'portainer.groups.group',
params: { id: group.Id, tab: 'access' },
}}
color="link"
icon={Users}
@@ -16,7 +16,7 @@ export function ListView() {
</AddButton>
</PageHeader>
<div className="mx-5">
<div className="mx-5 mb-5">
<EnvironmentGroupsTable />
</div>
</>
@@ -28,6 +28,8 @@ interface Props extends AutomationTestingProps {
confirmRemove?: boolean;
/** When true, don't show the add/remove buttons and hide the checkbox */
readOnly?: boolean;
/** Use when the datatable isn't a standalone widget, like in a form */
noWidget?: boolean;
}
export function AssociatedEnvironmentsTable({
@@ -40,6 +42,7 @@ export function AssociatedEnvironmentsTable({
confirmRemove = true,
readOnly = false,
'data-cy': dataCy,
noWidget,
}: Props) {
const tableState = useTableStateWithoutStorage('Name');
const [selectedIds, setSelectedIds] = useState<string[]>([]);
@@ -47,6 +50,7 @@ export function AssociatedEnvironmentsTable({
return (
<Datatable<EnvironmentTableData>
noWidget={noWidget}
disableSelect={readOnly}
isLoading={isLoading}
title={title}
@@ -3,6 +3,7 @@ import { useState } from 'react';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { FormSection } from '@@/form-components/FormSection';
import { Widget, WidgetBody } from '@@/Widget';
import { EnvironmentTableData } from './types';
import { AssociatedEnvironmentsTable } from './AssociatedEnvironmentsTable';
@@ -27,16 +28,21 @@ export function FormModeEnvironmentsSelector({ selectedIds, onChange }: Props) {
return (
<FormSection title="Associate environments">
<p className="small text-muted">
Assocate environments to this group by clicking the add button below.
Associate environments to this group by clicking the add button below.
</p>
<AssociatedEnvironmentsTable
title="Associated environments"
environments={selectedEnvironments}
onRemove={handleRemove}
onOpenAddDrawer={() => setDrawerOpen(true)}
confirmRemove={false}
data-cy="group-associatedEndpoints"
/>
<Widget>
<WidgetBody className="no-padding">
<AssociatedEnvironmentsTable
noWidget
title="Associated environments"
environments={selectedEnvironments}
onRemove={handleRemove}
onOpenAddDrawer={() => setDrawerOpen(true)}
confirmRemove={false}
data-cy="group-associatedEndpoints"
/>
</WidgetBody>
</Widget>
<AddEnvironmentsDrawer
open={drawerOpen}
@@ -87,8 +87,8 @@ function manageAccess(item: AccessViewerPolicyModel, isPureAdmin: boolean) {
if (item.groupName) {
return (
<Link
to="portainer.groups.group.access"
params={{ id: item.groupId }}
to="portainer.groups.group"
params={{ id: item.groupId, tab: 'access' }}
data-cy={`manage-access-button-${item.roleName}`}
>
{manageAccessLabel}