mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
F: Improve lazy loading and hydration
This commit is contained in:
parent
bb8824d2c3
commit
276b6a6be6
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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[]>({
|
||||||
|
|
|
@ -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 }));
|
||||||
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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 }));
|
||||||
|
}
|
||||||
|
|
|
@ -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 }));
|
||||||
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './LibraryPage';
|
export { LibraryPage as Component } from './LibraryPage';
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './ManualsPage';
|
export { ManualsPage as Component } from './ManualsPage';
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './OssPage';
|
export { OssPage as Component } from './OssPage';
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './RSFormPage';
|
export { RSFormPage as Component } from './RSFormPage';
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './RegisterPage';
|
export { RegisterPage as Component } from './RegisterPage';
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './UserProfilePage';
|
export { UserProfilePage as Component } from './UserProfilePage';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user