F: Improve lazy loading and hydration

This commit is contained in:
Ivan 2025-01-29 23:18:08 +03:00
parent 33e73dfb47
commit 177b881adf
30 changed files with 118 additions and 103 deletions

View File

@ -1,30 +1,27 @@
import React from 'react';
import { createBrowserRouter } from 'react-router'; import { createBrowserRouter } from 'react-router';
import { prefetchAuth } from '@/backend/auth/useAuth';
import { prefetchLibrary } from '@/backend/library/useLibrary';
import { prefetchOSS } from '@/backend/oss/useOSS';
import { prefetchRSForm } from '@/backend/rsform/useRSForm';
import { prefetchProfile } from '@/backend/users/useProfile';
import { prefetchUsers } from '@/backend/users/useUsers';
import Loader from '@/components/ui/Loader';
import CreateItemPage from '@/pages/CreateItemPage'; import CreateItemPage from '@/pages/CreateItemPage';
import HomePage from '@/pages/HomePage'; import HomePage from '@/pages/HomePage';
import LibraryPage from '@/pages/LibraryPage';
import LoginPage from '@/pages/LoginPage'; import LoginPage from '@/pages/LoginPage';
import NotFoundPage from '@/pages/NotFoundPage'; import NotFoundPage from '@/pages/NotFoundPage';
import OssPage from '@/pages/OssPage';
import RSFormPage from '@/pages/RSFormPage';
import ApplicationLayout from './ApplicationLayout'; import ApplicationLayout from './ApplicationLayout';
import { routes } from './urls'; import { routes } from './urls';
const UserProfilePage = React.lazy(() => import('@/pages/UserProfilePage'));
const RestorePasswordPage = React.lazy(() => import('@/pages/RestorePasswordPage'));
const PasswordChangePage = React.lazy(() => import('@/pages/PasswordChangePage'));
const RegisterPage = React.lazy(() => import('@/pages/RegisterPage'));
const ManualsPage = React.lazy(() => import('@/pages/ManualsPage'));
const IconsPage = React.lazy(() => import('@/pages/IconsPage'));
const DatabaseSchemaPage = React.lazy(() => import('@/pages/DatabaseSchemaPage'));
export const Router = createBrowserRouter([ export const Router = createBrowserRouter([
{ {
path: '/', path: '/',
element: <ApplicationLayout />, element: <ApplicationLayout />,
errorElement: <NotFoundPage />, errorElement: <NotFoundPage />,
loader: prefetchAuth,
hydrateFallbackElement: <Loader />,
children: [ children: [
{ {
path: '', path: '',
@ -40,23 +37,25 @@ export const Router = createBrowserRouter([
}, },
{ {
path: routes.signup, path: routes.signup,
element: <RegisterPage /> lazy: () => import('@/pages/RegisterPage')
}, },
{ {
path: routes.profile, path: routes.profile,
element: <UserProfilePage /> loader: prefetchProfile,
lazy: () => import('@/pages/UserProfilePage')
}, },
{ {
path: routes.restore_password, path: routes.restore_password,
element: <RestorePasswordPage /> lazy: () => import('@/pages/RestorePasswordPage')
}, },
{ {
path: routes.password_change, path: routes.password_change,
element: <PasswordChangePage /> lazy: () => import('@/pages/PasswordChangePage')
}, },
{ {
path: routes.library, path: routes.library,
element: <LibraryPage /> loader: () => Promise.allSettled([prefetchLibrary(), prefetchUsers()]),
lazy: () => import('@/pages/LibraryPage')
}, },
{ {
path: routes.create_schema, path: routes.create_schema,
@ -64,24 +63,37 @@ export const Router = createBrowserRouter([
}, },
{ {
path: `${routes.rsforms}/:id`, path: `${routes.rsforms}/:id`,
element: <RSFormPage /> loader: data => prefetchRSForm(parseRSFormURL(data.params.id, data.request.url)),
lazy: () => import('@/pages/RSFormPage')
}, },
{ {
path: `${routes.oss}/:id`, path: `${routes.oss}/:id`,
element: <OssPage /> loader: data => prefetchOSS(parseOssURL(data.params.id)),
lazy: () => import('@/pages/OssPage')
}, },
{ {
path: routes.manuals, path: routes.manuals,
element: <ManualsPage /> lazy: () => import('@/pages/ManualsPage')
}, },
{ {
path: `${routes.icons}`, path: `${routes.icons}`,
element: <IconsPage /> lazy: () => import('@/pages/IconsPage')
}, },
{ {
path: `${routes.database_schema}`, path: `${routes.database_schema}`,
element: <DatabaseSchemaPage /> lazy: () => import('@/pages/DatabaseSchemaPage')
} }
] ]
} }
]); ]);
// ======= Internals =========
function parseRSFormURL(id: string | undefined, url: string) {
const params = new URLSearchParams(url.split('?')[1]);
const version = params.get('v');
return { itemID: id ? Number(id) : undefined, version: version ? Number(version) : undefined };
}
function parseOssURL(id: string | undefined) {
return { itemID: id ? Number(id) : undefined };
}

View File

@ -1,5 +1,6 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '../queryClient';
import { authApi } from './api'; import { authApi } from './api';
export function useAuth() { export function useAuth() {
@ -19,3 +20,7 @@ export function useAuthSuspense() {
}); });
return { user, isAnonymous: user.id === null }; return { user, isAnonymous: user.id === null };
} }
export function prefetchAuth() {
return queryClient.prefetchQuery(authApi.getAuthQueryOptions());
}

View File

@ -74,7 +74,7 @@ export const libraryApi = {
}, },
getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) => getLibraryQueryOptions: ({ isAdmin }: { isAdmin: boolean }) =>
queryOptions({ queryOptions({
queryKey: libraryApi.libraryListKey, queryKey: [...libraryApi.libraryListKey, isAdmin ? 'admin' : 'user'],
staleTime: DELAYS.staleMedium, staleTime: DELAYS.staleMedium,
queryFn: meta => queryFn: meta =>
axiosGet<ILibraryItem[]>({ axiosGet<ILibraryItem[]>({

View File

@ -1,22 +1,29 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { useAuthSuspense } from '@/backend/auth/useAuth'; import { useAuthSuspense } from '@/backend/auth/useAuth';
import { queryClient } from '@/backend/queryClient';
import { usePreferencesStore } from '@/stores/preferences';
import { libraryApi } from './api'; import { libraryApi } from './api';
export function useLibrarySuspense() { export function useLibrarySuspense() {
const adminMode = usePreferencesStore(state => state.adminMode);
const { user } = useAuthSuspense(); const { user } = useAuthSuspense();
const { data: items } = useSuspenseQuery({ const { data: items } = useSuspenseQuery({
...libraryApi.getLibraryQueryOptions({ isAdmin: user?.is_staff ?? false }) ...libraryApi.getLibraryQueryOptions({ isAdmin: user.is_staff && adminMode })
}); });
return { items }; return { items };
} }
export function useLibrary() { export function useLibrary() {
// NOTE: Using suspense here to avoid duplicated library data requests const adminMode = usePreferencesStore(state => state.adminMode);
const { user } = useAuthSuspense(); const { user } = useAuthSuspense();
const { data: items, isLoading } = useQuery({ const { data: items, isLoading } = useQuery({
...libraryApi.getLibraryQueryOptions({ isAdmin: user.is_staff }) ...libraryApi.getLibraryQueryOptions({ isAdmin: user.is_staff && adminMode })
}); });
return { items: items ?? [], isLoading }; return { items: items ?? [], isLoading };
} }
export function prefetchLibrary() {
return queryClient.prefetchQuery(libraryApi.getLibraryQueryOptions({ isAdmin: false }));
}

View File

@ -1,5 +1,7 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '@/backend/queryClient';
import { libraryApi } from './api'; import { libraryApi } from './api';
export function useTemplatesSuspense() { export function useTemplatesSuspense() {
@ -15,3 +17,7 @@ export function useTemplates() {
}); });
return { templates: templates ?? [] }; return { templates: templates ?? [] };
} }
export function prefetchTemplates() {
return queryClient.prefetchQuery(libraryApi.getTemplatesQueryOptions());
}

View File

@ -4,6 +4,7 @@ import { useLibrary, useLibrarySuspense } from '@/backend/library/useLibrary';
import { LibraryItemID } from '@/models/library'; import { LibraryItemID } from '@/models/library';
import { OssLoader } from '@/models/OssLoader'; import { OssLoader } from '@/models/OssLoader';
import { queryClient } from '../queryClient';
import { ossApi } from './api'; import { ossApi } from './api';
export function useOss({ itemID }: { itemID?: LibraryItemID }) { export function useOss({ itemID }: { itemID?: LibraryItemID }) {
@ -24,3 +25,10 @@ export function useOssSuspense({ itemID }: { itemID: LibraryItemID }) {
const schema = new OssLoader(data!, libraryItems).produceOSS(); const schema = new OssLoader(data!, libraryItems).produceOSS();
return { schema }; return { schema };
} }
export function prefetchOSS({ itemID }: { itemID?: LibraryItemID }) {
if (!itemID) {
return null;
}
return queryClient.prefetchQuery(ossApi.getOssQueryOptions({ itemID }));
}

View File

@ -3,6 +3,7 @@ import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { LibraryItemID, VersionID } from '@/models/library'; import { LibraryItemID, VersionID } from '@/models/library';
import { RSFormLoader } from '@/models/RSFormLoader'; import { RSFormLoader } from '@/models/RSFormLoader';
import { queryClient } from '../queryClient';
import { rsformsApi } from './api'; import { rsformsApi } from './api';
export function useRSForm({ itemID, version }: { itemID?: LibraryItemID; version?: VersionID }) { export function useRSForm({ itemID, version }: { itemID?: LibraryItemID; version?: VersionID }) {
@ -21,3 +22,10 @@ export function useRSFormSuspense({ itemID, version }: { itemID: LibraryItemID;
const schema = new RSFormLoader(data!).produceRSForm(); const schema = new RSFormLoader(data!).produceRSForm();
return { schema }; return { schema };
} }
export function prefetchRSForm({ itemID, version }: { itemID?: LibraryItemID; version?: VersionID }) {
if (!itemID) {
return null;
}
return queryClient.prefetchQuery(rsformsApi.getRSFormQueryOptions({ itemID, version }));
}

View File

@ -1,5 +1,7 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '@/backend/queryClient';
import { usersApi } from './api'; import { usersApi } from './api';
export function useProfile() { export function useProfile() {
@ -19,3 +21,7 @@ export function useProfileSuspense() {
}); });
return { profile }; return { profile };
} }
export function prefetchProfile() {
return queryClient.prefetchQuery(usersApi.getProfileQueryOptions());
}

View File

@ -1,5 +1,7 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '@/backend/queryClient';
import { usersApi } from './api'; import { usersApi } from './api';
export function useUsersSuspense() { export function useUsersSuspense() {
@ -15,3 +17,7 @@ export function useUsers() {
}); });
return { users: users ?? [] }; return { users: users ?? [] };
} }
export function prefetchUsers() {
return queryClient.prefetchQuery(usersApi.getUsersQueryOptions());
}

View File

@ -4,11 +4,6 @@ import { createRoot } from 'react-dom/client';
import App from './app'; import App from './app';
import GlobalProviders from './app/GlobalProviders'; import GlobalProviders from './app/GlobalProviders';
import { authApi } from './backend/auth/api';
import { queryClient } from './backend/queryClient';
// Prefetch auth data
queryClient.prefetchQuery(authApi.getAuthQueryOptions()).catch(console.error);
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<GlobalProviders> <GlobalProviders>

View File

@ -6,9 +6,8 @@ import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout'; import { useAppLayoutStore, useFitHeight } from '@/stores/appLayout';
import { resources } from '@/utils/constants'; import { resources } from '@/utils/constants';
function DatabaseSchemaPage() { export function Component() {
const hideFooter = useAppLayoutStore(state => state.hideFooter); const hideFooter = useAppLayoutStore(state => state.hideFooter);
const panelHeight = useFitHeight('0px'); const panelHeight = useFitHeight('0px');
useEffect(() => { useEffect(() => {
@ -26,5 +25,3 @@ function DatabaseSchemaPage() {
</div> </div>
); );
} }
export default DatabaseSchemaPage;

View File

@ -3,7 +3,7 @@
// @ts-nocheck // @ts-nocheck
import * as icons from '@/components/Icons'; import * as icons from '@/components/Icons';
export function IconsPage() { export function Component() {
const iconsList = Object.keys(icons).filter(key => key.startsWith('Icon')); const iconsList = Object.keys(icons).filter(key => key.startsWith('Icon'));
return ( return (
<div className='flex flex-col items-center px-6 py-3'> <div className='flex flex-col items-center px-6 py-3'>
@ -19,5 +19,3 @@ export function IconsPage() {
</div> </div>
); );
} }
export default IconsPage;

View File

@ -19,7 +19,7 @@ import TableLibraryItems from './TableLibraryItems';
import ToolbarSearch from './ToolbarSearch'; import ToolbarSearch from './ToolbarSearch';
import ViewSideLocation from './ViewSideLocation'; import ViewSideLocation from './ViewSideLocation';
function LibraryPage() { export function LibraryPage() {
const { items: libraryItems } = useLibrarySuspense(); const { items: libraryItems } = useLibrarySuspense();
const { renameLocation } = useRenameLocation(); const { renameLocation } = useRenameLocation();
@ -83,5 +83,3 @@ function LibraryPage() {
</> </>
); );
} }
export default LibraryPage;

View File

@ -1 +1 @@
export { default } from './LibraryPage'; export { LibraryPage as Component } from './LibraryPage';

View File

@ -9,7 +9,7 @@ import { urls } from '@/app/urls';
import { useAuthSuspense } from '@/backend/auth/useAuth'; import { useAuthSuspense } from '@/backend/auth/useAuth';
import { useLogin } from '@/backend/auth/useLogin'; import { useLogin } from '@/backend/auth/useLogin';
import ExpectedAnonymous from '@/components/ExpectedAnonymous'; import ExpectedAnonymous from '@/components/ExpectedAnonymous';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import SubmitButton from '@/components/ui/SubmitButton'; import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import TextURL from '@/components/ui/TextURL'; import TextURL from '@/components/ui/TextURL';
@ -97,7 +97,6 @@ function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
На Портале отсутствует такое сочетание имени пользователя и пароля На Портале отсутствует такое сочетание имени пользователя и пароля
</div> </div>
); );
} else {
return <InfoError error={error} />;
} }
throw error as Error;
} }

