Introduce datagrid to permission group list (#4102)
Co-authored-by: wojteknowacki <wojciech.nowacki@saleor.io>
This commit is contained in:
parent
4a0b2230ac
commit
7d48cded75
17 changed files with 319 additions and 274 deletions
5
.changeset/sharp-wombats-teach.md
Normal file
5
.changeset/sharp-wombats-teach.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-dashboard": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add datagrid feature to permission group list
|
|
@ -3,12 +3,8 @@
|
||||||
|
|
||||||
import faker from "faker";
|
import faker from "faker";
|
||||||
|
|
||||||
import {
|
import { PERMISSION_GROUP_DETAILS_SELECTORS } from "../../elements/permissionGroup/permissionGroupDetails";
|
||||||
PERMISSION_GROUP_DETAILS_SELECTORS,
|
import { PERMISSION_GROUP_LIST_SELECTORS } from "../../elements/permissionGroup/permissionGroupsList";
|
||||||
} from "../../elements/permissionGroup/permissionGroupDetails";
|
|
||||||
import {
|
|
||||||
PERMISSION_GROUP_LIST_SELECTORS,
|
|
||||||
} from "../../elements/permissionGroup/permissionGroupsList";
|
|
||||||
import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
|
import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
|
||||||
import { SHARED_ELEMENTS } from "../../elements/shared/sharedElements";
|
import { SHARED_ELEMENTS } from "../../elements/shared/sharedElements";
|
||||||
import {
|
import {
|
||||||
|
@ -21,9 +17,8 @@ import {
|
||||||
createPermissionGroup,
|
createPermissionGroup,
|
||||||
getPermissionGroup,
|
getPermissionGroup,
|
||||||
} from "../../support/api/requests/PermissionGroup.js";
|
} from "../../support/api/requests/PermissionGroup.js";
|
||||||
import {
|
import { getStaffMembersStartsWith } from "../../support/api/requests/StaffMembers";
|
||||||
getStaffMembersStartsWith,
|
import { ensureCanvasStatic } from "../../support/customCommands/sharedElementsOperations/canvas";
|
||||||
} from "../../support/api/requests/StaffMembers";
|
|
||||||
|
|
||||||
describe("Permissions groups", () => {
|
describe("Permissions groups", () => {
|
||||||
const startsWith = "CyPermissions-" + Date.now();
|
const startsWith = "CyPermissions-" + Date.now();
|
||||||
|
@ -69,37 +64,31 @@ describe("Permissions groups", () => {
|
||||||
() => {
|
() => {
|
||||||
const permissionName = `A-${startsWith}${faker.datatype.number()}`;
|
const permissionName = `A-${startsWith}${faker.datatype.number()}`;
|
||||||
let staffMember;
|
let staffMember;
|
||||||
|
cy.addAliasToGraphRequest("PermissionGroupDelete");
|
||||||
getStaffMembersStartsWith(TEST_ADMIN_USER.email)
|
getStaffMembersStartsWith(TEST_ADMIN_USER.email)
|
||||||
.its("body.data.staffUsers.edges")
|
.its("body.data.staffUsers.edges")
|
||||||
.then(staffMemberResp => {
|
.then(staffMemberResp => {
|
||||||
staffMember = staffMemberResp[0].node;
|
staffMember = staffMemberResp[0].node;
|
||||||
|
|
||||||
createPermissionGroup({
|
createPermissionGroup({
|
||||||
name: permissionName,
|
name: permissionName,
|
||||||
userIdsArray: `["${staffMember.id}"]`,
|
userIdsArray: `["${staffMember.id}"]`,
|
||||||
permissionsArray: permissionManageProducts,
|
permissionsArray: permissionManageProducts,
|
||||||
|
}).then(createPermissionGroupResponse => {
|
||||||
|
cy.visit(
|
||||||
|
urlList.permissionsGroups +
|
||||||
|
createPermissionGroupResponse.group.id,
|
||||||
|
);
|
||||||
|
cy.contains(SHARED_ELEMENTS.header, permissionName);
|
||||||
|
cy.get(BUTTON_SELECTORS.deleteButton).click();
|
||||||
|
cy.clickSubmitButton().waitForRequestAndCheckIfNoErrors(
|
||||||
|
"@PermissionGroupDelete",
|
||||||
|
);
|
||||||
|
ensureCanvasStatic(SHARED_ELEMENTS.dataGridTable);
|
||||||
|
cy.get(SHARED_ELEMENTS.dataGridTable).should(
|
||||||
|
"not.contain.text",
|
||||||
|
permissionName,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
cy.visit(urlList.permissionsGroups);
|
|
||||||
cy.contains(
|
|
||||||
PERMISSION_GROUP_LIST_SELECTORS.permissionGroupRow,
|
|
||||||
permissionName,
|
|
||||||
)
|
|
||||||
.should("be.visible")
|
|
||||||
.find(BUTTON_SELECTORS.deleteIcon)
|
|
||||||
.click()
|
|
||||||
.get(BUTTON_SELECTORS.submit)
|
|
||||||
.click();
|
|
||||||
cy.contains(
|
|
||||||
PERMISSION_GROUP_LIST_SELECTORS.permissionGroupRow,
|
|
||||||
permissionName,
|
|
||||||
)
|
|
||||||
.should("not.exist")
|
|
||||||
.visit(staffMemberDetailsUrl(staffMember.id))
|
|
||||||
.get(SHARED_ELEMENTS.header)
|
|
||||||
.should("be.visible")
|
|
||||||
.contains(permissionName)
|
|
||||||
.should("not.exist");
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -7157,6 +7157,9 @@
|
||||||
"context": "label",
|
"context": "label",
|
||||||
"string": "External app"
|
"string": "External app"
|
||||||
},
|
},
|
||||||
|
"nBzIBG": {
|
||||||
|
"string": "Delete permission group"
|
||||||
|
},
|
||||||
"nEWp+k": {
|
"nEWp+k": {
|
||||||
"context": "quantity of ordered products",
|
"context": "quantity of ordered products",
|
||||||
"string": "Quantity"
|
"string": "Quantity"
|
||||||
|
|
|
@ -130,6 +130,7 @@ export const defaultListSettings: AppListViewSettings = {
|
||||||
},
|
},
|
||||||
[ListViews.PERMISSION_GROUP_LIST]: {
|
[ListViews.PERMISSION_GROUP_LIST]: {
|
||||||
rowNumber: PAGINATE_BY,
|
rowNumber: PAGINATE_BY,
|
||||||
|
columns: ["name", "members"],
|
||||||
},
|
},
|
||||||
[ListViews.VOUCHER_LIST]: {
|
[ListViews.VOUCHER_LIST]: {
|
||||||
rowNumber: PAGINATE_BY,
|
rowNumber: PAGINATE_BY,
|
||||||
|
|
|
@ -22,6 +22,7 @@ const props: PermissonGroupDetailsPageProps = {
|
||||||
members: users,
|
members: users,
|
||||||
onAssign: () => undefined,
|
onAssign: () => undefined,
|
||||||
onSort: () => undefined,
|
onSort: () => undefined,
|
||||||
|
onDelete: () => undefined,
|
||||||
onSubmit: () => new Promise(resolve => resolve(undefined)),
|
onSubmit: () => new Promise(resolve => resolve(undefined)),
|
||||||
onUnassign: () => undefined,
|
onUnassign: () => undefined,
|
||||||
permissionGroup,
|
permissionGroup,
|
||||||
|
|
|
@ -66,6 +66,7 @@ export interface PermissonGroupDetailsPageProps
|
||||||
permissionsExceeded: boolean;
|
permissionsExceeded: boolean;
|
||||||
saveButtonBarState: "loading" | "success" | "error" | "default";
|
saveButtonBarState: "loading" | "success" | "error" | "default";
|
||||||
onAssign: () => void;
|
onAssign: () => void;
|
||||||
|
onDelete: () => void;
|
||||||
onUnassign: (ids: string[]) => void;
|
onUnassign: (ids: string[]) => void;
|
||||||
onSubmit: (data: PermissionGroupDetailsPageFormData) => SubmitPromise;
|
onSubmit: (data: PermissionGroupDetailsPageFormData) => SubmitPromise;
|
||||||
}
|
}
|
||||||
|
@ -77,6 +78,7 @@ export const PermissionGroupDetailsPage: React.FC<
|
||||||
errors,
|
errors,
|
||||||
members,
|
members,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
onDelete,
|
||||||
permissionGroup,
|
permissionGroup,
|
||||||
permissions,
|
permissions,
|
||||||
permissionsExceeded,
|
permissionsExceeded,
|
||||||
|
@ -190,6 +192,7 @@ export const PermissionGroupDetailsPage: React.FC<
|
||||||
<div>
|
<div>
|
||||||
<Savebar
|
<Savebar
|
||||||
onCancel={() => navigate(permissionGroupListUrl())}
|
onCancel={() => navigate(permissionGroupListUrl())}
|
||||||
|
onDelete={onDelete}
|
||||||
onSubmit={submit}
|
onSubmit={submit}
|
||||||
state={saveButtonBarState}
|
state={saveButtonBarState}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -1,176 +0,0 @@
|
||||||
// @ts-strict-ignore
|
|
||||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
|
||||||
import Skeleton from "@dashboard/components/Skeleton";
|
|
||||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
|
||||||
import TableCellHeader from "@dashboard/components/TableCellHeader";
|
|
||||||
import { TablePaginationWithContext } from "@dashboard/components/TablePagination";
|
|
||||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
|
||||||
import { PermissionGroupFragment } from "@dashboard/graphql";
|
|
||||||
import { maybe, renderCollection, stopPropagation } from "@dashboard/misc";
|
|
||||||
import {
|
|
||||||
permissionGroupDetailsUrl,
|
|
||||||
PermissionGroupListUrlSortField,
|
|
||||||
} from "@dashboard/permissionGroups/urls";
|
|
||||||
import { ListProps, SortPage } from "@dashboard/types";
|
|
||||||
import { getArrowDirection } from "@dashboard/utils/sort";
|
|
||||||
import {
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableFooter,
|
|
||||||
TableHead,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import { DeleteIcon, IconButton, makeStyles } from "@saleor/macaw-ui";
|
|
||||||
import React from "react";
|
|
||||||
import { FormattedMessage } from "react-intl";
|
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
[theme.breakpoints.up("lg")]: {
|
|
||||||
colActions: {
|
|
||||||
width: 180,
|
|
||||||
},
|
|
||||||
colMembers: {
|
|
||||||
width: 180,
|
|
||||||
},
|
|
||||||
colName: {
|
|
||||||
width: "auto",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
colActions: {
|
|
||||||
paddingRight: theme.spacing(),
|
|
||||||
textAlign: "right",
|
|
||||||
},
|
|
||||||
colActionsHeader: {
|
|
||||||
textAlign: "right",
|
|
||||||
},
|
|
||||||
colMembers: {
|
|
||||||
textAlign: "right",
|
|
||||||
},
|
|
||||||
colName: {
|
|
||||||
paddingLeft: 0,
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
cursor: "pointer",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{ name: "PermissionGroupList" },
|
|
||||||
);
|
|
||||||
const numberOfColumns = 3;
|
|
||||||
|
|
||||||
interface PermissionGroupListProps
|
|
||||||
extends ListProps,
|
|
||||||
SortPage<PermissionGroupListUrlSortField> {
|
|
||||||
permissionGroups: PermissionGroupFragment[];
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PermissionGroupList: React.FC<PermissionGroupListProps> = props => {
|
|
||||||
const { disabled, permissionGroups, onDelete, onSort, sort } = props;
|
|
||||||
const classes = useStyles(props);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ResponsiveTable>
|
|
||||||
<TableHead>
|
|
||||||
<TableRowLink>
|
|
||||||
<TableCellHeader
|
|
||||||
direction={
|
|
||||||
sort.sort === PermissionGroupListUrlSortField.name
|
|
||||||
? getArrowDirection(sort.asc)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
arrowPosition="right"
|
|
||||||
onClick={() => onSort(PermissionGroupListUrlSortField.name)}
|
|
||||||
className={classes.colName}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id="szXISP"
|
|
||||||
defaultMessage="Permission Group Name"
|
|
||||||
description="permission group name"
|
|
||||||
/>
|
|
||||||
</TableCellHeader>
|
|
||||||
<TableCellHeader className={classes.colMembers} textAlign="right">
|
|
||||||
<FormattedMessage id="+a+2ug" defaultMessage="Members" />
|
|
||||||
</TableCellHeader>
|
|
||||||
<TableCell className={classes.colActionsHeader}>
|
|
||||||
<FormattedMessage id="wL7VAE" defaultMessage="Actions" />
|
|
||||||
</TableCell>
|
|
||||||
</TableRowLink>
|
|
||||||
</TableHead>
|
|
||||||
<TableFooter>
|
|
||||||
<TableRowLink>
|
|
||||||
<TablePaginationWithContext
|
|
||||||
colSpan={numberOfColumns}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</TableRowLink>
|
|
||||||
</TableFooter>
|
|
||||||
<TableBody>
|
|
||||||
{renderCollection(
|
|
||||||
permissionGroups,
|
|
||||||
permissionGroup => (
|
|
||||||
<TableRowLink
|
|
||||||
className={!!permissionGroup ? classes.link : undefined}
|
|
||||||
hover={!!permissionGroup}
|
|
||||||
key={permissionGroup ? permissionGroup.id : "skeleton"}
|
|
||||||
href={
|
|
||||||
permissionGroup && permissionGroupDetailsUrl(permissionGroup.id)
|
|
||||||
}
|
|
||||||
data-test-id={"id-" + maybe(() => permissionGroup.id)}
|
|
||||||
>
|
|
||||||
<TableCell className={classes.colName}>
|
|
||||||
{permissionGroup ? (
|
|
||||||
<span data-test-id="name">{permissionGroup.name}</span>
|
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colMembers}>
|
|
||||||
{permissionGroup ? (
|
|
||||||
<span data-test-id="members">
|
|
||||||
{permissionGroup.users.length}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classes.colActions}>
|
|
||||||
{permissionGroup ? (
|
|
||||||
<>
|
|
||||||
{permissionGroup.userCanManage && (
|
|
||||||
<TableButtonWrapper>
|
|
||||||
<IconButton
|
|
||||||
variant="secondary"
|
|
||||||
data-test-id="delete-icon"
|
|
||||||
color="primary"
|
|
||||||
onClick={stopPropagation(() =>
|
|
||||||
onDelete(permissionGroup.id),
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</TableButtonWrapper>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
</TableRowLink>
|
|
||||||
),
|
|
||||||
() => (
|
|
||||||
<TableRowLink>
|
|
||||||
<TableCell colSpan={numberOfColumns}>
|
|
||||||
<FormattedMessage
|
|
||||||
id="CXn88q"
|
|
||||||
defaultMessage="No permission groups found"
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
</TableRowLink>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</ResponsiveTable>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
PermissionGroupList.displayName = "PermissionGroupList";
|
|
||||||
export default PermissionGroupList;
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default } from "./PermissionGroupList";
|
|
||||||
export * from "./PermissionGroupList";
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
import { useColumns } from "@dashboard/components/Datagrid/ColumnPicker/useColumns";
|
||||||
|
import Datagrid from "@dashboard/components/Datagrid/Datagrid";
|
||||||
|
import {
|
||||||
|
DatagridChangeStateContext,
|
||||||
|
useDatagridChangeState,
|
||||||
|
} from "@dashboard/components/Datagrid/hooks/useDatagridChange";
|
||||||
|
import { useEmptyColumn } from "@dashboard/components/Datagrid/hooks/useEmptyColumn";
|
||||||
|
import { TablePaginationWithContext } from "@dashboard/components/TablePagination";
|
||||||
|
import { PermissionGroupFragment } from "@dashboard/graphql";
|
||||||
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
|
import {
|
||||||
|
permissionGroupDetailsUrl,
|
||||||
|
PermissionGroupListUrlSortField,
|
||||||
|
} from "@dashboard/permissionGroups/urls";
|
||||||
|
import { canBeSorted } from "@dashboard/permissionGroups/views/PermissionGroupList/sort";
|
||||||
|
import { ListProps, SortPage } from "@dashboard/types";
|
||||||
|
import { Item } from "@glideapps/glide-data-grid";
|
||||||
|
import { Box } from "@saleor/macaw-ui/next";
|
||||||
|
import React, { useCallback, useMemo } from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createGetCellContent,
|
||||||
|
permissionGroupsListStaticColumnsAdapter,
|
||||||
|
} from "./datagrid";
|
||||||
|
import { messages } from "./messages";
|
||||||
|
|
||||||
|
interface PermissionGroupListDatagridProps
|
||||||
|
extends ListProps,
|
||||||
|
SortPage<PermissionGroupListUrlSortField> {
|
||||||
|
permissionGroups: PermissionGroupFragment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PermissionGroupListDatagrid = ({
|
||||||
|
disabled,
|
||||||
|
onSort,
|
||||||
|
permissionGroups,
|
||||||
|
sort,
|
||||||
|
settings,
|
||||||
|
onUpdateListSettings,
|
||||||
|
}: PermissionGroupListDatagridProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const datagridState = useDatagridChangeState();
|
||||||
|
const navigate = useNavigator();
|
||||||
|
|
||||||
|
const emptyColumn = useEmptyColumn();
|
||||||
|
const permissionGroupsListStaticColumns = useMemo(
|
||||||
|
() => permissionGroupsListStaticColumnsAdapter(intl, sort, emptyColumn),
|
||||||
|
[intl, sort],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onColumnChange = useCallback(
|
||||||
|
(picked: string[]) => {
|
||||||
|
if (onUpdateListSettings) {
|
||||||
|
onUpdateListSettings("columns", picked.filter(Boolean));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onUpdateListSettings],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { handlers, visibleColumns, recentlyAddedColumn } = useColumns({
|
||||||
|
selectedColumns: settings?.columns ?? [],
|
||||||
|
staticColumns: permissionGroupsListStaticColumns,
|
||||||
|
onSave: onColumnChange,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCellContent = useCallback(
|
||||||
|
createGetCellContent({
|
||||||
|
permissionGroups,
|
||||||
|
columns: visibleColumns,
|
||||||
|
}),
|
||||||
|
[permissionGroups, intl, visibleColumns],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRowClick = useCallback(
|
||||||
|
([_, row]: Item) => {
|
||||||
|
const rowData: PermissionGroupFragment = permissionGroups[row];
|
||||||
|
|
||||||
|
if (rowData) {
|
||||||
|
navigate(permissionGroupDetailsUrl(rowData.id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[permissionGroups],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRowAnchor = useCallback(
|
||||||
|
([, row]: Item) => permissionGroupDetailsUrl(permissionGroups[row].id),
|
||||||
|
[permissionGroups],
|
||||||
|
);
|
||||||
|
const handleHeaderClick = useCallback(
|
||||||
|
(col: number) => {
|
||||||
|
const columnName = visibleColumns[col]
|
||||||
|
.id as PermissionGroupListUrlSortField;
|
||||||
|
if (canBeSorted(columnName)) {
|
||||||
|
onSort(columnName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[visibleColumns, onSort],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DatagridChangeStateContext.Provider value={datagridState}>
|
||||||
|
<Datagrid
|
||||||
|
readonly
|
||||||
|
loading={disabled}
|
||||||
|
rowMarkers="none"
|
||||||
|
columnSelect="single"
|
||||||
|
hasRowHover={true}
|
||||||
|
freezeColumns={2}
|
||||||
|
onColumnMoved={handlers.onMove}
|
||||||
|
onColumnResize={handlers.onResize}
|
||||||
|
verticalBorder={col => col > 1}
|
||||||
|
rows={permissionGroups?.length ?? 0}
|
||||||
|
availableColumns={visibleColumns}
|
||||||
|
emptyText={intl.formatMessage(messages.empty)}
|
||||||
|
getCellContent={getCellContent}
|
||||||
|
getCellError={() => false}
|
||||||
|
selectionActions={() => null}
|
||||||
|
menuItems={() => []}
|
||||||
|
onRowClick={handleRowClick}
|
||||||
|
onHeaderClicked={handleHeaderClick}
|
||||||
|
rowAnchor={handleRowAnchor}
|
||||||
|
recentlyAddedColumn={recentlyAddedColumn}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box paddingX={6}>
|
||||||
|
<TablePaginationWithContext
|
||||||
|
component="div"
|
||||||
|
settings={settings}
|
||||||
|
disabled={disabled}
|
||||||
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</DatagridChangeStateContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { PLACEHOLDER } from "@dashboard/components/Datagrid/const";
|
||||||
|
import { readonlyTextCell } from "@dashboard/components/Datagrid/customCells/cells";
|
||||||
|
import { AvailableColumn } from "@dashboard/components/Datagrid/types";
|
||||||
|
import { PermissionGroupFragment } from "@dashboard/graphql";
|
||||||
|
import { PermissionGroupListUrlSortField } from "@dashboard/permissionGroups/urls";
|
||||||
|
import { Sort } from "@dashboard/types";
|
||||||
|
import { getColumnSortDirectionIcon } from "@dashboard/utils/columns/getColumnSortDirectionIcon";
|
||||||
|
import { GridCell, Item } from "@glideapps/glide-data-grid";
|
||||||
|
import { IntlShape } from "react-intl";
|
||||||
|
|
||||||
|
import { columnsMessages } from "./messages";
|
||||||
|
|
||||||
|
export const permissionGroupsListStaticColumnsAdapter = (
|
||||||
|
intl: IntlShape,
|
||||||
|
sort: Sort<PermissionGroupListUrlSortField>,
|
||||||
|
emptyColumn: AvailableColumn,
|
||||||
|
) =>
|
||||||
|
[
|
||||||
|
emptyColumn,
|
||||||
|
{
|
||||||
|
id: "name",
|
||||||
|
title: intl.formatMessage(columnsMessages.name),
|
||||||
|
width: 450,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "members",
|
||||||
|
title: intl.formatMessage(columnsMessages.members),
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
].map(column => ({
|
||||||
|
...column,
|
||||||
|
icon: getColumnSortDirectionIcon(sort, column.id),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const createGetCellContent =
|
||||||
|
({
|
||||||
|
permissionGroups,
|
||||||
|
columns,
|
||||||
|
}: {
|
||||||
|
permissionGroups: PermissionGroupFragment[];
|
||||||
|
columns: AvailableColumn[];
|
||||||
|
}) =>
|
||||||
|
([column, row]: Item): GridCell => {
|
||||||
|
const rowData: PermissionGroupFragment | undefined = permissionGroups[row];
|
||||||
|
const columnId = columns[column]?.id;
|
||||||
|
|
||||||
|
if (!columnId || !rowData) {
|
||||||
|
return readonlyTextCell("");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (columnId) {
|
||||||
|
case "name":
|
||||||
|
return readonlyTextCell(rowData?.name ?? PLACEHOLDER);
|
||||||
|
case "members":
|
||||||
|
return readonlyTextCell(
|
||||||
|
rowData?.users?.length?.toString() ?? PLACEHOLDER,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return readonlyTextCell("");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./PermissionGroupListDatagrid";
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { defineMessages } from "react-intl";
|
||||||
|
|
||||||
|
export const columnsMessages = defineMessages({
|
||||||
|
name: {
|
||||||
|
id: "szXISP",
|
||||||
|
defaultMessage: "Permission Group Name",
|
||||||
|
description: "permission group name",
|
||||||
|
},
|
||||||
|
members: {
|
||||||
|
id: "+a+2ug",
|
||||||
|
defaultMessage: "Members",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const messages = defineMessages({
|
||||||
|
empty: {
|
||||||
|
id: "CXn88q",
|
||||||
|
defaultMessage: "No permission groups found",
|
||||||
|
},
|
||||||
|
deletePermissionGroup: {
|
||||||
|
defaultMessage: "Delete permission group",
|
||||||
|
id: "nBzIBG",
|
||||||
|
},
|
||||||
|
});
|
|
@ -19,7 +19,6 @@ const props: PermissionGroupListPageProps = {
|
||||||
...pageListProps.default,
|
...pageListProps.default,
|
||||||
...sortPageProps,
|
...sortPageProps,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
onDelete: () => undefined,
|
|
||||||
sort: {
|
sort: {
|
||||||
...sortPageProps.sort,
|
...sortPageProps.sort,
|
||||||
sort: PermissionGroupListUrlSortField.name,
|
sort: PermissionGroupListUrlSortField.name,
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||||
import { Button } from "@dashboard/components/Button";
|
|
||||||
import { ListPageLayout } from "@dashboard/components/Layouts";
|
import { ListPageLayout } from "@dashboard/components/Layouts";
|
||||||
import { configurationMenuUrl } from "@dashboard/configuration";
|
import { configurationMenuUrl } from "@dashboard/configuration";
|
||||||
import { PermissionGroupFragment } from "@dashboard/graphql";
|
import { PermissionGroupFragment } from "@dashboard/graphql";
|
||||||
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
import { sectionNames } from "@dashboard/intl";
|
import { sectionNames } from "@dashboard/intl";
|
||||||
import { Card } from "@material-ui/core";
|
import { Card } from "@material-ui/core";
|
||||||
|
import { Button } from "@saleor/macaw-ui/next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -13,29 +14,30 @@ import {
|
||||||
permissionGroupAddUrl,
|
permissionGroupAddUrl,
|
||||||
PermissionGroupListUrlSortField,
|
PermissionGroupListUrlSortField,
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
import PermissionGroupList from "../PermissionGroupList";
|
import { PermissionGroupListDatagrid } from "../PermissionGroupListDatagrid";
|
||||||
|
|
||||||
export interface PermissionGroupListPageProps
|
export interface PermissionGroupListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
SortPage<PermissionGroupListUrlSortField> {
|
SortPage<PermissionGroupListUrlSortField> {
|
||||||
permissionGroups: PermissionGroupFragment[];
|
permissionGroups: PermissionGroupFragment[];
|
||||||
onDelete: (id: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PermissionGroupListPage: React.FC<
|
const PermissionGroupListPage: React.FC<
|
||||||
PermissionGroupListPageProps
|
PermissionGroupListPageProps
|
||||||
> = listProps => {
|
> = listProps => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const navigate = useNavigator();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListPageLayout>
|
<ListPageLayout>
|
||||||
<TopNav
|
<TopNav
|
||||||
|
withoutBorder
|
||||||
href={configurationMenuUrl}
|
href={configurationMenuUrl}
|
||||||
title={intl.formatMessage(sectionNames.permissionGroups)}
|
title={intl.formatMessage(sectionNames.permissionGroups)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
href={permissionGroupAddUrl}
|
onClick={() => navigate(permissionGroupAddUrl)}
|
||||||
data-test-id="create-permission-group"
|
data-test-id="create-permission-group"
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -46,7 +48,7 @@ const PermissionGroupListPage: React.FC<
|
||||||
</Button>
|
</Button>
|
||||||
</TopNav>
|
</TopNav>
|
||||||
<Card>
|
<Card>
|
||||||
<PermissionGroupList {...listProps} />
|
<PermissionGroupListDatagrid {...listProps} />
|
||||||
</Card>
|
</Card>
|
||||||
</ListPageLayout>
|
</ListPageLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { Button } from "@dashboard/components/Button";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config";
|
||||||
import {
|
import {
|
||||||
PermissionGroupDetailsQuery,
|
PermissionGroupDetailsQuery,
|
||||||
|
PermissionGroupErrorFragment,
|
||||||
|
usePermissionGroupDeleteMutation,
|
||||||
usePermissionGroupDetailsQuery,
|
usePermissionGroupDetailsQuery,
|
||||||
usePermissionGroupUpdateMutation,
|
usePermissionGroupUpdateMutation,
|
||||||
} from "@dashboard/graphql";
|
} from "@dashboard/graphql";
|
||||||
|
@ -13,8 +15,9 @@ import useNotifier from "@dashboard/hooks/useNotifier";
|
||||||
import useShop from "@dashboard/hooks/useShop";
|
import useShop from "@dashboard/hooks/useShop";
|
||||||
import useStateFromProps from "@dashboard/hooks/useStateFromProps";
|
import useStateFromProps from "@dashboard/hooks/useStateFromProps";
|
||||||
import { commonMessages } from "@dashboard/intl";
|
import { commonMessages } from "@dashboard/intl";
|
||||||
import { extractMutationErrors } from "@dashboard/misc";
|
import { extractMutationErrors, getStringOrPlaceholder } from "@dashboard/misc";
|
||||||
import MembersErrorDialog from "@dashboard/permissionGroups/components/MembersErrorDialog";
|
import MembersErrorDialog from "@dashboard/permissionGroups/components/MembersErrorDialog";
|
||||||
|
import PermissionGroupDeleteDialog from "@dashboard/permissionGroups/components/PermissionGroupDeleteDialog";
|
||||||
import useStaffMemberSearch from "@dashboard/searches/useStaffMemberSearch";
|
import useStaffMemberSearch from "@dashboard/searches/useStaffMemberSearch";
|
||||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||||
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
||||||
|
@ -33,6 +36,7 @@ import {
|
||||||
permissionGroupDetailsUrl,
|
permissionGroupDetailsUrl,
|
||||||
PermissionGroupDetailsUrlDialog,
|
PermissionGroupDetailsUrlDialog,
|
||||||
PermissionGroupDetailsUrlQueryParams,
|
PermissionGroupDetailsUrlQueryParams,
|
||||||
|
permissionGroupListUrl,
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
import {
|
import {
|
||||||
arePermissionsExceeded,
|
arePermissionsExceeded,
|
||||||
|
@ -127,6 +131,27 @@ export const PermissionGroupDetails: React.FC<PermissionGroupDetailsProps> = ({
|
||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [deleteError, setDeleteError] =
|
||||||
|
React.useState<PermissionGroupErrorFragment>();
|
||||||
|
|
||||||
|
const [permissionGroupDelete, permissionGroupDeleteOps] =
|
||||||
|
usePermissionGroupDeleteMutation({
|
||||||
|
onCompleted: data => {
|
||||||
|
if (data?.permissionGroupDelete?.errors?.length === 0) {
|
||||||
|
notify({
|
||||||
|
status: "success",
|
||||||
|
text: intl.formatMessage({
|
||||||
|
id: "DovGIa",
|
||||||
|
defaultMessage: "Permission Group Deleted",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
navigate(permissionGroupListUrl());
|
||||||
|
} else {
|
||||||
|
setDeleteError(data?.permissionGroupDelete?.errors?.[0]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const unassignMembers = () => {
|
const unassignMembers = () => {
|
||||||
setMembersList(
|
setMembersList(
|
||||||
membersList?.filter(m => !listElements.includes(m.id)) ?? [],
|
membersList?.filter(m => !listElements.includes(m.id)) ?? [],
|
||||||
|
@ -193,6 +218,7 @@ export const PermissionGroupDetails: React.FC<PermissionGroupDetailsProps> = ({
|
||||||
members={membersList}
|
members={membersList}
|
||||||
onAssign={() => openModal("assign")}
|
onAssign={() => openModal("assign")}
|
||||||
onUnassign={ids => openModal("unassign", { ids })}
|
onUnassign={ids => openModal("unassign", { ids })}
|
||||||
|
onDelete={() => openModal("remove")}
|
||||||
errors={
|
errors={
|
||||||
permissionGroupUpdateResult?.data?.permissionGroupUpdate?.errors ?? []
|
permissionGroupUpdateResult?.data?.permissionGroupUpdate?.errors ?? []
|
||||||
}
|
}
|
||||||
|
@ -219,6 +245,7 @@ export const PermissionGroupDetails: React.FC<PermissionGroupDetailsProps> = ({
|
||||||
}
|
}
|
||||||
onSort={handleSort}
|
onSort={handleSort}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AssignMembersDialog
|
<AssignMembersDialog
|
||||||
loading={searchResult.loading}
|
loading={searchResult.loading}
|
||||||
staffMembers={mapEdgesToItems(searchResult?.data?.search) ?? []}
|
staffMembers={mapEdgesToItems(searchResult?.data?.search) ?? []}
|
||||||
|
@ -235,6 +262,7 @@ export const PermissionGroupDetails: React.FC<PermissionGroupDetailsProps> = ({
|
||||||
closeModal();
|
closeModal();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UnassignMembersDialog
|
<UnassignMembersDialog
|
||||||
onConfirm={unassignMembers}
|
onConfirm={unassignMembers}
|
||||||
confirmButtonState={permissionGroupUpdateResult.status}
|
confirmButtonState={permissionGroupUpdateResult.status}
|
||||||
|
@ -242,12 +270,28 @@ export const PermissionGroupDetails: React.FC<PermissionGroupDetailsProps> = ({
|
||||||
open={params.action === "unassign"}
|
open={params.action === "unassign"}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MembersErrorDialog
|
<MembersErrorDialog
|
||||||
onConfirm={closeModal}
|
onConfirm={closeModal}
|
||||||
confirmButtonState={permissionGroupUpdateResult.status}
|
confirmButtonState={permissionGroupUpdateResult.status}
|
||||||
open={params.action === "unassignError"}
|
open={params.action === "unassignError"}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PermissionGroupDeleteDialog
|
||||||
|
onConfirm={() =>
|
||||||
|
permissionGroupDelete({
|
||||||
|
variables: {
|
||||||
|
id: data?.permissionGroup?.id ?? "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
error={deleteError}
|
||||||
|
name={getStringOrPlaceholder(data?.permissionGroup?.name)}
|
||||||
|
confirmButtonState={permissionGroupDeleteOps.status}
|
||||||
|
open={params.action === "remove"}
|
||||||
|
onClose={closeModal}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,30 +1,20 @@
|
||||||
import {
|
import { usePermissionGroupListQuery } from "@dashboard/graphql";
|
||||||
PermissionGroupErrorFragment,
|
|
||||||
usePermissionGroupDeleteMutation,
|
|
||||||
usePermissionGroupListQuery,
|
|
||||||
} from "@dashboard/graphql";
|
|
||||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
|
||||||
import { usePaginationReset } from "@dashboard/hooks/usePaginationReset";
|
import { usePaginationReset } from "@dashboard/hooks/usePaginationReset";
|
||||||
import usePaginator, {
|
import usePaginator, {
|
||||||
createPaginationState,
|
createPaginationState,
|
||||||
PaginatorContext,
|
PaginatorContext,
|
||||||
} from "@dashboard/hooks/usePaginator";
|
} from "@dashboard/hooks/usePaginator";
|
||||||
import { getStringOrPlaceholder } from "@dashboard/misc";
|
|
||||||
import PermissionGroupDeleteDialog from "@dashboard/permissionGroups/components/PermissionGroupDeleteDialog";
|
|
||||||
import { ListViews } from "@dashboard/types";
|
import { ListViews } from "@dashboard/types";
|
||||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
|
||||||
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
||||||
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
||||||
import { getSortParams } from "@dashboard/utils/sort";
|
import { getSortParams } from "@dashboard/utils/sort";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
|
||||||
|
|
||||||
import PermissionGroupListPage from "../../components/PermissionGroupListPage";
|
import PermissionGroupListPage from "../../components/PermissionGroupListPage";
|
||||||
import {
|
import {
|
||||||
permissionGroupListUrl,
|
permissionGroupListUrl,
|
||||||
PermissionGroupListUrlDialog,
|
|
||||||
PermissionGroupListUrlQueryParams,
|
PermissionGroupListUrlQueryParams,
|
||||||
} from "../../urls";
|
} from "../../urls";
|
||||||
import { getSortQueryVariables } from "./sort";
|
import { getSortQueryVariables } from "./sort";
|
||||||
|
@ -37,10 +27,8 @@ export const PermissionGroupList: React.FC<PermissionGroupListProps> = ({
|
||||||
params,
|
params,
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
|
||||||
const intl = useIntl();
|
|
||||||
const { updateListSettings, settings } = useListSettings(
|
const { updateListSettings, settings } = useListSettings(
|
||||||
ListViews.STAFF_MEMBERS_LIST,
|
ListViews.PERMISSION_GROUP_LIST,
|
||||||
);
|
);
|
||||||
|
|
||||||
usePaginationReset(permissionGroupListUrl, params, settings.rowNumber);
|
usePaginationReset(permissionGroupListUrl, params, settings.rowNumber);
|
||||||
|
@ -53,7 +41,7 @@ export const PermissionGroupList: React.FC<PermissionGroupListProps> = ({
|
||||||
}),
|
}),
|
||||||
[params, settings.rowNumber],
|
[params, settings.rowNumber],
|
||||||
);
|
);
|
||||||
const { data, loading, refetch } = usePermissionGroupListQuery({
|
const { data, loading } = usePermissionGroupListQuery({
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: queryVariables,
|
variables: queryVariables,
|
||||||
});
|
});
|
||||||
|
@ -70,33 +58,7 @@ export const PermissionGroupList: React.FC<PermissionGroupListProps> = ({
|
||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
|
||||||
PermissionGroupListUrlDialog,
|
|
||||||
PermissionGroupListUrlQueryParams
|
|
||||||
>(navigate, permissionGroupListUrl, params);
|
|
||||||
|
|
||||||
const permissionGroups = mapEdgesToItems(data?.permissionGroups) ?? [];
|
const permissionGroups = mapEdgesToItems(data?.permissionGroups) ?? [];
|
||||||
const [deleteError, setDeleteError] =
|
|
||||||
React.useState<PermissionGroupErrorFragment>();
|
|
||||||
|
|
||||||
const [permissionGroupDelete] = usePermissionGroupDeleteMutation({
|
|
||||||
onCompleted: data => {
|
|
||||||
if (data?.permissionGroupDelete?.errors?.length === 0) {
|
|
||||||
notify({
|
|
||||||
status: "success",
|
|
||||||
text: intl.formatMessage({
|
|
||||||
id: "DovGIa",
|
|
||||||
defaultMessage: "Permission Group Deleted",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
setDeleteError(undefined);
|
|
||||||
closeModal();
|
|
||||||
} else {
|
|
||||||
setDeleteError(data?.permissionGroupDelete?.errors?.[0]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaginatorContext.Provider value={paginationValues}>
|
<PaginatorContext.Provider value={paginationValues}>
|
||||||
|
@ -105,26 +67,9 @@ export const PermissionGroupList: React.FC<PermissionGroupListProps> = ({
|
||||||
settings={settings}
|
settings={settings}
|
||||||
sort={getSortParams(params)}
|
sort={getSortParams(params)}
|
||||||
permissionGroups={permissionGroups}
|
permissionGroups={permissionGroups}
|
||||||
onDelete={id => openModal("remove", { id })}
|
|
||||||
onUpdateListSettings={updateListSettings}
|
onUpdateListSettings={updateListSettings}
|
||||||
onSort={handleSort}
|
onSort={handleSort}
|
||||||
/>
|
/>
|
||||||
<PermissionGroupDeleteDialog
|
|
||||||
onConfirm={() =>
|
|
||||||
permissionGroupDelete({
|
|
||||||
variables: {
|
|
||||||
id: params?.id ?? "",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
error={deleteError}
|
|
||||||
name={getStringOrPlaceholder(
|
|
||||||
permissionGroups?.find(group => group.id === params.id)?.name,
|
|
||||||
)}
|
|
||||||
confirmButtonState={"default"}
|
|
||||||
open={params.action === "remove"}
|
|
||||||
onClose={closeModal}
|
|
||||||
/>
|
|
||||||
</PaginatorContext.Provider>
|
</PaginatorContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,3 +15,12 @@ export function getSortQueryField(
|
||||||
|
|
||||||
export const getSortQueryVariables =
|
export const getSortQueryVariables =
|
||||||
createGetSortQueryVariables(getSortQueryField);
|
createGetSortQueryVariables(getSortQueryField);
|
||||||
|
|
||||||
|
export function canBeSorted(sort: PermissionGroupListUrlSortField) {
|
||||||
|
switch (sort) {
|
||||||
|
case PermissionGroupListUrlSortField.name:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue