mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Improve RSForm and Constituenta UI
includeing RSForm list filtering
This commit is contained in:
parent
db547b9f61
commit
555784b0b1
|
@ -3,6 +3,7 @@ import { config } from './constants'
|
||||||
import { ErrorInfo } from './components/BackendError'
|
import { ErrorInfo } from './components/BackendError'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { ICurrentUser, IRSForm, IUserInfo, IUserProfile } from './models'
|
import { ICurrentUser, IRSForm, IUserInfo, IUserProfile } from './models'
|
||||||
|
import { FilterType, RSFormsFilter } from './hooks/useRSForms'
|
||||||
|
|
||||||
export type BackendCallback = (response: AxiosResponse) => void;
|
export type BackendCallback = (response: AxiosResponse) => void;
|
||||||
|
|
||||||
|
@ -69,10 +70,17 @@ export async function getActiveUsers(request?: IFrontRequest) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRSForms(request?: IFrontRequest) {
|
export async function getRSForms(filter: RSFormsFilter, request?: IFrontRequest) {
|
||||||
|
let endpoint: string = ''
|
||||||
|
if (filter.type === FilterType.PERSONAL) {
|
||||||
|
endpoint = `${config.url.BASE}rsforms?owner=${filter.data!}`
|
||||||
|
} else {
|
||||||
|
endpoint = `${config.url.BASE}rsforms?is_common=true`
|
||||||
|
}
|
||||||
|
|
||||||
AxiosGet<IRSForm[]>({
|
AxiosGet<IRSForm[]>({
|
||||||
title: `RSForms list`,
|
title: `RSForms list`,
|
||||||
endpoint: `${config.url.BASE}rsforms/`,
|
endpoint: endpoint,
|
||||||
request: request
|
request: request
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ interface CheckboxProps {
|
||||||
required?: boolean
|
required?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
widthClass?: string
|
widthClass?: string
|
||||||
value?: any
|
value?: boolean
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ function Checkbox({id, required, disabled, label, widthClass='w-full', value, on
|
||||||
className='relative cursor-pointer peer w-4 h-4 shrink-0 mt-0.5 bg-white border rounded-sm appearance-none dark:bg-gray-900 checked:bg-blue-700 dark:checked:bg-orange-500'
|
className='relative cursor-pointer peer w-4 h-4 shrink-0 mt-0.5 bg-white border rounded-sm appearance-none dark:bg-gray-900 checked:bg-blue-700 dark:checked:bg-orange-500'
|
||||||
required={required}
|
required={required}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={value}
|
checked={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
interface SubmitButtonProps {
|
interface SubmitButtonProps {
|
||||||
text: string
|
text: string
|
||||||
loading: boolean
|
loading?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ function TextInput({id, type, required, label, disabled, placeholder, widthClass
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>
|
/>
|
||||||
<input id={id}
|
<input id={id}
|
||||||
className={'px-3 py-2 mt-2 leading-tight border shadow dark:bg-gray-800 '+ widthClass}
|
className={'px-3 py-2 mt-2 leading-tight border shadow dark:bg-gray-800 truncate hover:text-clip '+ widthClass}
|
||||||
required={required}
|
required={required}
|
||||||
type={type}
|
type={type}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
|
|
@ -10,7 +10,7 @@ function UserTools() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateMyWork = () => {
|
const navigateMyWork = () => {
|
||||||
navigate('/rsforms?filter=owned');
|
navigate('/rsforms?filter=personal');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -14,7 +14,7 @@ interface IRSFormContext {
|
||||||
isEditable: boolean
|
isEditable: boolean
|
||||||
isClaimable: boolean
|
isClaimable: boolean
|
||||||
|
|
||||||
setActive: (cst: IConstituenta) => void
|
setActive: (cst: IConstituenta | undefined) => void
|
||||||
reload: () => void
|
reload: () => void
|
||||||
upload: (data: any, callback?: BackendCallback) => void
|
upload: (data: any, callback?: BackendCallback) => void
|
||||||
destroy: (callback: BackendCallback) => void
|
destroy: (callback: BackendCallback) => void
|
||||||
|
@ -51,12 +51,6 @@ export const RSFormState = ({ id, children }: RSFormStateProps) => {
|
||||||
const isEditable = useMemo(() => (user?.id === schema?.owner || user?.is_staff || false), [user, schema]);
|
const isEditable = useMemo(() => (user?.id === schema?.owner || user?.is_staff || false), [user, schema]);
|
||||||
const isClaimable = useMemo(() => (user?.id !== schema?.owner || false), [user, schema]);
|
const isClaimable = useMemo(() => (user?.id !== schema?.owner || false), [user, schema]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (schema?.items && schema?.items.length > 0) {
|
|
||||||
setActive(schema?.items[0]);
|
|
||||||
}
|
|
||||||
}, [schema])
|
|
||||||
|
|
||||||
async function upload(data: any, callback?: BackendCallback) {
|
async function upload(data: any, callback?: BackendCallback) {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
patchRSForm(id, {
|
patchRSForm(id, {
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { IRSForm } from '../models'
|
import { IRSForm } from '../models'
|
||||||
import { ErrorInfo } from '../components/BackendError';
|
import { ErrorInfo } from '../components/BackendError';
|
||||||
import { getRSForms } from '../backendAPI';
|
import { getRSForms } from '../backendAPI';
|
||||||
|
|
||||||
|
export enum FilterType {
|
||||||
|
PERSONAL = 'personal',
|
||||||
|
COMMON = 'common'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RSFormsFilter {
|
||||||
|
type: FilterType
|
||||||
|
data?: any
|
||||||
|
}
|
||||||
|
|
||||||
export function useRSForms() {
|
export function useRSForms() {
|
||||||
const [rsforms, setRSForms] = useState<IRSForm[]>([]);
|
const [rsforms, setRSForms] = useState<IRSForm[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<ErrorInfo>(undefined);
|
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const loadList = useCallback(async (filter: RSFormsFilter) => {
|
||||||
getRSForms({
|
getRSForms(filter, {
|
||||||
showError: true,
|
showError: true,
|
||||||
setLoading: setLoading,
|
setLoading: setLoading,
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
|
@ -17,9 +27,5 @@ export function useRSForms() {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
return { rsforms, error, loading, loadList };
|
||||||
fetchData();
|
|
||||||
}, [fetchData])
|
|
||||||
|
|
||||||
return { rsforms, error, loading };
|
|
||||||
}
|
}
|
|
@ -3,7 +3,6 @@ import { IUserProfile } from '../models'
|
||||||
import { ErrorInfo } from '../components/BackendError'
|
import { ErrorInfo } from '../components/BackendError'
|
||||||
import { getProfile } from '../backendAPI'
|
import { getProfile } from '../backendAPI'
|
||||||
|
|
||||||
|
|
||||||
export function useUserProfile() {
|
export function useUserProfile() {
|
||||||
const [user, setUser] = useState<IUserProfile | undefined>(undefined);
|
const [user, setUser] = useState<IUserProfile | undefined>(undefined);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
|
@ -68,7 +68,7 @@ export enum ParsingStatus {
|
||||||
// Constituenta data
|
// Constituenta data
|
||||||
export interface IConstituenta {
|
export interface IConstituenta {
|
||||||
entityUID: number
|
entityUID: number
|
||||||
alias: boolean
|
alias: string
|
||||||
cstType: CstType
|
cstType: CstType
|
||||||
convention?: string
|
convention?: string
|
||||||
term?: {
|
term?: {
|
||||||
|
@ -140,3 +140,16 @@ export function GetErrLabel(cst: IConstituenta) {
|
||||||
}
|
}
|
||||||
return 'ОК';
|
return 'ОК';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetCstTypeLabel(type: CstType) {
|
||||||
|
switch(type) {
|
||||||
|
case CstType.BASE: return 'Базисное множество';
|
||||||
|
case CstType.CONSTANT: return 'Константное множество';
|
||||||
|
case CstType.STRUCTURED: return 'Родовая структура';
|
||||||
|
case CstType.AXIOM: return 'Аксиома';
|
||||||
|
case CstType.TERM: return 'Терм';
|
||||||
|
case CstType.FUNCTION: return 'Терм-функция';
|
||||||
|
case CstType.PREDICATE: return 'Предикат-функция';
|
||||||
|
case CstType.THEOREM: return 'Теорема';
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ function LoginPage() {
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
login(username, password, () => { navigate('/rsforms?filter=owned'); });
|
login(username, password, () => { navigate('/rsforms?filter=personal'); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ function LoginPage() {
|
||||||
|
|
||||||
<div className='flex items-center justify-between mt-4'>
|
<div className='flex items-center justify-between mt-4'>
|
||||||
<SubmitButton text='Вход' loading={loading}/>
|
<SubmitButton text='Вход' loading={loading}/>
|
||||||
<TextURL text='Восстановить пароль...' href='restore-password' />
|
<TextURL text='Восстановить пароль...' href='/restore-password' />
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-2'>
|
<div className='mt-2'>
|
||||||
<TextURL text='Нет аккаунта? Зарегистрируйтесь...' href='/signup' />
|
<TextURL text='Нет аккаунта? Зарегистрируйтесь...' href='/signup' />
|
||||||
|
|
|
@ -80,7 +80,7 @@ function RSFormCreatePage() {
|
||||||
/>
|
/>
|
||||||
<Checkbox id='common' label='Общедоступная схема'
|
<Checkbox id='common' label='Общедоступная схема'
|
||||||
value={common}
|
value={common}
|
||||||
onChange={event => setCommon(event.target.value === 'true')}
|
onChange={event => setCommon(event.target.checked)}
|
||||||
/>
|
/>
|
||||||
<FileInput id='trs' label='Загрузить *.trs'
|
<FileInput id='trs' label='Загрузить *.trs'
|
||||||
acceptType='.trs'
|
acceptType='.trs'
|
||||||
|
|
|
@ -1,14 +1,122 @@
|
||||||
import Card from '../../components/Common/Card';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import PrettyJson from '../../components/Common/PrettyJSON';
|
import PrettyJson from '../../components/Common/PrettyJSON';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
import { GetCstTypeLabel } from '../../models';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import TextArea from '../../components/Common/TextArea';
|
||||||
|
import ExpressionEditor from './ExpressionEditor';
|
||||||
|
import SubmitButton from '../../components/Common/SubmitButton';
|
||||||
|
|
||||||
function ConstituentEditor() {
|
function ConstituentEditor() {
|
||||||
const { active } = useRSForm();
|
const { active, schema, setActive, isEditable } = useRSForm();
|
||||||
|
|
||||||
|
const [alias, setAlias] = useState('');
|
||||||
|
const [type, setType] = useState('');
|
||||||
|
const [term, setTerm] = useState('');
|
||||||
|
const [textDefinition, setTextDefinition] = useState('');
|
||||||
|
const [expression, setExpression] = useState('');
|
||||||
|
const [convention, setConvention] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!active && schema?.items && schema?.items.length > 0) {
|
||||||
|
setActive(schema?.items[0]);
|
||||||
|
}
|
||||||
|
}, [schema, setActive, active])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (active) {
|
||||||
|
setAlias(active.alias);
|
||||||
|
setType(GetCstTypeLabel(active.cstType));
|
||||||
|
setTerm(active.term?.raw || '');
|
||||||
|
setTextDefinition(active.definition?.text?.raw || '');
|
||||||
|
setExpression(active.definition?.formal || '');
|
||||||
|
}
|
||||||
|
}, [active]);
|
||||||
|
|
||||||
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
// if (!processing) {
|
||||||
|
// const data = {
|
||||||
|
// 'title': title,
|
||||||
|
// 'alias': alias,
|
||||||
|
// 'comment': comment,
|
||||||
|
// 'is_common': common,
|
||||||
|
// };
|
||||||
|
// upload(data, () => {
|
||||||
|
// toast.success('Изменения сохранены');
|
||||||
|
// reload();
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRename = useCallback(() => {
|
||||||
|
toast.info('Переименование в разработке');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleChangeType = useCallback(() => {
|
||||||
|
toast.info('Изменение типа в разработке');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<div className='flex items-start w-full gap-2'>
|
||||||
{active && <PrettyJson data={active}/>}
|
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] px-4 py-2 border'>
|
||||||
</Card>
|
<div className='flex items-center justify-between gap-1'>
|
||||||
|
<span className='mr-12'>
|
||||||
|
<label
|
||||||
|
title='Переименовать конституенту'
|
||||||
|
className='font-semibold underline cursor-pointer'
|
||||||
|
onClick={handleRename}
|
||||||
|
>
|
||||||
|
ID
|
||||||
|
</label>
|
||||||
|
<b className='ml-2'>{alias}</b>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<label
|
||||||
|
title='Изменить тип конституенты'
|
||||||
|
className='font-semibold underline cursor-pointer'
|
||||||
|
onClick={handleChangeType}
|
||||||
|
>
|
||||||
|
Тип
|
||||||
|
</label>
|
||||||
|
<span className='ml-2'>{type}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<TextArea id='term' label='Термин'
|
||||||
|
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
||||||
|
rows={2}
|
||||||
|
value={term}
|
||||||
|
disabled={!isEditable}
|
||||||
|
onChange={event => setTerm(event.target.value)}
|
||||||
|
/>
|
||||||
|
<ExpressionEditor id='expression' label='Формальное выражение'
|
||||||
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||||
|
value={expression}
|
||||||
|
disabled={!isEditable}
|
||||||
|
onChange={event => setExpression(event.target.value)}
|
||||||
|
/>
|
||||||
|
<TextArea id='definition' label='Текстовое определение'
|
||||||
|
placeholder='Лингвистическая интерпретация формального выражения'
|
||||||
|
rows={4}
|
||||||
|
value={textDefinition}
|
||||||
|
disabled={!isEditable}
|
||||||
|
onChange={event => setTextDefinition(event.target.value)}
|
||||||
|
/>
|
||||||
|
<TextArea id='convention' label='Конвенция / Комментарий'
|
||||||
|
placeholder='Договоренность об интерпретации неопределяемых понятий или комментарий к производному понятию'
|
||||||
|
rows={4}
|
||||||
|
value={convention}
|
||||||
|
disabled={!isEditable}
|
||||||
|
onChange={event => setConvention(event.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='flex items-center justify-between gap-1 py-2 mt-2'>
|
||||||
|
<SubmitButton text='Сохранить изменения' disabled={!isEditable} />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<PrettyJson data={active || ''} />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
35
rsconcept/frontend/src/pages/RSFormPage/ExpressionEditor.tsx
Normal file
35
rsconcept/frontend/src/pages/RSFormPage/ExpressionEditor.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import Label from '../../components/Common/Label';
|
||||||
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
|
||||||
|
interface ExpressionEditorProps {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
disabled?: boolean
|
||||||
|
placeholder?: string
|
||||||
|
value: any
|
||||||
|
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExpressionEditor({id, label, disabled, placeholder, value, onChange}: ExpressionEditorProps) {
|
||||||
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col items-start [&:not(:first-child)]:mt-3 w-full'>
|
||||||
|
<Label
|
||||||
|
text={label}
|
||||||
|
required={false}
|
||||||
|
htmlFor={id}
|
||||||
|
/>
|
||||||
|
<textarea id='comment'
|
||||||
|
className='w-full px-3 py-2 mt-2 leading-tight border shadow dark:bg-gray-800'
|
||||||
|
rows={6}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExpressionEditor;
|
|
@ -49,7 +49,7 @@ function RSFormCard() {
|
||||||
if (window.confirm('Вы уверены, что хотите удалить данную схему?')) {
|
if (window.confirm('Вы уверены, что хотите удалить данную схему?')) {
|
||||||
destroy(() => {
|
destroy(() => {
|
||||||
toast.success('Схема удалена');
|
toast.success('Схема удалена');
|
||||||
navigate('/rsforms?filter=owned');
|
navigate('/rsforms?filter=personal');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [destroy, navigate]);
|
}, [destroy, navigate]);
|
||||||
|
@ -91,7 +91,7 @@ function RSFormCard() {
|
||||||
<Checkbox id='common' label='Общедоступная схема'
|
<Checkbox id='common' label='Общедоступная схема'
|
||||||
value={common}
|
value={common}
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
onChange={event => setCommon(event.target.value === 'true')}
|
onChange={event => setCommon(event.target.checked)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex items-center justify-between gap-1 py-2 mt-2'>
|
<div className='flex items-center justify-between gap-1 py-2 mt-2'>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import Card from '../../components/Common/Card';
|
import Card from '../../components/Common/Card';
|
||||||
|
import PrettyJson from '../../components/Common/PrettyJSON';
|
||||||
|
|
||||||
function RSFormStats() {
|
function RSFormStats() {
|
||||||
const { schema } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
@ -10,6 +11,7 @@ function RSFormStats() {
|
||||||
<label className='font-semibold'>Всего конституент:</label>
|
<label className='font-semibold'>Всего конституент:</label>
|
||||||
<span className='ml-2'>{schema!.items!.length}</span>
|
<span className='ml-2'>{schema!.items!.length}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<PrettyJson data={schema || ''}/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,30 +2,55 @@ import { Tabs, TabList, TabPanel } from 'react-tabs';
|
||||||
import ConstituentsTable from './ConstituentsTable';
|
import ConstituentsTable from './ConstituentsTable';
|
||||||
import { IConstituenta } from '../../models';
|
import { IConstituenta } from '../../models';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import ConceptTab from '../../components/Common/ConceptTab';
|
import ConceptTab from '../../components/Common/ConceptTab';
|
||||||
import RSFormCard from './RSFormCard';
|
import RSFormCard from './RSFormCard';
|
||||||
import { Loader } from '../../components/Common/Loader';
|
import { Loader } from '../../components/Common/Loader';
|
||||||
import BackendError from '../../components/BackendError';
|
import BackendError from '../../components/BackendError';
|
||||||
import ConstituentEditor from './ConstituentEditor';
|
import ConstituentEditor from './ConstituentEditor';
|
||||||
import RSFormStats from './RSFormStats';
|
import RSFormStats from './RSFormStats';
|
||||||
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
enum RSFormTabs {
|
enum TabsList {
|
||||||
CARD = 0,
|
CARD = 0,
|
||||||
CST_LIST = 1,
|
CST_LIST = 1,
|
||||||
CST_EDIT = 2
|
CST_EDIT = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
function RSFormEditor() {
|
function RSFormTabs() {
|
||||||
const { setActive, error, schema, loading } = useRSForm();
|
const { setActive, active, error, schema, loading } = useRSForm();
|
||||||
const [tabIndex, setTabIndex] = useState(RSFormTabs.CARD);
|
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', TabsList.CARD);
|
||||||
|
const search = useLocation().search;
|
||||||
|
|
||||||
const onEditCst = (cst: IConstituenta) => {
|
const onEditCst = (cst: IConstituenta) => {
|
||||||
console.log(`Set active cst: ${cst.alias}`);
|
console.log(`Set active cst: ${cst.alias}`);
|
||||||
setActive(cst);
|
setActive(cst);
|
||||||
setTabIndex(RSFormTabs.CST_EDIT)
|
setTabIndex(TabsList.CST_EDIT)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSelectTab = (index: number) => {
|
||||||
|
setTabIndex(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tabQuery = new URLSearchParams(search).get('tab');
|
||||||
|
const activeQuery = new URLSearchParams(search).get('active');
|
||||||
|
const activeCst = schema?.items?.find((cst) => cst.entityUID === Number(activeQuery)) || undefined;
|
||||||
|
setTabIndex(Number(tabQuery) || TabsList.CARD);
|
||||||
|
setActive(activeCst);
|
||||||
|
}, [search, setTabIndex, setActive, schema?.items]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (schema) {
|
||||||
|
let url = `/rsforms/${schema.id}?tab=${tabIndex}`
|
||||||
|
if (active) {
|
||||||
|
url = url + `&active=${active.entityUID}`
|
||||||
|
}
|
||||||
|
window.history.replaceState(null, '', url);
|
||||||
|
}
|
||||||
|
}, [tabIndex, active, schema]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container w-full'>
|
<div className='container w-full'>
|
||||||
{ loading && <Loader /> }
|
{ loading && <Loader /> }
|
||||||
|
@ -33,7 +58,7 @@ function RSFormEditor() {
|
||||||
{ schema && !loading &&
|
{ schema && !loading &&
|
||||||
<Tabs
|
<Tabs
|
||||||
selectedIndex={tabIndex}
|
selectedIndex={tabIndex}
|
||||||
onSelect={(index) => setTabIndex(index)}
|
onSelect={onSelectTab}
|
||||||
defaultFocus={true}
|
defaultFocus={true}
|
||||||
selectedTabClassName='font-bold'
|
selectedTabClassName='font-bold'
|
||||||
>
|
>
|
||||||
|
@ -43,7 +68,7 @@ function RSFormEditor() {
|
||||||
<ConceptTab>Редактор</ConceptTab>
|
<ConceptTab>Редактор</ConceptTab>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanel className='flex items-start w-full gap-2 '>
|
<TabPanel className='flex items-start w-full gap-2'>
|
||||||
<RSFormCard />
|
<RSFormCard />
|
||||||
<RSFormStats />
|
<RSFormStats />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
@ -60,4 +85,4 @@ function RSFormEditor() {
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSFormEditor;
|
export default RSFormTabs;
|
|
@ -1,12 +1,12 @@
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { RSFormState } from '../../context/RSFormContext';
|
import { RSFormState } from '../../context/RSFormContext';
|
||||||
import RSFormEditor from './RSFormEditor';
|
import RSFormTabs from './RSFormTabs';
|
||||||
|
|
||||||
function RSFormPage() {
|
function RSFormPage() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
return (
|
return (
|
||||||
<RSFormState id={id || ''}>
|
<RSFormState id={id || ''}>
|
||||||
<RSFormEditor />
|
<RSFormTabs />
|
||||||
</RSFormState>
|
</RSFormState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,25 @@
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import BackendError from '../../components/BackendError'
|
import BackendError from '../../components/BackendError'
|
||||||
import { Loader } from '../../components/Common/Loader'
|
import { Loader } from '../../components/Common/Loader'
|
||||||
import { useRSForms } from '../../hooks/useRSForms'
|
import { FilterType, RSFormsFilter, useRSForms } from '../../hooks/useRSForms'
|
||||||
import RSFormsTable from './RSFormsTable';
|
import RSFormsTable from './RSFormsTable';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useAuth } from '../../context/AuthContext';
|
||||||
|
|
||||||
function RSFormsPage() {
|
function RSFormsPage() {
|
||||||
const { rsforms, error, loading } = useRSForms();
|
const search = useLocation().search;
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { rsforms, error, loading, loadList } = useRSForms();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const filterQuery = new URLSearchParams(search).get('filter');
|
||||||
|
const type = (!user || !filterQuery ? FilterType.COMMON : filterQuery as FilterType);
|
||||||
|
let filter: RSFormsFilter = {type: type};
|
||||||
|
if (type === FilterType.PERSONAL) {
|
||||||
|
filter.data = user?.id;
|
||||||
|
}
|
||||||
|
loadList(filter);
|
||||||
|
}, [search, user, loadList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user