View File

@ -1,7 +1,5 @@
'use client'; 'use client';
import { useCallback } from 'react';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import useQueryStrings from '@/hooks/useQueryStrings'; import useQueryStrings from '@/hooks/useQueryStrings';
@ -12,19 +10,16 @@ import { PARAMETER } from '@/utils/constants';
import TopicsList from './TopicsList'; import TopicsList from './TopicsList';
import ViewTopic from './ViewTopic'; import ViewTopic from './ViewTopic';
function ManualsPage() { export function ManualsPage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const query = useQueryStrings(); const query = useQueryStrings();
const activeTopic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic; const activeTopic = (query.get('topic') || HelpTopic.MAIN) as HelpTopic;
const mainHeight = useMainHeight(); const mainHeight = useMainHeight();
const onSelectTopic = useCallback( function onSelectTopic(newTopic: HelpTopic) {
(newTopic: HelpTopic) => { router.push(urls.help_topic(newTopic));
router.push(urls.help_topic(newTopic)); }
},
[router]
);
if (!Object.values(HelpTopic).includes(activeTopic)) { if (!Object.values(HelpTopic).includes(activeTopic)) {
setTimeout(() => { setTimeout(() => {
@ -40,5 +35,3 @@ function ManualsPage() {
</div> </div>
); );
} }
export default ManualsPage;

View File

@ -1 +1 @@
export { default } from './ManualsPage'; export { ManualsPage as Component } from './ManualsPage';

View File

@ -6,14 +6,14 @@ import { useParams } from 'react-router';
import { useBlockNavigation, useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { useBlockNavigation, useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import TextURL from '@/components/ui/TextURL'; import TextURL from '@/components/ui/TextURL';
import { useModificationStore } from '@/stores/modification'; import { useModificationStore } from '@/stores/modification';
import { OssEditState } from './OssEditContext'; import { OssEditState } from './OssEditContext';
import OssTabs from './OssTabs'; import OssTabs from './OssTabs';
function OssPage() { export function OssPage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const params = useParams(); const params = useParams();
const itemID = params.id ? Number(params.id) : undefined; const itemID = params.id ? Number(params.id) : undefined;
@ -35,8 +35,6 @@ function OssPage() {
); );
} }
export default OssPage;
// ====== Internals ========= // ====== Internals =========
function ProcessError({ error }: { error: ErrorData }): React.ReactElement { function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response) { if (axios.isAxiosError(error) && error.response) {
@ -58,5 +56,5 @@ function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
); );
} }
} }
return <InfoError error={error} />; throw error as Error;
} }

