mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
R: Improve suspense and loading behavior
This commit is contained in:
parent
d182b2d34e
commit
cf2384564a
|
@ -23,7 +23,7 @@ export const useResetPassword = () => {
|
||||||
data: IResetPasswordDTO, //
|
data: IResetPasswordDTO, //
|
||||||
onSuccess?: () => void
|
onSuccess?: () => void
|
||||||
) => resetMutation.mutate(data, { onSuccess }),
|
) => resetMutation.mutate(data, { onSuccess }),
|
||||||
isPending: resetMutation.isPending,
|
isPending: resetMutation.isPending || validateMutation.isPending,
|
||||||
error: resetMutation.error,
|
error: resetMutation.error,
|
||||||
reset: resetMutation.reset
|
reset: resetMutation.reset
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI';
|
import { matchLibraryItem, matchLibraryItemLocation } from '@/models/libraryAPI';
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { useLibrary } from './useLibrary';
|
||||||
|
|
||||||
export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
||||||
const { items } = useLibrary();
|
const { items } = useLibrary();
|
||||||
const { user } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
|
|
||||||
let result = items;
|
let result = items;
|
||||||
if (!filter.folderMode && filter.head) {
|
if (!filter.folderMode && filter.head) {
|
||||||
|
@ -28,10 +28,10 @@ export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
||||||
result = result.filter(item => filter.isVisible === item.visible);
|
result = result.filter(item => filter.isVisible === item.visible);
|
||||||
}
|
}
|
||||||
if (filter.isOwned !== undefined) {
|
if (filter.isOwned !== undefined) {
|
||||||
result = result.filter(item => filter.isOwned === (item.owner === user?.id));
|
result = result.filter(item => filter.isOwned === (item.owner === user.id));
|
||||||
}
|
}
|
||||||
if (filter.isEditor !== undefined) {
|
if (filter.isEditor !== undefined) {
|
||||||
result = result.filter(item => filter.isEditor == user?.editor.includes(item.id));
|
result = result.filter(item => filter.isEditor == user.editor.includes(item.id));
|
||||||
}
|
}
|
||||||
if (filter.filterUser !== undefined) {
|
if (filter.filterUser !== undefined) {
|
||||||
result = result.filter(item => filter.filterUser === item.owner);
|
result = result.filter(item => filter.filterUser === item.owner);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useLogout } from '@/backend/auth/useLogout';
|
import { useLogout } from '@/backend/auth/useLogout';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
|
|
||||||
function ExpectedAnonymous() {
|
function ExpectedAnonymous() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
const { logout } = useLogout();
|
const { logout } = useLogout();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ function ExpectedAnonymous() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-fade-in flex flex-col items-center gap-3 py-6'>
|
<div className='cc-fade-in flex flex-col items-center gap-3 py-6'>
|
||||||
<p className='font-semibold'>{`Вы вошли в систему как ${user?.username ?? ''}`}</p>
|
<p className='font-semibold'>{`Вы вошли в систему как ${user.username}`}</p>
|
||||||
<div className='flex gap-3'>
|
<div className='flex gap-3'>
|
||||||
<TextURL text='Новая схема' href='/library/create' />
|
<TextURL text='Новая схема' href='/library/create' />
|
||||||
<span> | </span>
|
<span> | </span>
|
|
@ -1,25 +0,0 @@
|
||||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
|
||||||
import Loader from '@/components/ui/Loader';
|
|
||||||
|
|
||||||
interface DataLoaderProps {
|
|
||||||
isLoading?: boolean;
|
|
||||||
error?: ErrorData;
|
|
||||||
hasNoData?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DataLoader({ isLoading, hasNoData, error, children }: React.PropsWithChildren<DataLoaderProps>) {
|
|
||||||
if (isLoading) {
|
|
||||||
return <Loader />;
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
return <InfoError error={error} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNoData) {
|
|
||||||
return <div className='cc-fade-in w-full text-center p-1'>Данные не загружены</div>;
|
|
||||||
} else {
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DataLoader;
|
|
|
@ -5,7 +5,7 @@ import { useState } 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 { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { IRSFormCloneDTO } from '@/backend/library/api';
|
import { IRSFormCloneDTO } from '@/backend/library/api';
|
||||||
import { useCloneItem } from '@/backend/library/useCloneItem';
|
import { useCloneItem } from '@/backend/library/useCloneItem';
|
||||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
|
@ -35,7 +35,7 @@ function DlgCloneLibraryItem() {
|
||||||
state => state.props as DlgCloneLibraryItemProps
|
state => state.props as DlgCloneLibraryItemProps
|
||||||
);
|
);
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
|
|
||||||
const [title, setTitle] = useState(cloneTitle(base));
|
const [title, setTitle] = useState(cloneTitle(base));
|
||||||
const [alias, setAlias] = useState(base.alias);
|
const [alias, setAlias] = useState(base.alias);
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { Suspense, useEffect, useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
import { IInlineSynthesisDTO } from '@/backend/rsform/api';
|
import { IInlineSynthesisDTO } from '@/backend/rsform/api';
|
||||||
import { useRSForm } from '@/backend/rsform/useRSForm';
|
import { useRSForm } from '@/backend/rsform/useRSForm';
|
||||||
|
import Loader from '@/components/ui/Loader';
|
||||||
import Modal from '@/components/ui/Modal';
|
import Modal from '@/components/ui/Modal';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import { LibraryItemID } from '@/models/library';
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
@ -14,7 +15,7 @@ import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import TabConstituents from './TabConstituents';
|
import TabConstituents from './TabConstituents';
|
||||||
import TabSchema from './TabSchema';
|
import TabSource from './TabSource';
|
||||||
import TabSubstitutions from './TabSubstitutions';
|
import TabSubstitutions from './TabSubstitutions';
|
||||||
|
|
||||||
export interface DlgInlineSynthesisProps {
|
export interface DlgInlineSynthesisProps {
|
||||||
|
@ -32,20 +33,20 @@ function DlgInlineSynthesis() {
|
||||||
const { receiver, onInlineSynthesis } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
|
const { receiver, onInlineSynthesis } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
|
||||||
const [activeTab, setActiveTab] = useState(TabID.SCHEMA);
|
const [activeTab, setActiveTab] = useState(TabID.SCHEMA);
|
||||||
|
|
||||||
const [donorID, setDonorID] = useState<LibraryItemID | undefined>(undefined);
|
const [sourceID, setSourceID] = useState<LibraryItemID | undefined>(undefined);
|
||||||
const [selected, setSelected] = useState<ConstituentaID[]>([]);
|
const [selected, setSelected] = useState<ConstituentaID[]>([]);
|
||||||
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>([]);
|
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>([]);
|
||||||
|
|
||||||
const source = useRSForm({ itemID: donorID });
|
const { schema } = useRSForm({ itemID: sourceID });
|
||||||
|
|
||||||
const validated = !!source.schema && selected.length > 0;
|
const validated = selected.length > 0;
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
if (!source.schema) {
|
if (!sourceID || selected.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onInlineSynthesis({
|
onInlineSynthesis({
|
||||||
source: source.schema.id,
|
source: sourceID,
|
||||||
receiver: receiver.id,
|
receiver: receiver.id,
|
||||||
items: selected,
|
items: selected,
|
||||||
substitutions: substitutions
|
substitutions: substitutions
|
||||||
|
@ -53,9 +54,16 @@ function DlgInlineSynthesis() {
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelected(source.schema ? source.schema.items.map(cst => cst.id) : []);
|
if (schema) {
|
||||||
|
setSelected(schema.items.map(cst => cst.id));
|
||||||
|
}
|
||||||
|
}, [schema, setSelected]);
|
||||||
|
|
||||||
|
function handleSetSource(schemaID: LibraryItemID) {
|
||||||
|
setSourceID(schemaID);
|
||||||
|
setSelected([]);
|
||||||
setSubstitutions([]);
|
setSubstitutions([]);
|
||||||
}, [source.schema]);
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -73,32 +81,44 @@ function DlgInlineSynthesis() {
|
||||||
>
|
>
|
||||||
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none', 'bg-prim-200')}>
|
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none', 'bg-prim-200')}>
|
||||||
<TabLabel label='Схема' title='Источник конституент' className='w-[8rem]' />
|
<TabLabel label='Схема' title='Источник конституент' className='w-[8rem]' />
|
||||||
<TabLabel label='Содержание' title='Перечень конституент' className='w-[8rem]' />
|
<TabLabel
|
||||||
<TabLabel label='Отождествления' title='Таблица отождествлений' className='w-[8rem]' />
|
label='Содержание'
|
||||||
|
title={!sourceID ? 'Выберите схему' : 'Перечень конституент'}
|
||||||
|
className='w-[8rem]'
|
||||||
|
disabled={!sourceID}
|
||||||
|
/>
|
||||||
|
<TabLabel
|
||||||
|
label='Отождествления'
|
||||||
|
title={!sourceID ? 'Выберите схему' : 'Таблица отождествлений'}
|
||||||
|
className='w-[8rem]'
|
||||||
|
disabled={!sourceID}
|
||||||
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabSchema selected={donorID} setSelected={setDonorID} receiver={receiver} />
|
<TabSource selected={sourceID} setSelected={handleSetSource} receiver={receiver} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabConstituents
|
{!!sourceID ? (
|
||||||
schema={source.schema}
|
<Suspense fallback={<Loader />}>
|
||||||
loading={source.isLoading}
|
<TabConstituents itemID={sourceID} selected={selected} setSelected={setSelected} />
|
||||||
selected={selected}
|
</Suspense>
|
||||||
setSelected={setSelected}
|
) : null}
|
||||||
/>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabSubstitutions
|
{!!sourceID ? (
|
||||||
receiver={receiver}
|
<Suspense fallback={<Loader />}>
|
||||||
source={source.schema}
|
<TabSubstitutions
|
||||||
selected={selected}
|
sourceID={sourceID}
|
||||||
loading={source.isLoading}
|
receiver={receiver}
|
||||||
substitutions={substitutions}
|
selected={selected}
|
||||||
setSubstitutions={setSubstitutions}
|
substitutions={substitutions}
|
||||||
/>
|
setSubstitutions={setSubstitutions}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
) : null}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -1,33 +1,29 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { useRSFormSuspense } from '@/backend/rsform/useRSForm';
|
||||||
import PickMultiConstituenta from '@/components/select/PickMultiConstituenta';
|
import PickMultiConstituenta from '@/components/select/PickMultiConstituenta';
|
||||||
import DataLoader from '@/components/wrap/DataLoader';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
import { ConstituentaID } from '@/models/rsform';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
interface TabConstituentsProps {
|
interface TabConstituentsProps {
|
||||||
schema?: IRSForm;
|
itemID: LibraryItemID;
|
||||||
loading?: boolean;
|
|
||||||
error?: ErrorData;
|
|
||||||
selected: ConstituentaID[];
|
selected: ConstituentaID[];
|
||||||
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
|
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabConstituents({ schema, error, loading, selected, setSelected }: TabConstituentsProps) {
|
function TabConstituents({ itemID, selected, setSelected }: TabConstituentsProps) {
|
||||||
|
const { schema } = useRSFormSuspense({ itemID });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataLoader isLoading={loading} error={error} hasNoData={!schema}>
|
<PickMultiConstituenta
|
||||||
{schema ? (
|
schema={schema}
|
||||||
<PickMultiConstituenta
|
data={schema.items}
|
||||||
schema={schema}
|
rows={13}
|
||||||
data={schema.items}
|
prefixID={prefixes.cst_inline_synth_list}
|
||||||
rows={13}
|
selected={selected}
|
||||||
prefixID={prefixes.cst_inline_synth_list}
|
setSelected={setSelected}
|
||||||
selected={selected}
|
/>
|
||||||
setSelected={setSelected}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</DataLoader>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,13 @@ import { LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
import { IRSForm } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { sortItemsForInlineSynthesis } from '@/models/rsformAPI';
|
import { sortItemsForInlineSynthesis } from '@/models/rsformAPI';
|
||||||
|
|
||||||
interface TabSchemaProps {
|
interface TabSourceProps {
|
||||||
selected?: LibraryItemID;
|
selected?: LibraryItemID;
|
||||||
setSelected: (newValue: LibraryItemID) => void;
|
setSelected: (newValue: LibraryItemID) => void;
|
||||||
receiver: IRSForm;
|
receiver: IRSForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabSchema({ selected, receiver, setSelected }: TabSchemaProps) {
|
function TabSource({ selected, receiver, setSelected }: TabSourceProps) {
|
||||||
const { items: libraryItems } = useLibrary();
|
const { items: libraryItems } = useLibrary();
|
||||||
const selectedInfo = libraryItems.find(item => item.id === selected);
|
const selectedInfo = libraryItems.find(item => item.id === selected);
|
||||||
const sortedItems = sortItemsForInlineSynthesis(receiver, libraryItems);
|
const sortedItems = sortItemsForInlineSynthesis(receiver, libraryItems);
|
||||||
|
@ -43,4 +43,4 @@ function TabSchema({ selected, receiver, setSelected }: TabSchemaProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabSchema;
|
export default TabSource;
|
|
@ -1,48 +1,34 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { useRSFormSuspense } from '@/backend/rsform/useRSForm';
|
||||||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||||
import DataLoader from '@/components/wrap/DataLoader';
|
import { LibraryItemID } from '@/models/library';
|
||||||
import { ICstSubstitute } from '@/models/oss';
|
import { ICstSubstitute } from '@/models/oss';
|
||||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
interface TabSubstitutionsProps {
|
interface TabSubstitutionsProps {
|
||||||
receiver?: IRSForm;
|
receiver: IRSForm;
|
||||||
source?: IRSForm;
|
sourceID: LibraryItemID;
|
||||||
selected: ConstituentaID[];
|
selected: ConstituentaID[];
|
||||||
|
|
||||||
loading?: boolean;
|
|
||||||
error?: ErrorData;
|
|
||||||
|
|
||||||
substitutions: ICstSubstitute[];
|
substitutions: ICstSubstitute[];
|
||||||
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabSubstitutions({
|
function TabSubstitutions({ sourceID, receiver, selected, substitutions, setSubstitutions }: TabSubstitutionsProps) {
|
||||||
source,
|
const { schema: source } = useRSFormSuspense({ itemID: sourceID });
|
||||||
receiver,
|
|
||||||
selected,
|
|
||||||
|
|
||||||
error,
|
|
||||||
loading,
|
|
||||||
|
|
||||||
substitutions,
|
|
||||||
setSubstitutions
|
|
||||||
}: TabSubstitutionsProps) {
|
|
||||||
const schemas = [...(source ? [source] : []), ...(receiver ? [receiver] : [])];
|
const schemas = [...(source ? [source] : []), ...(receiver ? [receiver] : [])];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataLoader isLoading={loading} error={error} hasNoData={!source}>
|
<PickSubstitutions
|
||||||
<PickSubstitutions
|
substitutions={substitutions}
|
||||||
substitutions={substitutions}
|
setSubstitutions={setSubstitutions}
|
||||||
setSubstitutions={setSubstitutions}
|
rows={10}
|
||||||
rows={10}
|
prefixID={prefixes.cst_inline_synth_substitutes}
|
||||||
prefixID={prefixes.cst_inline_synth_substitutes}
|
schemas={schemas}
|
||||||
schemas={schemas}
|
filter={cst => cst.id !== source?.id || selected.includes(cst.id)}
|
||||||
filter={cst => cst.id !== source?.id || selected.includes(cst.id)}
|
/>
|
||||||
/>
|
|
||||||
</DataLoader>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { useRSForm } from '@/backend/rsform/useRSForm';
|
||||||
import { RelocateUpIcon } from '@/components/DomainIcons';
|
import { RelocateUpIcon } from '@/components/DomainIcons';
|
||||||
import PickMultiConstituenta from '@/components/select/PickMultiConstituenta';
|
import PickMultiConstituenta from '@/components/select/PickMultiConstituenta';
|
||||||
import SelectLibraryItem from '@/components/select/SelectLibraryItem';
|
import SelectLibraryItem from '@/components/select/SelectLibraryItem';
|
||||||
|
import Loader from '@/components/ui/Loader';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import Modal from '@/components/ui/Modal';
|
import Modal from '@/components/ui/Modal';
|
||||||
import DataLoader from '@/components/wrap/DataLoader';
|
|
||||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { IOperation, IOperationSchema } from '@/models/oss';
|
import { IOperation, IOperationSchema } from '@/models/oss';
|
||||||
|
@ -119,19 +119,18 @@ function DlgRelocateConstituents() {
|
||||||
onSelectValue={handleSelectDestination}
|
onSelectValue={handleSelectDestination}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DataLoader isLoading={sourceData.isLoading} error={sourceData.error}>
|
{sourceData.isLoading ? <Loader /> : null}
|
||||||
{sourceData.schema ? (
|
{!sourceData.isLoading && sourceData.schema ? (
|
||||||
<PickMultiConstituenta
|
<PickMultiConstituenta
|
||||||
noBorder
|
noBorder
|
||||||
schema={sourceData.schema}
|
schema={sourceData.schema}
|
||||||
data={filteredConstituents}
|
data={filteredConstituents}
|
||||||
rows={12}
|
rows={12}
|
||||||
prefixID={prefixes.dlg_cst_constituents_list}
|
prefixID={prefixes.dlg_cst_constituents_list}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
setSelected={setSelected}
|
setSelected={setSelected}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</DataLoader>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import RequireAuth from '@/components/wrap/RequireAuth';
|
import RequireAuth from '@/components/RequireAuth';
|
||||||
|
|
||||||
import FormCreateItem from './FormCreateItem';
|
import FormCreateItem from './FormCreateItem';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useCallback, useEffect, useRef, useState } 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 { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { ILibraryCreateDTO } from '@/backend/library/api';
|
import { ILibraryCreateDTO } from '@/backend/library/api';
|
||||||
import { useCreateItem } from '@/backend/library/useCreateItem';
|
import { useCreateItem } from '@/backend/library/useCreateItem';
|
||||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
|
@ -29,7 +29,7 @@ import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||||
|
|
||||||
function FormCreateItem() {
|
function FormCreateItem() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
const { createItem, isPending, error, reset } = useCreateItem();
|
const { createItem, isPending, error, reset } = useCreateItem();
|
||||||
|
|
||||||
const searchLocation = useLibrarySearchStore(state => state.location);
|
const searchLocation = useLibrarySearchStore(state => state.location);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import { useLibrary } from '@/backend/library/useLibrary';
|
import { useLibrary } from '@/backend/library/useLibrary';
|
||||||
import { SubfoldersIcon } from '@/components/DomainIcons';
|
import { SubfoldersIcon } from '@/components/DomainIcons';
|
||||||
import { IconFolderEdit, IconFolderTree } from '@/components/Icons';
|
import { IconFolderEdit, IconFolderTree } from '@/components/Icons';
|
||||||
|
@ -23,7 +23,7 @@ interface ViewSideLocationProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) {
|
function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) {
|
||||||
const { user, isAnonymous } = useAuth();
|
const { user, isAnonymous } = useAuthSuspense();
|
||||||
const { items } = useLibrary();
|
const { items } = useLibrary();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@ import { useEffect, useState } 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 { useAuth } 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 InfoError, { ErrorData } from '@/components/info/InfoError';
|
import InfoError, { 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';
|
||||||
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
|
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
import { resources } from '@/utils/constants';
|
import { resources } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ function LoginPage() {
|
||||||
const query = useQueryStrings();
|
const query = useQueryStrings();
|
||||||
const userQuery = query.get('username');
|
const userQuery = query.get('username');
|
||||||
|
|
||||||
const { isAnonymous } = useAuth();
|
const { isAnonymous } = useAuthSuspense();
|
||||||
const { login, isPending, error, reset } = useLogin();
|
const { login, isPending, error, reset } = useLogin();
|
||||||
|
|
||||||
const [username, setUsername] = useState(userQuery || '');
|
const [username, setUsername] = useState(userQuery || '');
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import SelectLocationContext from '@/components/select/SelectLocationContext';
|
import SelectLocationContext from '@/components/select/SelectLocationContext';
|
||||||
import SelectLocationHead from '@/components/select/SelectLocationHead';
|
import SelectLocationHead from '@/components/select/SelectLocationHead';
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
|
@ -21,7 +21,7 @@ export interface DlgChangeLocationProps {
|
||||||
|
|
||||||
function DlgChangeLocation() {
|
function DlgChangeLocation() {
|
||||||
const { initial, onChangeLocation } = useDialogsStore(state => state.props as DlgChangeLocationProps);
|
const { initial, onChangeLocation } = useDialogsStore(state => state.props as DlgChangeLocationProps);
|
||||||
const { user } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
const [head, setHead] = useState<LocationHead>(initial.substring(0, 2) as LocationHead);
|
const [head, setHead] = useState<LocationHead>(initial.substring(0, 2) as LocationHead);
|
||||||
const [body, setBody] = useState<string>(initial.substring(3));
|
const [body, setBody] = useState<string>(initial.substring(3));
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ 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 InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||||
|
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 DataLoader from '@/components/wrap/DataLoader';
|
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
|
|
||||||
function PasswordChangePage() {
|
function PasswordChangePage() {
|
||||||
|
@ -51,43 +51,48 @@ function PasswordChangePage() {
|
||||||
validateToken({ token: token ?? '' }, () => setIsTokenValid(true));
|
validateToken({ token: token ?? '' }, () => setIsTokenValid(true));
|
||||||
}, [token, validateToken]);
|
}, [token, validateToken]);
|
||||||
|
|
||||||
return (
|
if (error) {
|
||||||
<DataLoader isLoading={isPending} hasNoData={!isTokenValid}>
|
return <ProcessError error={error} />;
|
||||||
<form className={clsx('cc-fade-in cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
}
|
||||||
<TextInput
|
|
||||||
id='new_password'
|
|
||||||
type='password'
|
|
||||||
label='Новый пароль'
|
|
||||||
autoComplete='new-password'
|
|
||||||
allowEnter
|
|
||||||
colors={passwordColor}
|
|
||||||
value={newPassword}
|
|
||||||
onChange={event => {
|
|
||||||
setNewPassword(event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
id='new_password_repeat'
|
|
||||||
type='password'
|
|
||||||
label='Повторите новый'
|
|
||||||
autoComplete='new-password'
|
|
||||||
allowEnter
|
|
||||||
colors={passwordColor}
|
|
||||||
value={newPasswordRepeat}
|
|
||||||
onChange={event => {
|
|
||||||
setNewPasswordRepeat(event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SubmitButton
|
if (isPending || !isTokenValid) {
|
||||||
text='Установить пароль'
|
return <Loader />;
|
||||||
className='self-center w-[12rem] mt-3'
|
}
|
||||||
loading={isPending}
|
|
||||||
disabled={!canSubmit}
|
return (
|
||||||
/>
|
<form className={clsx('cc-fade-in cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
||||||
{error ? <ProcessError error={error} /> : null}
|
<TextInput
|
||||||
</form>
|
id='new_password'
|
||||||
</DataLoader>
|
type='password'
|
||||||
|
label='Новый пароль'
|
||||||
|
autoComplete='new-password'
|
||||||
|
allowEnter
|
||||||
|
colors={passwordColor}
|
||||||
|
value={newPassword}
|
||||||
|
onChange={event => {
|
||||||
|
setNewPassword(event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
id='new_password_repeat'
|
||||||
|
type='password'
|
||||||
|
label='Повторите новый'
|
||||||
|
autoComplete='new-password'
|
||||||
|
allowEnter
|
||||||
|
colors={passwordColor}
|
||||||
|
value={newPasswordRepeat}
|
||||||
|
onChange={event => {
|
||||||
|
setNewPasswordRepeat(event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SubmitButton
|
||||||
|
text='Установить пароль'
|
||||||
|
className='self-center w-[12rem] mt-3'
|
||||||
|
loading={isPending}
|
||||||
|
disabled={!canSubmit}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
import { useAuth } from '@/backend/auth/useAuth';
|
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||||
import Loader from '@/components/ui/Loader';
|
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
||||||
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
|
|
||||||
|
|
||||||
import FormSignup from './FormSignup';
|
import FormSignup from './FormSignup';
|
||||||
|
|
||||||
function RegisterPage() {
|
function RegisterPage() {
|
||||||
const { user, isLoading } = useAuth();
|
const { user } = useAuthSuspense();
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <Loader />;
|
|
||||||
}
|
|
||||||
if (user) {
|
if (user) {
|
||||||
return <ExpectedAnonymous />;
|
return <ExpectedAnonymous />;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import { Suspense } from 'react';
|
import RequireAuth from '@/components/RequireAuth';
|
||||||
|
|
||||||
import Loader from '@/components/ui/Loader';
|
|
||||||
import RequireAuth from '@/components/wrap/RequireAuth';
|
|
||||||
|
|
||||||
import EditorPassword from './EditorPassword';
|
import EditorPassword from './EditorPassword';
|
||||||
import EditorProfile from './EditorProfile';
|
import EditorProfile from './EditorProfile';
|
||||||
|
@ -9,15 +6,13 @@ import EditorProfile from './EditorProfile';
|
||||||
function UserProfilePage() {
|
function UserProfilePage() {
|
||||||
return (
|
return (
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<Suspense fallback={<Loader />}>
|
<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'>
|
<h1 className='mb-2 select-none'>Учетные данные пользователя</h1>
|
||||||
<h1 className='mb-2 select-none'>Учетные данные пользователя</h1>
|
<div className='flex py-2'>
|
||||||
<div className='flex py-2'>
|
<EditorProfile />
|
||||||
<EditorProfile />
|
<EditorPassword />
|
||||||
<EditorPassword />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</div>
|
||||||
</RequireAuth>
|
</RequireAuth>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user