mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
R: Replace AccessMode with RoleStore
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
This commit is contained in:
parent
b9ee1b9d08
commit
b260640ebf
|
@ -1,25 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface IAccessModeContext {
|
||||
accessLevel: UserLevel;
|
||||
setAccessLevel: React.Dispatch<React.SetStateAction<UserLevel>>;
|
||||
}
|
||||
|
||||
const AccessContext = createContext<IAccessModeContext | null>(null);
|
||||
export const useAccessMode = () => {
|
||||
const context = useContext(AccessContext);
|
||||
if (!context) {
|
||||
throw new Error(contextOutsideScope('useAccessMode', 'AccessModeState'));
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export const AccessModeState = ({ children }: React.PropsWithChildren) => {
|
||||
const [accessLevel, setAccessLevel] = useState<UserLevel>(UserLevel.READER);
|
||||
return <AccessContext value={{ accessLevel, setAccessLevel }}>{children}</AccessContext>;
|
||||
};
|
|
@ -103,7 +103,7 @@ export interface ITargetUsers {
|
|||
/**
|
||||
* Represents user access mode.
|
||||
*/
|
||||
export enum UserLevel {
|
||||
export enum UserRole {
|
||||
READER = 0,
|
||||
EDITOR,
|
||||
OWNER,
|
||||
|
|
|
@ -19,13 +19,13 @@ import Button from '@/components/ui/Button';
|
|||
import Divider from '@/components/ui/Divider';
|
||||
import Dropdown from '@/components/ui/Dropdown';
|
||||
import DropdownButton from '@/components/ui/DropdownButton';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useOSS } from '@/context/OssContext';
|
||||
import useDropdown from '@/hooks/useDropdown';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { describeAccessMode, labelAccessMode } from '@/utils/labels';
|
||||
import { UserRole } from '@/models/user';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { describeAccessMode as describeUserRole, labelAccessMode as labelUserRole } from '@/utils/labels';
|
||||
|
||||
import { useOssEdit } from './OssEditContext';
|
||||
|
||||
|
@ -39,7 +39,8 @@ function MenuOssTabs({ onDestroy }: MenuOssTabsProps) {
|
|||
const { user } = useAuth();
|
||||
const model = useOSS();
|
||||
|
||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||
const role = useRoleStore(state => state.role);
|
||||
const setRole = useRoleStore(state => state.setRole);
|
||||
|
||||
const schemaMenu = useDropdown();
|
||||
const editMenu = useDropdown();
|
||||
|
@ -55,9 +56,9 @@ function MenuOssTabs({ onDestroy }: MenuOssTabsProps) {
|
|||
controller.share();
|
||||
}
|
||||
|
||||
function handleChangeMode(newMode: UserLevel) {
|
||||
function handleChangeRole(newMode: UserRole) {
|
||||
accessMenu.hide();
|
||||
setAccessLevel(newMode);
|
||||
setRole(newMode);
|
||||
}
|
||||
|
||||
function handleCreateNew() {
|
||||
|
@ -97,7 +98,7 @@ function MenuOssTabs({ onDestroy }: MenuOssTabsProps) {
|
|||
<DropdownButton
|
||||
text='Удалить схему'
|
||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||
disabled={controller.isProcessing || accessLevel < UserLevel.OWNER}
|
||||
disabled={controller.isProcessing || role < UserRole.OWNER}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
) : null}
|
||||
|
@ -151,15 +152,15 @@ function MenuOssTabs({ onDestroy }: MenuOssTabsProps) {
|
|||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title={`Режим ${labelAccessMode(accessLevel)}`}
|
||||
title={`Режим ${labelUserRole(role)}`}
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={
|
||||
accessLevel === UserLevel.ADMIN ? (
|
||||
role === UserRole.ADMIN ? (
|
||||
<IconAdmin size='1.25rem' className='icon-primary' />
|
||||
) : accessLevel === UserLevel.OWNER ? (
|
||||
) : role === UserRole.OWNER ? (
|
||||
<IconOwner size='1.25rem' className='icon-primary' />
|
||||
) : accessLevel === UserLevel.EDITOR ? (
|
||||
) : role === UserRole.EDITOR ? (
|
||||
<IconEditor size='1.25rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconReader size='1.25rem' className='icon-primary' />
|
||||
|
@ -169,31 +170,31 @@ function MenuOssTabs({ onDestroy }: MenuOssTabsProps) {
|
|||
/>
|
||||
<Dropdown isOpen={accessMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.READER)}
|
||||
title={describeAccessMode(UserLevel.READER)}
|
||||
text={labelUserRole(UserRole.READER)}
|
||||
title={describeUserRole(UserRole.READER)}
|
||||
icon={<IconReader size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleChangeMode(UserLevel.READER)}
|
||||
onClick={() => handleChangeRole(UserRole.READER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.EDITOR)}
|
||||
title={describeAccessMode(UserLevel.EDITOR)}
|
||||
text={labelUserRole(UserRole.EDITOR)}
|
||||
title={describeUserRole(UserRole.EDITOR)}
|
||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||
disabled={!model.isOwned && !model.schema?.editors.includes(user.id)}
|
||||
onClick={() => handleChangeMode(UserLevel.EDITOR)}
|
||||
onClick={() => handleChangeRole(UserRole.EDITOR)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.OWNER)}
|
||||
title={describeAccessMode(UserLevel.OWNER)}
|
||||
text={labelUserRole(UserRole.OWNER)}
|
||||
title={describeUserRole(UserRole.OWNER)}
|
||||
icon={<IconOwner size='1rem' className='icon-primary' />}
|
||||
disabled={!model.isOwned}
|
||||
onClick={() => handleChangeMode(UserLevel.OWNER)}
|
||||
onClick={() => handleChangeRole(UserRole.OWNER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.ADMIN)}
|
||||
title={describeAccessMode(UserLevel.ADMIN)}
|
||||
text={labelUserRole(UserRole.ADMIN)}
|
||||
title={describeUserRole(UserRole.ADMIN)}
|
||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||
disabled={!user?.is_staff}
|
||||
onClick={() => handleChangeMode(UserLevel.ADMIN)}
|
||||
onClick={() => handleChangeRole(UserRole.ADMIN)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,6 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useState }
|
|||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
|
@ -30,8 +29,9 @@ import {
|
|||
OperationID,
|
||||
OperationType
|
||||
} from '@/models/oss';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
import { UserID, UserRole } from '@/models/user';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { errors, information } from '@/utils/labels';
|
||||
|
||||
|
@ -96,14 +96,13 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
|
|||
const router = useConceptNavigation();
|
||||
const { user } = useAuth();
|
||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||
|
||||
const role = useRoleStore(state => state.role);
|
||||
const adjustRole = useRoleStore(state => state.adjustRole);
|
||||
const model = useOSS();
|
||||
const library = useLibrary();
|
||||
|
||||
const isMutable = useMemo(
|
||||
() => accessLevel > UserLevel.READER && !model.schema?.read_only,
|
||||
[accessLevel, model.schema?.read_only]
|
||||
);
|
||||
const isMutable = role > UserRole.READER && !model.schema?.read_only;
|
||||
|
||||
const [showTooltip, setShowTooltip] = useState(true);
|
||||
|
||||
|
@ -128,23 +127,13 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
|
|||
|
||||
useEffect(
|
||||
() =>
|
||||
setAccessLevel(prev => {
|
||||
if (
|
||||
prev === UserLevel.EDITOR &&
|
||||
(model.isOwned || user?.is_staff || (user && model.schema?.editors.includes(user.id)))
|
||||
) {
|
||||
return UserLevel.EDITOR;
|
||||
} else if (user?.is_staff && (prev === UserLevel.ADMIN || adminMode)) {
|
||||
return UserLevel.ADMIN;
|
||||
} else if (model.isOwned) {
|
||||
return UserLevel.OWNER;
|
||||
} else if (user?.id && model.schema?.editors.includes(user?.id)) {
|
||||
return UserLevel.EDITOR;
|
||||
} else {
|
||||
return UserLevel.READER;
|
||||
}
|
||||
adjustRole({
|
||||
isOwner: model.isOwned,
|
||||
isEditor: (user && model.schema?.editors.includes(user?.id)) ?? false,
|
||||
isStaff: user?.is_staff ?? false,
|
||||
adminMode: adminMode
|
||||
}),
|
||||
[model.schema, setAccessLevel, model.isOwned, user, adminMode]
|
||||
[model.schema, adjustRole, model.isOwned, user, adminMode]
|
||||
);
|
||||
|
||||
const handleSetLocation = useCallback(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
import { AccessModeState } from '@/context/AccessModeContext';
|
||||
import { OssState } from '@/context/OssContext';
|
||||
|
||||
import OssTabs from './OssTabs';
|
||||
|
@ -10,11 +9,9 @@ import OssTabs from './OssTabs';
|
|||
function OssPage() {
|
||||
const params = useParams();
|
||||
return (
|
||||
<AccessModeState>
|
||||
<OssState itemID={params.id ?? ''}>
|
||||
<OssTabs />
|
||||
</OssState>
|
||||
</AccessModeState>
|
||||
<OssState itemID={params.id ?? ''}>
|
||||
<OssTabs />
|
||||
</OssState>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,13 @@ import MiniButton from '@/components/ui/MiniButton';
|
|||
import Overlay from '@/components/ui/Overlay';
|
||||
import Tooltip from '@/components/ui/Tooltip';
|
||||
import ValueIcon from '@/components/ui/ValueIcon';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useUsers } from '@/context/UsersContext';
|
||||
import useDropdown from '@/hooks/useDropdown';
|
||||
import { ILibraryItemData, ILibraryItemEditor } from '@/models/library';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
import { UserID, UserRole } from '@/models/user';
|
||||
import { useLibrarySearchStore } from '@/stores/librarySearch';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
import { prompts } from '@/utils/labels';
|
||||
|
||||
|
@ -35,7 +35,7 @@ interface EditorLibraryItemProps {
|
|||
|
||||
function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemProps) {
|
||||
const { getUserLabel, users } = useUsers();
|
||||
const { accessLevel } = useAccessMode();
|
||||
const role = useRoleStore(state => state.role);
|
||||
const intl = useIntl();
|
||||
const router = useConceptNavigation();
|
||||
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
||||
|
@ -86,9 +86,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
|
|||
value={item.location}
|
||||
title={controller.isAttachedToOSS ? 'Путь наследуется от ОСС' : 'Путь'}
|
||||
onClick={controller.promptLocation}
|
||||
disabled={
|
||||
isModified || controller.isProcessing || controller.isAttachedToOSS || accessLevel < UserLevel.OWNER
|
||||
}
|
||||
disabled={isModified || controller.isProcessing || controller.isAttachedToOSS || role < UserRole.OWNER}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -110,7 +108,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
|
|||
value={getUserLabel(item.owner)}
|
||||
title={controller.isAttachedToOSS ? 'Владелец наследуется от ОСС' : 'Владелец'}
|
||||
onClick={ownerSelector.toggle}
|
||||
disabled={isModified || controller.isProcessing || controller.isAttachedToOSS || accessLevel < UserLevel.OWNER}
|
||||
disabled={isModified || controller.isProcessing || controller.isAttachedToOSS || role < UserRole.OWNER}
|
||||
/>
|
||||
|
||||
<div className='sm:mb-1 flex justify-between items-center'>
|
||||
|
@ -120,7 +118,7 @@ function EditorLibraryItem({ item, isModified, controller }: EditorLibraryItemPr
|
|||
icon={<IconEditor size='1.25rem' className='icon-primary' />}
|
||||
value={item.editors.length}
|
||||
onClick={controller.promptEditors}
|
||||
disabled={isModified || controller.isProcessing || accessLevel < UserLevel.OWNER}
|
||||
disabled={isModified || controller.isProcessing || role < UserRole.OWNER}
|
||||
/>
|
||||
<Tooltip anchorSelect='#editor_stats' layer='z-modalTooltip'>
|
||||
<InfoUsers items={item?.editors ?? []} prefix={prefixes.user_editors} header='Редакторы' />
|
||||
|
|
|
@ -5,10 +5,10 @@ import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
|
|||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { AccessPolicy, ILibraryItemEditor } from '@/models/library';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { UserRole } from '@/models/user';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
interface ToolbarItemAccessProps {
|
||||
|
@ -20,7 +20,7 @@ interface ToolbarItemAccessProps {
|
|||
}
|
||||
|
||||
function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, controller }: ToolbarItemAccessProps) {
|
||||
const { accessLevel } = useAccessMode();
|
||||
const role = useRoleStore(state => state.role);
|
||||
const policy = controller.schema?.access_policy ?? AccessPolicy.PRIVATE;
|
||||
|
||||
return (
|
||||
|
@ -28,7 +28,7 @@ function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, c
|
|||
<Label text='Доступ' className='self-center select-none' />
|
||||
<div className='ml-auto cc-icons'>
|
||||
<SelectAccessPolicy
|
||||
disabled={accessLevel <= UserLevel.EDITOR || controller.isProcessing || controller.isAttachedToOSS}
|
||||
disabled={role <= UserRole.EDITOR || controller.isProcessing || controller.isAttachedToOSS}
|
||||
value={policy}
|
||||
onChange={newPolicy => controller.setAccessPolicy(newPolicy)}
|
||||
/>
|
||||
|
@ -37,7 +37,7 @@ function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, c
|
|||
title={visible ? 'Библиотека: отображать' : 'Библиотека: скрывать'}
|
||||
icon={<VisibilityIcon value={visible} />}
|
||||
onClick={toggleVisible}
|
||||
disabled={accessLevel === UserLevel.READER || controller.isProcessing}
|
||||
disabled={role === UserRole.READER || controller.isProcessing}
|
||||
/>
|
||||
|
||||
<MiniButton
|
||||
|
@ -50,7 +50,7 @@ function ToolbarItemAccess({ visible, toggleVisible, readOnly, toggleReadOnly, c
|
|||
)
|
||||
}
|
||||
onClick={toggleReadOnly}
|
||||
disabled={accessLevel === UserLevel.READER || controller.isProcessing}
|
||||
disabled={role === UserRole.READER || controller.isProcessing}
|
||||
/>
|
||||
|
||||
<BadgeHelp topic={HelpTopic.ACCESS} className={PARAMETER.TOOLTIP_WIDTH} offset={4} />
|
||||
|
|
|
@ -5,11 +5,11 @@ import BadgeHelp from '@/components/info/BadgeHelp';
|
|||
import MiniSelectorOSS from '@/components/select/MiniSelectorOSS';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { AccessPolicy, ILibraryItemEditor, LibraryItemType } from '@/models/library';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { IRSForm } from '@/models/rsform';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { UserRole } from '@/models/user';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip, tooltips } from '@/utils/labels';
|
||||
|
||||
|
@ -23,7 +23,7 @@ interface ToolbarRSFormCardProps {
|
|||
}
|
||||
|
||||
function ToolbarRSFormCard({ modified, controller, onSubmit, onDestroy }: ToolbarRSFormCardProps) {
|
||||
const { accessLevel } = useAccessMode();
|
||||
const role = useRoleStore(state => state.role);
|
||||
const canSave = modified && !controller.isProcessing;
|
||||
|
||||
const ossSelector = (() => {
|
||||
|
@ -63,7 +63,7 @@ function ToolbarRSFormCard({ modified, controller, onSubmit, onDestroy }: Toolba
|
|||
<MiniButton
|
||||
title='Удалить схему'
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
disabled={!controller.isMutable || controller.isProcessing || accessLevel < UserLevel.OWNER}
|
||||
disabled={!controller.isMutable || controller.isProcessing || role < UserRole.OWNER}
|
||||
onClick={onDestroy}
|
||||
/>
|
||||
) : null}
|
||||
|
|
|
@ -31,14 +31,14 @@ import Button from '@/components/ui/Button';
|
|||
import Divider from '@/components/ui/Divider';
|
||||
import Dropdown from '@/components/ui/Dropdown';
|
||||
import DropdownButton from '@/components/ui/DropdownButton';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useGlobalOss } from '@/context/GlobalOssContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useRSForm } from '@/context/RSFormContext';
|
||||
import useDropdown from '@/hooks/useDropdown';
|
||||
import { AccessPolicy } from '@/models/library';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { UserRole } from '@/models/user';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { describeAccessMode, labelAccessMode, tooltips } from '@/utils/labels';
|
||||
|
||||
import { OssTabID } from '../OssPage/OssTabs';
|
||||
|
@ -55,7 +55,8 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
|||
const model = useRSForm();
|
||||
const oss = useGlobalOss();
|
||||
|
||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||
const role = useRoleStore(state => state.role);
|
||||
const setRole = useRoleStore(state => state.setRole);
|
||||
|
||||
const schemaMenu = useDropdown();
|
||||
const editMenu = useDropdown();
|
||||
|
@ -126,9 +127,9 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
|||
controller.inlineSynthesis();
|
||||
}
|
||||
|
||||
function handleChangeMode(newMode: UserLevel) {
|
||||
function handleChangeMode(newMode: UserRole) {
|
||||
accessMenu.hide();
|
||||
setAccessLevel(newMode);
|
||||
setRole(newMode);
|
||||
}
|
||||
|
||||
function handleCreateNew() {
|
||||
|
@ -198,7 +199,7 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
|||
<DropdownButton
|
||||
text='Удалить схему'
|
||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||
disabled={controller.isProcessing || accessLevel < UserLevel.OWNER}
|
||||
disabled={controller.isProcessing || role < UserRole.OWNER}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
) : null}
|
||||
|
@ -310,15 +311,15 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
|||
noBorder
|
||||
noOutline
|
||||
tabIndex={-1}
|
||||
title={`Режим ${labelAccessMode(accessLevel)}`}
|
||||
title={`Режим ${labelAccessMode(role)}`}
|
||||
hideTitle={accessMenu.isOpen}
|
||||
className='h-full pr-2'
|
||||
icon={
|
||||
accessLevel === UserLevel.ADMIN ? (
|
||||
role === UserRole.ADMIN ? (
|
||||
<IconAdmin size='1.25rem' className='icon-primary' />
|
||||
) : accessLevel === UserLevel.OWNER ? (
|
||||
) : role === UserRole.OWNER ? (
|
||||
<IconOwner size='1.25rem' className='icon-primary' />
|
||||
) : accessLevel === UserLevel.EDITOR ? (
|
||||
) : role === UserRole.EDITOR ? (
|
||||
<IconEditor size='1.25rem' className='icon-primary' />
|
||||
) : (
|
||||
<IconReader size='1.25rem' className='icon-primary' />
|
||||
|
@ -328,31 +329,31 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
|||
/>
|
||||
<Dropdown isOpen={accessMenu.isOpen}>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.READER)}
|
||||
title={describeAccessMode(UserLevel.READER)}
|
||||
text={labelAccessMode(UserRole.READER)}
|
||||
title={describeAccessMode(UserRole.READER)}
|
||||
icon={<IconReader size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleChangeMode(UserLevel.READER)}
|
||||
onClick={() => handleChangeMode(UserRole.READER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.EDITOR)}
|
||||
title={describeAccessMode(UserLevel.EDITOR)}
|
||||
text={labelAccessMode(UserRole.EDITOR)}
|
||||
title={describeAccessMode(UserRole.EDITOR)}
|
||||
icon={<IconEditor size='1rem' className='icon-primary' />}
|
||||
disabled={!model.isOwned && !model.schema?.editors.includes(user.id)}
|
||||
onClick={() => handleChangeMode(UserLevel.EDITOR)}
|
||||
onClick={() => handleChangeMode(UserRole.EDITOR)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.OWNER)}
|
||||
title={describeAccessMode(UserLevel.OWNER)}
|
||||
text={labelAccessMode(UserRole.OWNER)}
|
||||
title={describeAccessMode(UserRole.OWNER)}
|
||||
icon={<IconOwner size='1rem' className='icon-primary' />}
|
||||
disabled={!model.isOwned}
|
||||
onClick={() => handleChangeMode(UserLevel.OWNER)}
|
||||
onClick={() => handleChangeMode(UserRole.OWNER)}
|
||||
/>
|
||||
<DropdownButton
|
||||
text={labelAccessMode(UserLevel.ADMIN)}
|
||||
title={describeAccessMode(UserLevel.ADMIN)}
|
||||
text={labelAccessMode(UserRole.ADMIN)}
|
||||
title={describeAccessMode(UserRole.ADMIN)}
|
||||
icon={<IconAdmin size='1rem' className='icon-primary' />}
|
||||
disabled={!user?.is_staff}
|
||||
onClick={() => handleChangeMode(UserLevel.ADMIN)}
|
||||
onClick={() => handleChangeMode(UserRole.ADMIN)}
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,6 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useState }
|
|||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useRSForm } from '@/context/RSFormContext';
|
||||
|
@ -49,8 +48,9 @@ import {
|
|||
TermForm
|
||||
} from '@/models/rsform';
|
||||
import { generateAlias } from '@/models/rsformAPI';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
import { UserID, UserRole } from '@/models/user';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||
import { information, prompts } from '@/utils/labels';
|
||||
import { promptUnsaved } from '@/utils/utils';
|
||||
|
@ -143,13 +143,11 @@ export const RSEditState = ({
|
|||
const router = useConceptNavigation();
|
||||
const { user } = useAuth();
|
||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||
const role = useRoleStore(state => state.role);
|
||||
const adjustRole = useRoleStore(state => state.adjustRole);
|
||||
const model = useRSForm();
|
||||
|
||||
const isMutable = useMemo(
|
||||
() => accessLevel > UserLevel.READER && !model.schema?.read_only,
|
||||
[accessLevel, model.schema?.read_only]
|
||||
);
|
||||
const isMutable = useMemo(() => role > UserRole.READER && !model.schema?.read_only, [role, model.schema?.read_only]);
|
||||
const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]);
|
||||
const canDeleteSelected = useMemo(
|
||||
() => selected.length > 0 && selected.every(id => !model.schema?.cstByID.get(id)?.is_inherited),
|
||||
|
@ -199,23 +197,13 @@ export const RSEditState = ({
|
|||
|
||||
useEffect(
|
||||
() =>
|
||||
setAccessLevel(prev => {
|
||||
if (
|
||||
prev === UserLevel.EDITOR &&
|
||||
(model.isOwned || user?.is_staff || (user && model.schema?.editors.includes(user.id)))
|
||||
) {
|
||||
return UserLevel.EDITOR;
|
||||
} else if (user?.is_staff && (prev === UserLevel.ADMIN || adminMode)) {
|
||||
return UserLevel.ADMIN;
|
||||
} else if (model.isOwned) {
|
||||
return UserLevel.OWNER;
|
||||
} else if (user?.id && model.schema?.editors.includes(user?.id)) {
|
||||
return UserLevel.EDITOR;
|
||||
} else {
|
||||
return UserLevel.READER;
|
||||
}
|
||||
adjustRole({
|
||||
isOwner: model.isOwned,
|
||||
isEditor: (user && model.schema?.editors.includes(user?.id)) ?? false,
|
||||
isStaff: user?.is_staff ?? false,
|
||||
adminMode: adminMode
|
||||
}),
|
||||
[model.schema, setAccessLevel, model.isOwned, user, adminMode]
|
||||
[model.schema, adjustRole, model.isOwned, user, adminMode]
|
||||
);
|
||||
|
||||
const updateSchema = useCallback(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
import { AccessModeState } from '@/context/AccessModeContext';
|
||||
import { RSFormState } from '@/context/RSFormContext';
|
||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||
|
||||
|
@ -13,11 +12,9 @@ function RSFormPage() {
|
|||
const query = useQueryStrings();
|
||||
const version = query.get('v') ?? undefined;
|
||||
return (
|
||||
<AccessModeState>
|
||||
<RSFormState itemID={params.id ?? ''} versionID={version}>
|
||||
<RSTabs />
|
||||
</RSFormState>
|
||||
</AccessModeState>
|
||||
<RSFormState itemID={params.id ?? ''} versionID={version}>
|
||||
<RSTabs />
|
||||
</RSFormState>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { UserRole } from '@/models/user';
|
||||
import { useFitHeight } from '@/stores/appLayout';
|
||||
import { useRoleStore } from '@/stores/role';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import ConstituentsSearch from './ConstituentsSearch';
|
||||
|
@ -27,8 +27,8 @@ interface ViewConstituentsProps {
|
|||
|
||||
function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit, isMounted }: ViewConstituentsProps) {
|
||||
const windowSize = useWindowSize();
|
||||
const { accessLevel } = useAccessMode();
|
||||
const listHeight = useFitHeight(!isBottom ? '8.2rem' : accessLevel !== UserLevel.READER ? '42rem' : '35rem', '10rem');
|
||||
const role = useRoleStore(state => state.role);
|
||||
const listHeight = useFitHeight(!isBottom ? '8.2rem' : role !== UserRole.READER ? '42rem' : '35rem', '10rem');
|
||||
|
||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
|
||||
|
||||
|
|
37
rsconcept/frontend/src/stores/role.ts
Normal file
37
rsconcept/frontend/src/stores/role.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
import { UserRole } from '@/models/user';
|
||||
|
||||
export interface RoleFlags {
|
||||
isOwner: boolean;
|
||||
isEditor: boolean;
|
||||
isStaff: boolean;
|
||||
adminMode: boolean;
|
||||
}
|
||||
|
||||
interface RoleStore {
|
||||
role: UserRole;
|
||||
setRole: (value: UserRole) => void;
|
||||
adjustRole: (flags: RoleFlags) => void;
|
||||
}
|
||||
|
||||
export const useRoleStore = create<RoleStore>()(set => ({
|
||||
role: UserRole.READER,
|
||||
setRole: value => set({ role: value }),
|
||||
adjustRole: ({ isOwner, isEditor, isStaff, adminMode }: RoleFlags) =>
|
||||
set(state => {
|
||||
if (state.role === UserRole.EDITOR && (isOwner || isStaff || isEditor)) {
|
||||
return { role: UserRole.EDITOR };
|
||||
}
|
||||
if (isStaff && (state.role === UserRole.ADMIN || adminMode)) {
|
||||
return { role: UserRole.ADMIN };
|
||||
}
|
||||
if (isOwner) {
|
||||
return { role: UserRole.OWNER };
|
||||
}
|
||||
if (isEditor) {
|
||||
return { role: UserRole.EDITOR };
|
||||
}
|
||||
return { role: UserRole.READER };
|
||||
})
|
||||
}));
|
|
@ -19,7 +19,7 @@ import {
|
|||
RSErrorType,
|
||||
TokenID
|
||||
} from '@/models/rslang';
|
||||
import { UserLevel } from '@/models/user';
|
||||
import { UserRole } from '@/models/user';
|
||||
|
||||
import { PARAMETER } from './constants';
|
||||
|
||||
|
@ -822,31 +822,31 @@ export function describeSubstitutionError(error: ISubstitutionErrorDescription):
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves label for {@link UserLevel}.
|
||||
* Retrieves label for {@link UserRole}.
|
||||
*/
|
||||
export function labelAccessMode(mode: UserLevel): string {
|
||||
export function labelAccessMode(mode: UserRole): string {
|
||||
// prettier-ignore
|
||||
switch (mode) {
|
||||
case UserLevel.READER: return 'Читатель';
|
||||
case UserLevel.EDITOR: return 'Редактор';
|
||||
case UserLevel.OWNER: return 'Владелец';
|
||||
case UserLevel.ADMIN: return 'Администратор';
|
||||
case UserRole.READER: return 'Читатель';
|
||||
case UserRole.EDITOR: return 'Редактор';
|
||||
case UserRole.OWNER: return 'Владелец';
|
||||
case UserRole.ADMIN: return 'Администратор';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves description for {@link UserLevel}.
|
||||
* Retrieves description for {@link UserRole}.
|
||||
*/
|
||||
export function describeAccessMode(mode: UserLevel): string {
|
||||
export function describeAccessMode(mode: UserRole): string {
|
||||
// prettier-ignore
|
||||
switch (mode) {
|
||||
case UserLevel.READER:
|
||||
case UserRole.READER:
|
||||
return 'Режим запрещает редактирование';
|
||||
case UserLevel.EDITOR:
|
||||
case UserRole.EDITOR:
|
||||
return 'Режим редактирования';
|
||||
case UserLevel.OWNER:
|
||||
case UserRole.OWNER:
|
||||
return 'Режим владельца';
|
||||
case UserLevel.ADMIN:
|
||||
case UserRole.ADMIN:
|
||||
return 'Режим администратора';
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user