View File

@ -1 +1 @@
export { default } from './OssPage'; export { OssPage as Component } from './OssPage';

View File

@ -8,13 +8,13 @@ import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { IResetPasswordDTO } from '@/backend/auth/api'; import { IResetPasswordDTO } from '@/backend/auth/api';
import { useResetPassword } from '@/backend/auth/useResetPassword'; import { useResetPassword } from '@/backend/auth/useResetPassword';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import Loader from '@/components/ui/Loader'; import Loader from '@/components/ui/Loader';
import SubmitButton from '@/components/ui/SubmitButton'; import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import useQueryStrings from '@/hooks/useQueryStrings'; import useQueryStrings from '@/hooks/useQueryStrings';
function PasswordChangePage() { export function Component() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const token = useQueryStrings().get('token'); const token = useQueryStrings().get('token');
@ -96,13 +96,10 @@ function PasswordChangePage() {
); );
} }
export default PasswordChangePage;
// ====== Internals ========= // ====== Internals =========
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) {
return <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данная ссылка не действительна</div>; return <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данная ссылка не действительна</div>;
} else {
return <InfoError error={error} />;
} }
throw error as Error;
} }

View File

@ -16,7 +16,7 @@ import { useModificationStore } from '@/stores/modification';
import { RSEditState, RSTabID } from './RSEditContext'; import { RSEditState, RSTabID } from './RSEditContext';
import RSTabs from './RSTabs'; import RSTabs from './RSTabs';
function RSFormPage() { export function RSFormPage() {
const router = useConceptNavigation(); const router = useConceptNavigation();
const query = useQueryStrings(); const query = useQueryStrings();
const params = useParams(); const params = useParams();
@ -33,7 +33,6 @@ function RSFormPage() {
} }
return ( return (
<ErrorBoundary <ErrorBoundary
onError={filterErrors}
FallbackComponent={({ error }) => ( FallbackComponent={({ error }) => (
<ProcessError error={error as ErrorData} isArchive={!!version} itemID={itemID} /> <ProcessError error={error as ErrorData} isArchive={!!version} itemID={itemID} />
)} )}
@ -45,16 +44,7 @@ function RSFormPage() {
); );
} }
export default RSFormPage;
// ====== Internals ========= // ====== Internals =========
const filterErrors = (error: Error) => {
if (axios.isAxiosError(error) && error.response && (error.response.status === 404 || error.response.status === 403)) {
return;
}
throw error;
};
function ProcessError({ function ProcessError({
error, error,
isArchive, isArchive,
@ -85,5 +75,5 @@ function ProcessError({
); );
} }
} }
return null; throw error as Error;
} }

