mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactoring: Context structure and error processing
This commit is contained in:
parent
3f2db1e7bc
commit
89c2e704a8
|
@ -17,18 +17,6 @@ import useQueryStrings from '@/hooks/useQueryStrings';
|
|||
import { IUserLoginData } from '@/models/user';
|
||||
import { resources } from '@/utils/constants';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return (
|
||||
<div className='text-sm select-text clr-text-red'>
|
||||
На Портале отсутствует такое сочетание имени пользователя и пароля
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
||||
function LoginPage() {
|
||||
const router = useConceptNavigation();
|
||||
const query = useQueryStrings();
|
||||
|
@ -105,3 +93,16 @@ function LoginPage() {
|
|||
}
|
||||
|
||||
export default LoginPage;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return (
|
||||
<div className='text-sm select-text clr-text-red'>
|
||||
На Портале отсутствует такое сочетание имени пользователя и пароля
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import axios from 'axios';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useConceptOptions } from '@/context/OptionsContext';
|
||||
|
@ -174,25 +170,7 @@ export const OssEditState = ({ children }: OssEditStateProps) => {
|
|||
</AnimatePresence>
|
||||
) : null}
|
||||
|
||||
{model.loading ? <Loader /> : null}
|
||||
{model.errorLoading ? <ProcessError error={model.errorLoading} /> : null}
|
||||
{model.schema && !model.loading ? children : null}
|
||||
{children}
|
||||
</OssEditContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>{`Схема с указанным идентификатором отсутствует`}</p>
|
||||
<div className='flex justify-center'>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
'use client';
|
||||
|
||||
import axios from 'axios';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
||||
|
@ -30,7 +34,7 @@ function OssTabs() {
|
|||
const activeTab = (Number(query.get('tab')) ?? OssTabID.CARD) as OssTabID;
|
||||
|
||||
const { calculateHeight } = useConceptOptions();
|
||||
const { schema, loading } = useOSS();
|
||||
const { schema, loading, errorLoading } = useOSS();
|
||||
const { destroyItem } = useLibrary();
|
||||
|
||||
const [isModified, setIsModified] = useState(false);
|
||||
|
@ -115,6 +119,8 @@ function OssTabs() {
|
|||
|
||||
return (
|
||||
<OssEditState>
|
||||
{loading ? <Loader /> : null}
|
||||
{errorLoading ? <ProcessError error={errorLoading} /> : null}
|
||||
{schema && !loading ? (
|
||||
<Tabs
|
||||
selectedIndex={activeTab}
|
||||
|
@ -141,3 +147,27 @@ function OssTabs() {
|
|||
}
|
||||
|
||||
export default OssTabs;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
if (error.response.status === 404) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>{`Схема с указанным идентификатором отсутствует`}</p>
|
||||
<div className='flex justify-center'>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (error.response.status === 403) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>Владелец ограничил доступ к данной схеме</p>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
|
|
27
rsconcept/frontend/src/pages/OssPage/ProcessError.tsx
Normal file
27
rsconcept/frontend/src/pages/OssPage/ProcessError.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
'use client';
|
||||
import axios from 'axios';
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
if (error.response.status === 404) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>{`Схема с указанным идентификатором отсутствует`}</p>
|
||||
<div className='flex justify-center'>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (error.response.status === 403) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>Владелец ограничил доступ к данной схеме</p>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
|
@ -14,14 +14,6 @@ import { useConceptNavigation } from '@/context/NavigationContext';
|
|||
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||
import { IPasswordTokenData, IResetPasswordData } from '@/models/user';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
||||
return <div className='mt-6 text-sm select-text clr-text-red'>Данная ссылка не действительна</div>;
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
||||
function PasswordChangePage() {
|
||||
const router = useConceptNavigation();
|
||||
const token = useQueryStrings().get('token');
|
||||
|
@ -117,3 +109,12 @@ function PasswordChangePage() {
|
|||
}
|
||||
|
||||
export default PasswordChangePage;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
||||
return <div className='mt-6 text-sm select-text clr-text-red'>Данная ссылка не действительна</div>;
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
'use client';
|
||||
|
||||
import axios from 'axios';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
|
@ -719,41 +714,12 @@ export const RSEditState = ({
|
|||
</AnimatePresence>
|
||||
) : null}
|
||||
|
||||
{model.loading ? <Loader /> : null}
|
||||
{model.errorLoading ? (
|
||||
<ProcessError error={model.errorLoading} isArchive={model.isArchive} itemID={model.itemID} />
|
||||
) : null}
|
||||
{model.schema && !model.loading ? children : null}
|
||||
{children}
|
||||
</RSEditContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({
|
||||
error,
|
||||
isArchive,
|
||||
itemID
|
||||
}: {
|
||||
error: ErrorData;
|
||||
isArchive: boolean;
|
||||
itemID: string;
|
||||
}): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>{`Схема с указанным идентификатором ${isArchive ? 'и версией ' : ''}отсутствует`}</p>
|
||||
<div className='flex justify-center'>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
{isArchive ? <Divider vertical margins='mx-3' /> : null}
|
||||
{isArchive ? <TextURL text='Актуальная версия' href={`/rsforms/${itemID}`} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
||||
function getNextActiveOnDelete(
|
||||
activeID: ConstituentaID | undefined,
|
||||
items: IConstituenta[],
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
'use client';
|
||||
|
||||
import axios from 'axios';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { urls } from '@/app/urls';
|
||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
||||
|
@ -39,7 +44,7 @@ function RSTabs() {
|
|||
const cstQuery = query.get('active');
|
||||
|
||||
const { setNoFooter, calculateHeight } = useConceptOptions();
|
||||
const { schema, loading } = useRSForm();
|
||||
const { schema, loading, errorLoading, isArchive, itemID } = useRSForm();
|
||||
const { destroyItem } = useLibrary();
|
||||
|
||||
const [isModified, setIsModified] = useState(false);
|
||||
|
@ -233,13 +238,15 @@ function RSTabs() {
|
|||
onCreateCst={onCreateCst}
|
||||
onDeleteCst={onDeleteCst}
|
||||
>
|
||||
{loading ? <Loader /> : null}
|
||||
{errorLoading ? <ProcessError error={errorLoading} isArchive={isArchive} itemID={itemID} /> : null}
|
||||
{schema && !loading ? (
|
||||
<Tabs
|
||||
selectedIndex={activeTab}
|
||||
onSelect={onSelectTab}
|
||||
defaultFocus
|
||||
selectedTabClassName='clr-selected'
|
||||
className='flex flex-col min-w-fit mx-auto'
|
||||
className='flex flex-col mx-auto min-w-fit'
|
||||
>
|
||||
<TabList className={clsx('mx-auto w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2')}>
|
||||
<RSTabsMenu onDestroy={onDestroySchema} />
|
||||
|
@ -269,3 +276,37 @@ function RSTabs() {
|
|||
}
|
||||
|
||||
export default RSTabs;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({
|
||||
error,
|
||||
isArchive,
|
||||
itemID
|
||||
}: {
|
||||
error: ErrorData;
|
||||
isArchive: boolean;
|
||||
itemID: string;
|
||||
}): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
if (error.response.status === 404) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>{`Схема с указанным идентификатором ${isArchive ? 'и версией ' : ''}отсутствует`}</p>
|
||||
<div className='flex justify-center'>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
{isArchive ? <Divider vertical margins='mx-3' /> : null}
|
||||
{isArchive ? <TextURL text='Актуальная версия' href={`/rsforms/${itemID}`} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (error.response.status === 403) {
|
||||
return (
|
||||
<div className='p-2 text-center'>
|
||||
<p>Владелец ограничил доступ к данной схеме</p>
|
||||
<TextURL text='Библиотека' href='/library' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
|
|
|
@ -25,24 +25,6 @@ import { HelpTopic } from '@/models/miscellaneous';
|
|||
import { IUserSignupData } from '@/models/user';
|
||||
import { globals, patterns } from '@/utils/constants';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
if ('email' in error.response.data) {
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
<div className='w-full text-sm text-center select-text clr-text-red'>{error.response.data.email}.</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className='text-sm select-text clr-text-red'>
|
||||
<PrettyJson data={error.response} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
|
||||
function RegisterPage() {
|
||||
const router = useConceptNavigation();
|
||||
const { user, signup, loading, error, setError } = useAuth();
|
||||
|
@ -203,3 +185,22 @@ function RegisterPage() {
|
|||
}
|
||||
|
||||
export default RegisterPage;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
if ('email' in error.response.data) {
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
<div className='w-full text-sm text-center select-text clr-text-red'>{error.response.data.email}.</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className='text-sm select-text clr-text-red'>
|
||||
<PrettyJson data={error.response} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
|
|
|
@ -12,14 +12,6 @@ import AnimateFade from '@/components/wrap/AnimateFade';
|
|||
import { useAuth } from '@/context/AuthContext';
|
||||
import { IRequestPasswordData } from '@/models/user';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return <div className='mt-6 text-sm select-text clr-text-red'>Данный email не используется на Портале.</div>;
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
||||
function RestorePasswordPage() {
|
||||
const { requestPasswordReset, loading, error, setError } = useAuth();
|
||||
|
||||
|
@ -73,3 +65,12 @@ function RestorePasswordPage() {
|
|||
}
|
||||
|
||||
export default RestorePasswordPage;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return <div className='mt-6 text-sm select-text clr-text-red'>Данный email не используется на Портале.</div>;
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,6 @@ import { useAuth } from '@/context/AuthContext';
|
|||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { IUserUpdatePassword } from '@/models/user';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return <div className='text-sm select-text clr-text-red'>Неверно введен старый пароль</div>;
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
||||
function EditorPassword() {
|
||||
const router = useConceptNavigation();
|
||||
const { updatePassword, error, setError, loading } = useAuth();
|
||||
|
@ -109,3 +101,12 @@ function EditorPassword() {
|
|||
}
|
||||
|
||||
export default EditorPassword;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return <div className='text-sm select-text clr-text-red'>Неверно введен старый пароль</div>;
|
||||
} else {
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,18 +12,6 @@ import { useBlockNavigation } from '@/context/NavigationContext';
|
|||
import { useUserProfile } from '@/context/UserProfileContext';
|
||||
import { IUserUpdateData } from '@/models/user';
|
||||
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
if ('email' in error.response.data) {
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
<div className='text-sm select-text clr-text-red'>{error.response.data.email}.</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
|
||||
function EditorProfile() {
|
||||
const { updateUser, user, errorProcessing, processing } = useUserProfile();
|
||||
|
||||
|
@ -106,3 +94,16 @@ function EditorProfile() {
|
|||
}
|
||||
|
||||
export default EditorProfile;
|
||||
|
||||
// ====== Internals =========
|
||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
if ('email' in error.response.data) {
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
<div className='text-sm select-text clr-text-red'>{error.response.data.email}.</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <InfoError error={error} />;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user