diff --git a/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx b/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx index 2e6e258159..cda9c40efa 100644 --- a/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx +++ b/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx @@ -1,10 +1,12 @@ import { ApiEndpoints } from '../../../../enums/ApiEndpoints'; +import { ModelType } from '../../../../enums/ModelType'; import { TemplateTable } from '../../../../tables/settings/TemplateTable'; export default function LabelTemplatePanel() { return ( + + <Trans>Groups</Trans> diff --git a/src/frontend/src/tables/settings/GroupTable.tsx b/src/frontend/src/tables/settings/GroupTable.tsx index 429e26e80f..c9d97637aa 100644 --- a/src/frontend/src/tables/settings/GroupTable.tsx +++ b/src/frontend/src/tables/settings/GroupTable.tsx @@ -25,6 +25,7 @@ import { import { useInstance } from '../../hooks/UseInstance'; import { useTable } from '../../hooks/UseTable'; import { apiUrl } from '../../states/ApiState'; +import { useUserState } from '../../states/UserState'; import { TableColumn } from '../Column'; import { InvenTreeTable } from '../InvenTreeTable'; import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions'; @@ -127,10 +128,15 @@ export function GroupDrawer({ export function GroupTable() { const table = useTable('groups'); const navigate = useNavigate(); + const user = useUserState(); const openDetailDrawer = useCallback( - (pk: number) => navigate(`group-${pk}/`), - [] + (pk: number) => { + if (user.hasChangePermission(ModelType.group)) { + navigate(`group-${pk}/`); + } + }, + [user] ); const columns: TableColumn[] = useMemo(() => { @@ -138,24 +144,30 @@ export function GroupTable() { { accessor: 'name', sortable: true, - title: t`Name` + title: t`Name`, + switchable: false } ]; }, []); - const rowActions = useCallback((record: GroupDetailI): RowAction[] => { - return [ - RowEditAction({ - onClick: () => openDetailDrawer(record.pk) - }), - RowDeleteAction({ - onClick: () => { - setSelectedGroup(record.pk); - deleteGroup.open(); - } - }) - ]; - }, []); + const rowActions = useCallback( + (record: GroupDetailI): RowAction[] => { + return [ + RowEditAction({ + onClick: () => openDetailDrawer(record.pk), + hidden: !user.hasChangePermission(ModelType.group) + }), + RowDeleteAction({ + hidden: !user.hasDeletePermission(ModelType.group), + onClick: () => { + setSelectedGroup(record.pk); + deleteGroup.open(); + } + }) + ]; + }, + [user] + ); const [selectedGroup, setSelectedGroup] = useState(-1); @@ -183,11 +195,12 @@ export function GroupTable() { key={'add-group'} onClick={() => newGroup.open()} tooltip={t`Add group`} + hidden={!user.hasAddPermission(ModelType.group)} /> ); return actions; - }, []); + }, [user]); return ( <> diff --git a/src/frontend/src/tables/settings/TemplateTable.tsx b/src/frontend/src/tables/settings/TemplateTable.tsx index 37e25ad526..c6bc59c9f7 100644 --- a/src/frontend/src/tables/settings/TemplateTable.tsx +++ b/src/frontend/src/tables/settings/TemplateTable.tsx @@ -48,6 +48,7 @@ export type TemplateI = { }; export interface TemplateProps { + modelType: ModelType; templateEndpoint: ApiEndpoints; printingEndpoint: ApiEndpoints; additionalFormFields?: ApiFormFieldSet; @@ -175,18 +176,22 @@ export function TemplateTable({ title: t`Modify`, tooltip: t`Modify template file`, icon: , - onClick: () => openDetailDrawer(record.pk) + onClick: () => openDetailDrawer(record.pk), + hidden: !user.hasChangePermission(templateProps.modelType) }, RowEditAction({ + hidden: !user.hasChangePermission(templateProps.modelType), onClick: () => { setSelectedTemplate(record.pk); editTemplate.open(); } }), RowDuplicateAction({ + hidden: true // TODO: Duplicate selected template }), RowDeleteAction({ + hidden: !user.hasDeletePermission(templateProps.modelType), onClick: () => { setSelectedTemplate(record.pk); deleteTemplate.open(); @@ -252,9 +257,10 @@ export function TemplateTable({ key="add-template" onClick={() => newTemplate.open()} tooltip={t`Add template`} + hidden={!user.hasAddPermission(templateProps.modelType)} /> ]; - }, []); + }, [user]); const modelTypeFilters = useFilters({ url: apiUrl(templateEndpoint), diff --git a/src/frontend/src/tables/settings/UserTable.tsx b/src/frontend/src/tables/settings/UserTable.tsx index a7108e64f5..f59ab0047c 100644 --- a/src/frontend/src/tables/settings/UserTable.tsx +++ b/src/frontend/src/tables/settings/UserTable.tsx @@ -19,6 +19,8 @@ import { DetailDrawerLink } from '../../components/nav/DetailDrawer'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; +import { ModelType } from '../../enums/ModelType'; +import { UserPermissions } from '../../enums/Roles'; import { useCreateApiFormModal, useDeleteApiFormModal @@ -29,6 +31,7 @@ import { apiUrl } from '../../states/ApiState'; import { useUserState } from '../../states/UserState'; import { TableColumn } from '../Column'; import { BooleanColumn } from '../ColumnRenderers'; +import { TableFilter } from '../Filter'; import { InvenTreeTable } from '../InvenTreeTable'; import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions'; import { GroupDetailI } from './GroupTable'; @@ -163,18 +166,19 @@ export function UserDrawer({ export function UserTable() { const table = useTable('users'); const navigate = useNavigate(); + const user = useUserState(); const openDetailDrawer = useCallback( - (pk: number) => navigate(`user-${pk}/`), - [] + (pk: number) => { + if (user.hasChangePermission(ModelType.user)) { + navigate(`user-${pk}/`); + } + }, + [user] ); const columns: TableColumn[] = useMemo(() => { return [ - { - accessor: 'email', - sortable: true - }, { accessor: 'username', sortable: true, @@ -188,8 +192,13 @@ export function UserTable() { accessor: 'last_name', sortable: true }, + { + accessor: 'email', + sortable: true + }, { accessor: 'groups', + title: t`Groups`, sortable: true, switchable: true, render: (record: any) => { @@ -211,19 +220,24 @@ export function UserTable() { // Row Actions const [selectedUser, setSelectedUser] = useState(-1); - const rowActions = useCallback((record: UserDetailI): RowAction[] => { - return [ - RowEditAction({ - onClick: () => openDetailDrawer(record.pk) - }), - RowDeleteAction({ - onClick: () => { - setSelectedUser(record.pk); - deleteUser.open(); - } - }) - ]; - }, []); + const rowActions = useCallback( + (record: UserDetailI): RowAction[] => { + return [ + RowEditAction({ + onClick: () => openDetailDrawer(record.pk), + hidden: !user.hasChangePermission(ModelType.user) + }), + RowDeleteAction({ + hidden: !user.hasDeletePermission(ModelType.user), + onClick: () => { + setSelectedUser(record.pk); + deleteUser.open(); + } + }) + ]; + }, + [user] + ); const deleteUser = useDeleteApiFormModal({ url: ApiEndpoints.user_list, @@ -256,10 +270,31 @@ export function UserTable() { key="add-user" onClick={newUser.open} tooltip={t`Add user`} + hidden={!user.hasAddPermission(ModelType.user)} /> ); return actions; + }, [user]); + + const tableFilters: TableFilter[] = useMemo(() => { + return [ + { + name: 'is_active', + label: t`Active`, + description: t`Show active users` + }, + { + name: 'is_staff', + label: t`Staff`, + description: t`Show staff users` + }, + { + name: 'is_superuser', + label: t`Superuser`, + description: t`Show superusers` + } + ]; }, []); return ( @@ -285,6 +320,7 @@ export function UserTable() { props={{ rowActions: rowActions, tableActions: tableActions, + tableFilters: tableFilters, onRowClick: (record) => openDetailDrawer(record.pk) }} />