View File

@ -1 +1 @@
export { default } from './RSFormPage'; export { RSFormPage as Component } from './RSFormPage';

View File

@ -8,7 +8,7 @@ import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { useSignup } from '@/backend/users/useSignup'; import { useSignup } from '@/backend/users/useSignup';
import { IconHelp } from '@/components/Icons'; import { IconHelp } from '@/components/Icons';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import Button from '@/components/ui/Button'; import Button from '@/components/ui/Button';
import Checkbox from '@/components/ui/Checkbox'; import Checkbox from '@/components/ui/Checkbox';
import FlexColumn from '@/components/ui/FlexColumn'; import FlexColumn from '@/components/ui/FlexColumn';
@ -188,5 +188,5 @@ function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
); );
} }
} }
return <InfoError error={error} />; throw error as Error;
} }

View File

@ -3,7 +3,7 @@ import ExpectedAnonymous from '@/components/ExpectedAnonymous';
import FormSignup from './FormSignup'; import FormSignup from './FormSignup';
function RegisterPage() { export function RegisterPage() {
const { user } = useAuthSuspense(); const { user } = useAuthSuspense();
if (user) { if (user) {
@ -12,5 +12,3 @@ function RegisterPage() {
return <FormSignup />; return <FormSignup />;
} }
} }
export default RegisterPage;

View File

@ -1 +1 @@
export { default } from './RegisterPage'; export { RegisterPage as Component } from './RegisterPage';

View File

@ -5,12 +5,12 @@ import clsx from 'clsx';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useRequestPasswordReset } from '@/backend/auth/useRequestPasswordReset'; import { useRequestPasswordReset } from '@/backend/auth/useRequestPasswordReset';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import SubmitButton from '@/components/ui/SubmitButton'; import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
import TextURL from '@/components/ui/TextURL'; import TextURL from '@/components/ui/TextURL';
function RestorePasswordPage() { export function Component() {
const { requestPasswordReset, isPending, error, reset } = useRequestPasswordReset(); const { requestPasswordReset, isPending, error, reset } = useRequestPasswordReset();
const [isCompleted, setIsCompleted] = useState(false); const [isCompleted, setIsCompleted] = useState(false);
@ -60,15 +60,12 @@ function RestorePasswordPage() {
} }
} }
export default RestorePasswordPage;
// ====== Internals ========= // ====== Internals =========
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) {
return ( return (
<div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данный email не используется на Портале.</div> <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данный email не используется на Портале.</div>
); );
} else {
return <InfoError error={error} />;
} }
throw error as Error;
} }

