Skip to content

Commit

Permalink
feat(groups) allow adding permisisons and identites when creatign a g…
Browse files Browse the repository at this point in the history
…roup. Simplify group editing

Signed-off-by: David Edler <[email protected]>
  • Loading branch information
edlerd committed Sep 12, 2024
1 parent 63378dc commit a7291ed
Show file tree
Hide file tree
Showing 26 changed files with 1,090 additions and 753 deletions.
2 changes: 1 addition & 1 deletion src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ const Navigation: FC = () => {
<SideNavigationItem key="/ui/permissions/groups">
<NavLink
to="/ui/permissions/groups"
title="LXD groups"
title="Groups"
onClick={softToggleMenu}
className="accordion-nav-secondary"
>
Expand Down
48 changes: 48 additions & 0 deletions src/components/PanelFormLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Button, Icon } from "@canonical/react-components";
import { FC } from "react";
import { pluralize } from "util/instanceBulkActions";

interface Props {
count: number;
entity: string;
icon: string;
isEditing: boolean;
isModified: boolean;
onClick: (entity: string) => void;
}

const PanelFormLink: FC<Props> = ({
count,
entity,
icon,
isEditing,
isModified,
onClick,
}) => {
return (
<Button
appearance="base"
className="panel-form-link"
onClick={() => onClick(entity)}
type="button"
>
<span className="panel-form-link__column">
<Icon name={icon} className="panel-form-link__icon" />
<span className="panel-form-link__title">
{isEditing ? "Edit " : "Add "} {pluralize(entity, 2)}
</span>
</span>
<span className="panel-form-link__column u-align--right">
{isModified && <Icon name="status-in-progress-small" />}
<span className="panel-form-link__count u-text--muted">
{count === 0
? `No ${pluralize(entity, 2)}`
: `${count} ${pluralize(entity, count)}`}
</span>
<Icon name="chevron-right" />
</span>
</Button>
);
};

export default PanelFormLink;
26 changes: 17 additions & 9 deletions src/pages/permissions/PermissionGroups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Loader from "components/Loader";
import ScrollableTable from "components/ScrollableTable";
import SelectableMainTable from "components/SelectableMainTable";
import SelectedTableNotification from "components/SelectedTableNotification";
import { FC, useEffect, useState } from "react";
import React, { FC, useEffect, useState } from "react";
import { queryKeys } from "util/queryKeys";
import useSortTableData from "util/useSortTableData";
import { getIdentityIdsForGroup } from "util/permissionIdentities";
Expand All @@ -29,7 +29,6 @@ import PermissionGroupsFilter from "./PermissionGroupsFilter";
import EditGroupIdentitiesBtn from "./actions/EditGroupIdentitiesBtn";
import EditGroupIdentitiesPanel from "./panels/EditGroupIdentitiesPanel";
import BulkDeleteGroupsBtn from "./actions/BulkDeleteGroupsBtn";
import EditGroupPermissionsPanel from "./panels/EditGroupPermissionsPanel";

const PermissionGroups: FC = () => {
const notify = useNotify();
Expand All @@ -50,6 +49,15 @@ const PermissionGroups: FC = () => {
notify.failure("Loading groups failed", error);
}

useEffect(() => {
const validSelections = selectedGroupNames.filter((name) =>
groups.some((group) => group.name === name),
);
if (validSelections.length !== selectedGroupNames.length) {
setSelectedGroupNames(validSelections);
}
}, [groups]);

useEffect(() => {
if (panelParams.group) {
setSelectedGroupNames([panelParams.group]);
Expand Down Expand Up @@ -87,6 +95,8 @@ const PermissionGroups: FC = () => {
selectedGroupNames.includes(group.name),
);

const panelGroup = groups.find((group) => group.name === panelParams.group);

const rows = filteredGroups.map((group) => {
const allIdentityIds = getIdentityIdsForGroup(group);
return {
Expand Down Expand Up @@ -283,19 +293,17 @@ const PermissionGroups: FC = () => {

{panelParams.panel === panels.createGroup && <CreateGroupPanel />}

{panelParams.panel === panels.editGroup && !!selectedGroups.length && (
<EditGroupPanel group={selectedGroups[0]} />
{panelParams.panel === panels.editGroup && panelGroup && (
<EditGroupPanel
group={panelGroup}
onClose={() => setSelectedGroupNames([])}
/>
)}

{panelParams.panel === panels.groupIdentities &&
!!selectedGroups.length && (
<EditGroupIdentitiesPanel groups={selectedGroups} />
)}

{panelParams.panel === panels.groupPermissions &&
!!selectedGroups.length && (
<EditGroupPermissionsPanel group={selectedGroups[0]} />
)}
</>
);
};
Expand Down
115 changes: 28 additions & 87 deletions src/pages/permissions/actions/GroupActions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { FC, useRef, useState } from "react";
import { ContextualMenu, Icon } from "@canonical/react-components";
import React, { FC } from "react";
import { Button, Icon, List } from "@canonical/react-components";
import { LxdGroup } from "types/permissions";
import usePanelParams from "util/usePanelParams";
import DeleteGroupModal from "./DeleteGroupModal";
import usePortal from "react-useportal";
import classnames from "classnames";

interface Props {
group: LxdGroup;
Expand All @@ -13,93 +12,35 @@ interface Props {
const GroupActions: FC<Props> = ({ group }) => {
const panelParams = usePanelParams();
const { openPortal, closePortal, isOpen, Portal } = usePortal();
const [displayAbove, setDisplayAbove] = useState(false);
const menuRef = useRef<HTMLImageElement>(null);

const adjustDisplayPosition = () => {
const menu = menuRef.current;
if (!menu) {
return;
}

const menuPosition = menu.getBoundingClientRect().top;
const showMenuAbove = menuPosition > window.innerHeight / 1.5;
setDisplayAbove(showMenuAbove);
};

return (
<>
<ContextualMenu
dropdownProps={{ "aria-label": "actions menu" }}
position="right"
dropdownClassName={classnames({ "show-menu-above": displayAbove })}
toggleClassName="u-no-margin--bottom u-no-padding--top u-no-padding--bottom"
toggleProps={{
"aria-label": "group actions menu",
}}
toggleLabel={
<img ref={menuRef} src="/ui/assets/icon/contextual-menu.svg" />
}
toggleAppearance="base"
title="Actions"
onClick={adjustDisplayPosition}
links={[
{
appearance: "base",
hasIcon: true,
onClick: () => panelParams.openEditGroup(group.name),
type: "button",
"aria-label": "Edit group details",
title: "Edit group details",
children: (
<>
<Icon name="edit" />
<span>Edit details</span>
</>
),
},
{
appearance: "base",
hasIcon: true,
onClick: () => panelParams.openGroupIdentities(group.name),
type: "button",
"aria-label": "Manage identities",
title: "Manage identities",
children: (
<>
<Icon name="user-group" />
<span>Manage identities</span>
</>
),
},
{
appearance: "base",
hasIcon: true,
onClick: () => panelParams.openGroupPermissions(group.name),
type: "button",
"aria-label": "Manage permissions",
title: "Manage permissions",
children: (
<>
<Icon name="lock-locked" />
<span>Manage permissions</span>
</>
),
},
{
appearance: "base",
hasIcon: true,
onClick: openPortal,
type: "button",
"aria-label": "Delete group",
title: "Delete group",
children: (
<>
<Icon name="delete" />
<span>Delete</span>
</>
),
},
<List
inline
className="u-no-margin--bottom actions-list"
items={[
<Button
key="edit"
appearance="base"
dense
hasIcon
onClick={() => panelParams.openEditGroup(group.name)}
type="button"
title="Edit group"
>
<Icon name="edit" />
</Button>,
<Button
key="delete"
appearance="base"
dense
hasIcon
onClick={openPortal}
type="button"
title="Delete group"
>
<Icon name="delete" />
</Button>,
]}
/>
{isOpen && (
Expand Down
34 changes: 33 additions & 1 deletion src/pages/permissions/forms/GroupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { FC, ReactNode } from "react";
import { Form, Input } from "@canonical/react-components";
import { FormikProps } from "formik/dist/types";
import AutoExpandingTextArea from "components/AutoExpandingTextArea";
import { GroupSubForm } from "pages/permissions/panels/CreateGroupPanel";
import PanelFormLink from "components/PanelFormLink";

export interface GroupFormValues {
name: string;
Expand All @@ -10,9 +12,23 @@ export interface GroupFormValues {

interface Props {
formik: FormikProps<GroupFormValues>;
setSubForm: (subForm: GroupSubForm) => void;
identityCount: number;
identityModifyCount: number;
permissionCount: number;
permissionModifyCount: number;
isEditing?: boolean;
}

const GroupForm: FC<Props> = ({ formik }) => {
const GroupForm: FC<Props> = ({
formik,
setSubForm,
identityCount,
identityModifyCount,
permissionCount,
permissionModifyCount,
isEditing = true,
}) => {
const getFormProps = (id: "name" | "description") => {
return {
id: id,
Expand Down Expand Up @@ -40,6 +56,22 @@ const GroupForm: FC<Props> = ({ formik }) => {
{...getFormProps("description")}
label="Description"
/>
<PanelFormLink
count={identityCount}
entity="identity"
icon="user-group"
isEditing={isEditing}
isModified={identityModifyCount > 0}
onClick={() => setSubForm("identity")}
/>
<PanelFormLink
count={permissionCount}
entity="permission"
icon="lock-locked"
isEditing={isEditing}
isModified={permissionModifyCount > 0}
onClick={() => setSubForm("permission")}
/>
</Form>
);
};
Expand Down
Loading

0 comments on commit a7291ed

Please sign in to comment.