Refactoring: prepare for user permissions implementation

This commit is contained in:
IRBorisov 2024-05-26 14:54:02 +03:00
parent 467cd3dcc9
commit e0dcbd612c
23 changed files with 175 additions and 125 deletions

View File

@ -7,21 +7,7 @@ import { toast } from 'react-toastify';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
import { ILexemeData, IResolutionData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language'; import { ILexemeData, IResolutionData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language';
import { import { ILibraryItem, ILibraryUpdateData, IVersionData } from '@/models/library';
ICurrentUser,
ILibraryItem,
ILibraryUpdateData,
IPasswordTokenData,
IRequestPasswordData,
IResetPasswordData as IResetPasswordData,
IUserInfo,
IUserLoginData,
IUserProfile,
IUserSignupData,
IUserUpdateData,
IUserUpdatePassword,
IVersionData
} from '@/models/library';
import { import {
IConstituentaList, IConstituentaList,
IConstituentaMeta, IConstituentaMeta,
@ -30,7 +16,6 @@ import {
ICstMovetoData, ICstMovetoData,
ICstRenameData, ICstRenameData,
ICstSubstituteData, ICstSubstituteData,
ICstTarget,
ICstUpdateData, ICstUpdateData,
IInlineSynthesisData, IInlineSynthesisData,
IProduceStructureResponse, IProduceStructureResponse,
@ -38,9 +23,24 @@ import {
IRSFormCreateData, IRSFormCreateData,
IRSFormData, IRSFormData,
IRSFormUploadData, IRSFormUploadData,
ITargetCst,
IVersionCreatedResponse IVersionCreatedResponse
} from '@/models/rsform'; } from '@/models/rsform';
import { IExpressionParse, IRSExpression } from '@/models/rslang'; import { IExpressionParse, IRSExpression } from '@/models/rslang';
import {
ICurrentUser,
IPasswordTokenData,
IRequestPasswordData,
IResetPasswordData,
ITargetUser,
ITargetUsers,
IUserInfo,
IUserLoginData,
IUserProfile,
IUserSignupData,
IUserUpdateData,
IUserUpdatePassword
} from '@/models/user';
import { buildConstants } from '../utils/constants'; import { buildConstants } from '../utils/constants';
@ -246,6 +246,34 @@ export function deleteLibraryItem(target: string, request: FrontAction) {
}); });
} }
export function patchSetOwner(target: string, request: FrontPush<ITargetUser>) {
AxiosPatch({
endpoint: `/api/library/${target}/set-owner`,
request: request
});
}
export function patchEditorsAdd(target: string, request: FrontPush<ITargetUser>) {
AxiosPatch({
endpoint: `/api/library/${target}/editors-add`,
request: request
});
}
export function patchEditorsRemove(target: string, request: FrontPush<ITargetUser>) {
AxiosPatch({
endpoint: `/api/library/${target}/editors-remove`,
request: request
});
}
export function patchEditorsSet(target: string, request: FrontPush<ITargetUsers>) {
AxiosPatch({
endpoint: `/api/library/${target}/editors-set`,
request: request
});
}
export function postSubscribe(target: string, request: FrontAction) { export function postSubscribe(target: string, request: FrontAction) {
AxiosPost({ AxiosPost({
endpoint: `/api/library/${target}/subscribe`, endpoint: `/api/library/${target}/subscribe`,
@ -304,7 +332,7 @@ export function patchRenameConstituenta(schema: string, request: FrontExchange<I
}); });
} }
export function patchProduceStructure(schema: string, request: FrontExchange<ICstTarget, IProduceStructureResponse>) { export function patchProduceStructure(schema: string, request: FrontExchange<ITargetCst, IProduceStructureResponse>) {
AxiosPatch({ AxiosPatch({
endpoint: `/api/rsforms/${schema}/cst-produce-structure`, endpoint: `/api/rsforms/${schema}/cst-produce-structure`,
request: request request: request

View File

@ -3,9 +3,9 @@ import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import { HelpTopic } from '@/models/miscellaneous'; import { HelpTopic } from '@/models/miscellaneous';
import TopicPage from '../../pages/ManualsPage/TopicPage';
import { IconHelp } from '../Icons'; import { IconHelp } from '../Icons';
import { CProps } from '../props'; import { CProps } from '../props';
import TopicPage from '../../pages/ManualsPage/TopicPage';
interface BadgeHelpProps extends CProps.Styling { interface BadgeHelpProps extends CProps.Styling {
topic: HelpTopic; topic: HelpTopic;

View File

@ -40,7 +40,7 @@ function DescribeError({ error }: { error: ErrorData }) {
); );
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const isHtml = isResponseHtml(error.response); const isHtml = isResponseHtml(error.response);
return ( return (
<div> <div>

View File

@ -10,7 +10,7 @@ import { CstMatchMode } from '@/models/miscellaneous';
import { prefixes } from '@/utils/constants'; import { prefixes } from '@/utils/constants';
import { describeCstMatchMode, labelCstMatchMode } from '@/utils/labels'; import { describeCstMatchMode, labelCstMatchMode } from '@/utils/labels';
import { IconAlias, IconTerm, IconFilter, IconFormula, IconText } from '../Icons'; import { IconAlias, IconFilter, IconFormula, IconTerm, IconText } from '../Icons';
import DropdownButton from '../ui/DropdownButton'; import DropdownButton from '../ui/DropdownButton';
function MatchModeIcon(mode: CstMatchMode, size: string, color?: string) { function MatchModeIcon(mode: CstMatchMode, size: string, color?: string) {

View File

@ -14,12 +14,17 @@ import {
postValidatePasswordToken postValidatePasswordToken
} from '@/app/backendAPI'; } from '@/app/backendAPI';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
import { IPasswordTokenData, IRequestPasswordData, IResetPasswordData, IUserLoginData } from '@/models/library'; import {
import { ICurrentUser } from '@/models/library'; ICurrentUser,
import { IUserSignupData } from '@/models/library'; IPasswordTokenData,
import { IUserProfile } from '@/models/library'; IRequestPasswordData,
import { IUserInfo } from '@/models/library'; IResetPasswordData,
import { IUserUpdatePassword } from '@/models/library'; IUserInfo,
IUserLoginData,
IUserProfile,
IUserSignupData,
IUserUpdatePassword
} from '@/models/user';
import { useUsers } from './UsersContext'; import { useUsers } from './UsersContext';

View File

@ -36,12 +36,12 @@ import {
ICstMovetoData, ICstMovetoData,
ICstRenameData, ICstRenameData,
ICstSubstituteData, ICstSubstituteData,
ICstTarget,
ICstUpdateData, ICstUpdateData,
IInlineSynthesisData, IInlineSynthesisData,
IRSForm, IRSForm,
IRSFormData, IRSFormData,
IRSFormUploadData IRSFormUploadData,
ITargetCst
} from '@/models/rsform'; } from '@/models/rsform';
import { useAuth } from './AuthContext'; import { useAuth } from './AuthContext';
@ -69,7 +69,7 @@ interface IRSFormContext {
resetAliases: (callback: () => void) => void; resetAliases: (callback: () => void) => void;
restoreOrder: (callback: () => void) => void; restoreOrder: (callback: () => void) => void;
produceStructure: (data: ICstTarget, callback?: DataCallback<ConstituentaID[]>) => void; produceStructure: (data: ITargetCst, callback?: DataCallback<ConstituentaID[]>) => void;
inlineSynthesis: (data: IInlineSynthesisData, callback?: DataCallback<IRSFormData>) => void; inlineSynthesis: (data: IInlineSynthesisData, callback?: DataCallback<IRSFormData>) => void;
cstCreate: (data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => void; cstCreate: (data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => void;
@ -269,7 +269,7 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps)
); );
const produceStructure = useCallback( const produceStructure = useCallback(
(data: ICstTarget, callback?: DataCallback<ConstituentaID[]>) => { (data: ITargetCst, callback?: DataCallback<ConstituentaID[]>) => {
setError(undefined); setError(undefined);
patchProduceStructure(schemaID, { patchProduceStructure(schemaID, {
data: data, data: data,

View File

@ -4,8 +4,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
import { DataCallback, getProfile, patchProfile } from '@/app/backendAPI'; import { DataCallback, getProfile, patchProfile } from '@/app/backendAPI';
import { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import { IUserProfile } from '@/models/library'; import { IUserProfile, IUserUpdateData } from '@/models/user';
import { IUserUpdateData } from '@/models/library';
import { useUsers } from './UsersContext'; import { useUsers } from './UsersContext';

View File

@ -3,7 +3,7 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { getActiveUsers } from '@/app/backendAPI'; import { getActiveUsers } from '@/app/backendAPI';
import { type IUserInfo } from '@/models/library'; import { IUserInfo } from '@/models/user';
interface IUsersContext { interface IUsersContext {
users: IUserInfo[]; users: IUserInfo[];

View File

@ -2,12 +2,12 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { DataCallback, postCheckExpression } from '@/app/backendAPI';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
import { CstType, IConstituenta, type IRSForm } from '@/models/rsform'; import { CstType, IConstituenta, type IRSForm } from '@/models/rsform';
import { getDefinitionPrefix } from '@/models/rsformAPI'; import { getDefinitionPrefix } from '@/models/rsformAPI';
import { IArgumentInfo, IExpressionParse } from '@/models/rslang'; import { IArgumentInfo, IExpressionParse } from '@/models/rslang';
import { RSErrorType } from '@/models/rslang'; import { RSErrorType } from '@/models/rslang';
import { DataCallback, postCheckExpression } from '@/app/backendAPI';
import { PARAMETER } from '@/utils/constants'; import { PARAMETER } from '@/utils/constants';
function useCheckExpression({ schema }: { schema?: IRSForm }) { function useCheckExpression({ schema }: { schema?: IRSForm }) {

View File

@ -2,10 +2,10 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { getRSFormDetails } from '@/app/backendAPI';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
import { IRSForm, IRSFormData } from '@/models/rsform'; import { IRSForm, IRSFormData } from '@/models/rsform';
import { RSFormLoader } from '@/models/RSFormLoader'; import { RSFormLoader } from '@/models/RSFormLoader';
import { getRSFormDetails } from '@/app/backendAPI';
function useRSFormDetails({ target, version }: { target?: string; version?: string }) { function useRSFormDetails({ target, version }: { target?: string; version?: string }) {
const [schema, setInnerSchema] = useState<IRSForm | undefined>(undefined); const [schema, setInnerSchema] = useState<IRSForm | undefined>(undefined);

View File

@ -2,10 +2,10 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { DataCallback, postResolveText } from '@/app/backendAPI';
import { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import { IResolutionData } from '@/models/language'; import { IResolutionData } from '@/models/language';
import { IRSForm } from '@/models/rsform'; import { IRSForm } from '@/models/rsform';
import { DataCallback, postResolveText } from '@/app/backendAPI';
function useResolveText({ schema }: { schema?: IRSForm }) { function useResolveText({ schema }: { schema?: IRSForm }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);

View File

@ -1,83 +1,7 @@
/** /**
* Module: Models for Library entities and Users. * Module: Models for LibraryItem.
*/ */
/**
* Represents user detailed information.
* Some information should only be accessible to authorized users
*/
export interface IUser {
id: number | null;
username: string;
is_staff: boolean;
email: string;
first_name: string;
last_name: string;
}
/**
* Represents CurrentUser information.
*/
export interface ICurrentUser extends Pick<IUser, 'id' | 'username' | 'is_staff'> {
subscriptions: number[];
}
/**
* Represents login data, used to authenticate users.
*/
export interface IUserLoginData extends Pick<IUser, 'username'> {
password: string;
}
/**
* Represents password reset data.
*/
export interface IResetPasswordData {
password: string;
token: string;
}
/**
* Represents password token data.
*/
export interface IPasswordTokenData extends Pick<IResetPasswordData, 'token'> {}
/**
* Represents password reset request data.
*/
export interface IRequestPasswordData extends Pick<IUser, 'email'> {}
/**
* Represents signup data, used to create new users.
*/
export interface IUserSignupData extends Omit<IUser, 'is_staff' | 'id'> {
password: string;
password2: string;
}
/**
* Represents user data, intended to update user profile in persistent storage.
*/
export interface IUserUpdateData extends Omit<IUser, 'is_staff' | 'id'> {}
/**
* Represents user profile for viewing and editing {@link IUser}.
*/
export interface IUserProfile extends Omit<IUser, 'is_staff'> {}
/**
* Represents user reference information.
*/
export interface IUserInfo extends Omit<IUserProfile, 'email' | 'username'> {}
/**
* Represents data needed to update password for current user.
*/
export interface IUserUpdatePassword {
old_password: string;
new_password: string;
}
/** /**
* Represents type of library items. * Represents type of library items.
*/ */

View File

@ -85,7 +85,7 @@ export interface IConstituentaMeta {
/** /**
* Represents target {@link IConstituenta}. * Represents target {@link IConstituenta}.
*/ */
export interface ICstTarget { export interface ITargetCst {
target: ConstituentaID; target: ConstituentaID;
} }
@ -156,7 +156,7 @@ export interface ICstUpdateData
/** /**
* Represents data, used in renaming {@link IConstituenta}. * Represents data, used in renaming {@link IConstituenta}.
*/ */
export interface ICstRenameData extends ICstTarget, Pick<IConstituentaMeta, 'alias' | 'cst_type'> {} export interface ICstRenameData extends ITargetCst, Pick<IConstituentaMeta, 'alias' | 'cst_type'> {}
/** /**
* Represents data, used in merging single {@link IConstituenta}. * Represents data, used in merging single {@link IConstituenta}.

View File

@ -0,0 +1,93 @@
/**
* Module: Models for Users.
*/
/**
* Represents user detailed information.
* Some information should only be accessible to authorized users
*/
export interface IUser {
id: number;
username: string;
is_staff: boolean;
email: string;
first_name: string;
last_name: string;
}
/**
* Represents CurrentUser information.
*/
export interface ICurrentUser extends Pick<IUser, 'id' | 'username' | 'is_staff'> {
subscriptions: number[];
}
/**
* Represents login data, used to authenticate users.
*/
export interface IUserLoginData extends Pick<IUser, 'username'> {
password: string;
}
/**
* Represents password reset data.
*/
export interface IResetPasswordData {
password: string;
token: string;
}
/**
* Represents password token data.
*/
export interface IPasswordTokenData extends Pick<IResetPasswordData, 'token'> {}
/**
* Represents password reset request data.
*/
export interface IRequestPasswordData extends Pick<IUser, 'email'> {}
/**
* Represents signup data, used to create new users.
*/
export interface IUserSignupData extends Omit<IUser, 'is_staff' | 'id'> {
password: string;
password2: string;
}
/**
* Represents user data, intended to update user profile in persistent storage.
*/
export interface IUserUpdateData extends Omit<IUser, 'is_staff' | 'id'> {}
/**
* Represents user profile for viewing and editing {@link IUser}.
*/
export interface IUserProfile extends Omit<IUser, 'is_staff'> {}
/**
* Represents user reference information.
*/
export interface IUserInfo extends Omit<IUserProfile, 'email' | 'username'> {}
/**
* Represents data needed to update password for current user.
*/
export interface IUserUpdatePassword {
old_password: string;
new_password: string;
}
/**
* Represents target {@link User}.
*/
export interface ITargetUser {
user: number;
}
/**
* Represents target multiple {@link User}.
*/
export interface ITargetUsers {
users: number[];
}

View File

@ -1,7 +1,8 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { IconImmutable, IconPublic } from '@/components/Icons'; import { IconImmutable, IconPublic } from '@/components/Icons';
import { ICurrentUser, ILibraryItem } from '@/models/library'; import { ILibraryItem } from '@/models/library';
import { ICurrentUser } from '@/models/user';
import { prefixes } from '@/utils/constants'; import { prefixes } from '@/utils/constants';
interface ItemIconsProps { interface ItemIconsProps {

View File

@ -14,7 +14,7 @@ import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext'; import { useConceptNavigation } from '@/context/NavigationContext';
import useQueryStrings from '@/hooks/useQueryStrings'; import useQueryStrings from '@/hooks/useQueryStrings';
import { IUserLoginData } from '@/models/library'; import { IUserLoginData } from '@/models/user';
import { resources } from '@/utils/constants'; import { resources } from '@/utils/constants';
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ProcessError({ error }: { error: ErrorData }): React.ReactElement {

View File

@ -1,6 +1,6 @@
import TopicPage from '@/pages/ManualsPage/TopicPage';
import AnimateFade from '@/components/wrap/AnimateFade'; import AnimateFade from '@/components/wrap/AnimateFade';
import { HelpTopic } from '@/models/miscellaneous'; import { HelpTopic } from '@/models/miscellaneous';
import TopicPage from '@/pages/ManualsPage/TopicPage';
interface ViewTopicProps { interface ViewTopicProps {
topic: HelpTopic; topic: HelpTopic;

View File

@ -12,7 +12,7 @@ import DataLoader from '@/components/wrap/DataLoader';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext'; import { useConceptNavigation } from '@/context/NavigationContext';
import useQueryStrings from '@/hooks/useQueryStrings'; import useQueryStrings from '@/hooks/useQueryStrings';
import { IPasswordTokenData, IResetPasswordData } from '@/models/library'; import { IPasswordTokenData, IResetPasswordData } from '@/models/user';
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 404) { if (axios.isAxiosError(error) && error.response && error.response.status === 404) {

View File

@ -38,10 +38,10 @@ import {
ICstMovetoData, ICstMovetoData,
ICstRenameData, ICstRenameData,
ICstSubstituteData, ICstSubstituteData,
ICstTarget,
ICstUpdateData, ICstUpdateData,
IInlineSynthesisData, IInlineSynthesisData,
IRSForm, IRSForm,
ITargetCst,
TermForm TermForm
} from '@/models/rsform'; } from '@/models/rsform';
import { generateAlias } from '@/models/rsformAPI'; import { generateAlias } from '@/models/rsformAPI';
@ -447,7 +447,7 @@ export const RSEditState = ({
if (isModified && !promptUnsaved()) { if (isModified && !promptUnsaved()) {
return; return;
} }
const data: ICstTarget = { const data: ITargetCst = {
target: activeCst.id target: activeCst.id
}; };
model.produceStructure(data, cstList => { model.produceStructure(data, cstList => {

View File

@ -19,7 +19,7 @@ import AnimateFade from '@/components/wrap/AnimateFade';
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous'; import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext'; import { useConceptNavigation } from '@/context/NavigationContext';
import { type IUserSignupData } from '@/models/library'; import { IUserSignupData } from '@/models/user';
import { globals, patterns } from '@/utils/constants'; import { globals, patterns } from '@/utils/constants';
function RegisterPage() { function RegisterPage() {

View File

@ -10,7 +10,7 @@ import TextInput from '@/components/ui/TextInput';
import TextURL from '@/components/ui/TextURL'; import TextURL from '@/components/ui/TextURL';
import AnimateFade from '@/components/wrap/AnimateFade'; import AnimateFade from '@/components/wrap/AnimateFade';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { IRequestPasswordData } from '@/models/library'; import { IRequestPasswordData } from '@/models/user';
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 400) { if (axios.isAxiosError(error) && error.response && error.response.status === 400) {

View File

@ -12,7 +12,7 @@ import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext'; import { useConceptNavigation } from '@/context/NavigationContext';
import { IUserUpdatePassword } from '@/models/library'; import { IUserUpdatePassword } from '@/models/user';
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 400) { if (axios.isAxiosError(error) && error.response && error.response.status === 400) {

View File

@ -8,7 +8,7 @@ import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import { useBlockNavigation } from '@/context/NavigationContext'; import { useBlockNavigation } from '@/context/NavigationContext';
import { useUserProfile } from '@/context/UserProfileContext'; import { useUserProfile } from '@/context/UserProfileContext';
import { IUserUpdateData } from '@/models/library'; import { IUserUpdateData } from '@/models/user';
function EditorProfile() { function EditorProfile() {
const { updateUser, user, processing } = useUserProfile(); const { updateUser, user, processing } = useUserProfile();