View File

@ -9,7 +9,7 @@ import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { IChangePasswordDTO } from '@/backend/auth/api'; import { IChangePasswordDTO } from '@/backend/auth/api';
import { useChangePassword } from '@/backend/auth/useChangePassword'; import { useChangePassword } from '@/backend/auth/useChangePassword';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import FlexColumn from '@/components/ui/FlexColumn'; import FlexColumn from '@/components/ui/FlexColumn';
import SubmitButton from '@/components/ui/SubmitButton'; import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
@ -97,7 +97,6 @@ export default EditorPassword;
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) {
return <div className='text-sm select-text text-warn-600'>Неверно введен старый пароль</div>; return <div className='text-sm select-text text-warn-600'>Неверно введен старый пароль</div>;
} else {
return <InfoError error={error} />;
} }
throw error as Error;
} }

View File

@ -7,7 +7,7 @@ import { useBlockNavigation } from '@/app/Navigation/NavigationContext';
import { IUpdateProfileDTO } from '@/backend/users/api'; import { IUpdateProfileDTO } from '@/backend/users/api';
import { useProfileSuspense } from '@/backend/users/useProfile'; import { useProfileSuspense } from '@/backend/users/useProfile';
import { useUpdateProfile } from '@/backend/users/useUpdateProfile'; import { useUpdateProfile } from '@/backend/users/useUpdateProfile';
import InfoError, { ErrorData } from '@/components/info/InfoError'; import { ErrorData } from '@/components/info/InfoError';
import SubmitButton from '@/components/ui/SubmitButton'; import SubmitButton from '@/components/ui/SubmitButton';
import TextInput from '@/components/ui/TextInput'; import TextInput from '@/components/ui/TextInput';
@ -99,5 +99,5 @@ function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
); );
} }
} }
return <InfoError error={error} />; throw error as Error;
} }

View File

@ -3,7 +3,7 @@ import RequireAuth from '@/components/RequireAuth';
import EditorPassword from './EditorPassword'; import EditorPassword from './EditorPassword';
import EditorProfile from './EditorProfile'; import EditorProfile from './EditorProfile';
function UserProfilePage() { export function UserProfilePage() {
return ( return (
<RequireAuth> <RequireAuth>
<div className='cc-fade-in flex flex-col py-2 mx-auto w-fit'> <div className='cc-fade-in flex flex-col py-2 mx-auto w-fit'>
@ -16,5 +16,3 @@ function UserProfilePage() {
</RequireAuth> </RequireAuth>
); );
} }
export default UserProfilePage;

View File

@ -1 +1 @@
export { default } from './UserProfilePage'; export { UserProfilePage as Component } from './UserProfilePage';