mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Add frontend for CstRename function
This commit is contained in:
parent
7322a232d4
commit
24da53bf40
|
@ -6,6 +6,7 @@ import Button from './Button';
|
|||
interface ModalProps {
|
||||
title?: string
|
||||
submitText?: string
|
||||
submitInvalidTooltip?: string
|
||||
readonly?: boolean
|
||||
canSubmit?: boolean
|
||||
hideWindow: () => void
|
||||
|
@ -14,7 +15,13 @@ interface ModalProps {
|
|||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function Modal({ title, hideWindow, onSubmit, readonly, onCancel, canSubmit, children, submitText = 'Продолжить' }: ModalProps) {
|
||||
function Modal({
|
||||
title, hideWindow, onSubmit,
|
||||
readonly, onCancel, canSubmit,
|
||||
submitInvalidTooltip,
|
||||
children,
|
||||
submitText = 'Продолжить'
|
||||
}: ModalProps) {
|
||||
const ref = useRef(null);
|
||||
useEscapeKey(hideWindow);
|
||||
|
||||
|
@ -30,19 +37,20 @@ function Modal({ title, hideWindow, onSubmit, readonly, onCancel, canSubmit, chi
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className='fixed top-0 left-0 z-50 w-full h-full opacity-50 clr-modal'>
|
||||
</div>
|
||||
<div className='fixed top-0 left-0 z-50 w-full h-full opacity-50 clr-modal' />
|
||||
<div
|
||||
ref={ref}
|
||||
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit max-w-[95vw] h-fit z-[60] clr-card border shadow-md mb-[5rem]'
|
||||
>
|
||||
{ title && <h1 className='mb-2 text-xl font-bold text-center'>{title}</h1> }
|
||||
<div className='max-h-[calc(95vh-15rem)] overflow-auto'>
|
||||
<div className='max-h-[calc(95vh-15rem)]'>
|
||||
{children}
|
||||
</div>
|
||||
<div className='flex justify-center w-full gap-4 pt-4 mt-2 border-t-4'>
|
||||
{!readonly && <Button
|
||||
{!readonly &&
|
||||
<Button
|
||||
text={submitText}
|
||||
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
||||
widthClass='min-w-[6rem] min-h-[2.6rem] w-fit h-fit'
|
||||
colorClass='clr-btn-primary'
|
||||
disabled={!canSubmit}
|
||||
|
|
|
@ -18,14 +18,14 @@ function TextInput({
|
|||
...props
|
||||
}: TextInputProps) {
|
||||
return (
|
||||
<div className={`flex ${singleRow ? 'items-center gap-4' : 'flex-col items-start'} [&:not(:first-child)]:mt-3`}>
|
||||
<div className={`flex [&:not(:first-child)]:mt-3 ${singleRow ? 'items-center gap-4 ' + widthClass : 'flex-col items-start'}`}>
|
||||
<Label
|
||||
text={label}
|
||||
required={required}
|
||||
htmlFor={id}
|
||||
/>
|
||||
<input id={id}
|
||||
className={`px-3 py-2 mt-2 leading-tight border shadow truncate hover:text-clip ${colorClass} ${widthClass}`}
|
||||
className={`px-3 py-2 mt-2 leading-tight border shadow truncate hover:text-clip ${colorClass} ${singleRow ? '' : widthClass}`}
|
||||
required={required}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
@ -5,13 +5,14 @@ import { type ErrorInfo } from '../components/BackendError'
|
|||
import { useRSFormDetails } from '../hooks/useRSFormDetails'
|
||||
import {
|
||||
type DataCallback, getTRSFile,
|
||||
patchConstituenta, patchDeleteConstituenta,
|
||||
patchMoveConstituenta, patchResetAliases, patchRSForm,
|
||||
patchConstituenta, patchDeleteConstituenta,
|
||||
patchMoveConstituenta, patchRenameConstituenta,
|
||||
patchResetAliases, patchRSForm,
|
||||
patchUploadTRS, postClaimRSForm, postNewConstituenta
|
||||
} from '../utils/backendAPI'
|
||||
import {
|
||||
IConstituentaList, IConstituentaMeta, ICstCreateData,
|
||||
ICstMovetoData, ICstUpdateData, IRSForm,
|
||||
ICstMovetoData, ICstRenameData, ICstUpdateData, IRSForm,
|
||||
IRSFormMeta, IRSFormUpdateData, IRSFormUploadData
|
||||
} from '../utils/models'
|
||||
import { useAuth } from './AuthContext'
|
||||
|
@ -42,6 +43,7 @@ interface IRSFormContext {
|
|||
resetAliases: (callback: () => void) => void
|
||||
|
||||
cstCreate: (data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => void
|
||||
cstRename: (data: ICstRenameData, callback?: DataCallback<IConstituentaMeta>) => void
|
||||
cstUpdate: (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => void
|
||||
cstDelete: (data: IConstituentaList, callback?: () => void) => void
|
||||
cstMoveTo: (data: ICstMovetoData, callback?: () => void) => void
|
||||
|
@ -153,7 +155,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
patchResetAliases(schemaID, {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => { setError(error) },
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
setSchema(Object.assign(schema, newData));
|
||||
if (callback) callback();
|
||||
|
@ -167,7 +169,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
getTRSFile(schemaID, {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => { setError(error) },
|
||||
onError: error => setError(error),
|
||||
onSuccess: callback
|
||||
});
|
||||
}, [schemaID, setError]);
|
||||
|
@ -179,7 +181,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => { setError(error) },
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
setSchema(newData.schema);
|
||||
if (callback) callback(newData.new_cst);
|
||||
|
@ -194,7 +196,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => { setError(error) },
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
setSchema(newData);
|
||||
if (callback) callback();
|
||||
|
@ -209,13 +211,27 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => { setError(error) },
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
reload(setProcessing, () => { if (callback != null) callback(newData); })
|
||||
reload(setProcessing, () => { if (callback) callback(newData); })
|
||||
}
|
||||
});
|
||||
}, [setError, reload]);
|
||||
|
||||
const cstRename = useCallback(
|
||||
(data: ICstRenameData, callback?: DataCallback<IConstituentaMeta>) => {
|
||||
setError(undefined)
|
||||
patchRenameConstituenta(schemaID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
reload(setProcessing, () => { if (callback) callback(newData); })
|
||||
}
|
||||
});
|
||||
}, [setError, reload, schemaID]);
|
||||
|
||||
const cstMoveTo = useCallback(
|
||||
(data: ICstMovetoData, callback?: () => void) => {
|
||||
setError(undefined)
|
||||
|
@ -223,7 +239,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => { setError(error) },
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
setSchema(newData);
|
||||
if (callback) callback();
|
||||
|
@ -237,11 +253,11 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
error, loading, processing,
|
||||
isForceAdmin, isReadonly, isOwned, isEditable,
|
||||
isClaimable, isTracking,
|
||||
toggleForceAdmin: () => { setIsForceAdmin(prev => !prev) },
|
||||
toggleReadonly: () => { setIsReadonly(prev => !prev) },
|
||||
toggleForceAdmin: () => setIsForceAdmin(prev => !prev),
|
||||
toggleReadonly: () => setIsReadonly(prev => !prev),
|
||||
toggleTracking,
|
||||
update, download, upload, claim, resetAliases,
|
||||
cstUpdate, cstCreate, cstDelete, cstMoveTo
|
||||
cstUpdate, cstCreate, cstRename, cstDelete, cstMoveTo
|
||||
}}>
|
||||
{ children }
|
||||
</RSFormContext.Provider>
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useLayoutEffect, useState } from 'react';
|
||||
|
||||
import ConceptSelect from '../../components/Common/ConceptSelect';
|
||||
import Modal from '../../components/Common/Modal';
|
||||
import TextInput from '../../components/Common/TextInput';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { CstType, ICstRenameData } from '../../utils/models';
|
||||
import { CstTypeSelector, getCstTypeLabel } from '../../utils/staticUI';
|
||||
import { createAliasFor, CstTypeSelector, getCstTypeLabel, getCstTypePrefix } from '../../utils/staticUI';
|
||||
|
||||
interface DlgRenameCstProps {
|
||||
hideWindow: () => void
|
||||
initial: ICstRenameData
|
||||
initial?: ICstRenameData
|
||||
onRename: (data: ICstRenameData) => void
|
||||
}
|
||||
|
||||
function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||
const { schema } = useRSForm();
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [cstType, setCstType] = useState<CstType>(CstType.BASE);
|
||||
const [cstID, setCstID] = useState(0)
|
||||
|
@ -26,11 +28,17 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
onRename(getData());
|
||||
};
|
||||
const handleSubmit = () => onRename(getData());
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (schema && initial && cstType !== initial.cst_type) {
|
||||
setAlias(createAliasFor(cstType, schema));
|
||||
}
|
||||
}, [initial, cstType, schema]);
|
||||
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (initial) {
|
||||
setCstType(initial.cst_type);
|
||||
setAlias(initial.alias);
|
||||
|
@ -38,34 +46,42 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
|||
}
|
||||
}, [initial]);
|
||||
|
||||
useEffect(() => {
|
||||
// setValidated(selectedType !== undefined);
|
||||
setValidated(true)
|
||||
}, [cstType, alias]
|
||||
);
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (!initial || !schema) {
|
||||
setValidated(false);
|
||||
} else if(alias === initial.alias || alias.length < 2 || alias[0] !== getCstTypePrefix(cstType)) {
|
||||
setValidated(false);
|
||||
} else {
|
||||
setValidated(!schema.items.find(cst => cst.alias === alias))
|
||||
}
|
||||
}, [cstType, alias, initial, schema]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title='Создание конституенты'
|
||||
title='Переименование конституенты'
|
||||
hideWindow={hideWindow}
|
||||
canSubmit={validated}
|
||||
onSubmit={handleSubmit}
|
||||
submitInvalidTooltip={'Введите имя, соответствующее типу и отсутствующее в схеме'}
|
||||
submitText='Переименовать'
|
||||
>
|
||||
<div className='h-fit w-[20rem] px-2 mb-2 flex flex-col justify-stretch'>
|
||||
<div className='flex justify-center w-full'>
|
||||
<ConceptSelect
|
||||
className='my-2 min-w-[15rem] self-center'
|
||||
options={CstTypeSelector}
|
||||
placeholder='Выберите тип'
|
||||
values={cstType ? [{ value: cstType, label: getCstTypeLabel(cstType) }] : []}
|
||||
onChange={data => { setCstType(data.length > 0 ? data[0].value : CstType.BASE); }}
|
||||
/>
|
||||
</div>
|
||||
<TextInput id='alias'
|
||||
label='Имя конституенты'
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
<div className='flex items-center gap-4 px-2 mb-2 h-fit min-w-[25rem]'>
|
||||
<ConceptSelect
|
||||
className='my-2 min-w-[14rem] self-center'
|
||||
options={CstTypeSelector}
|
||||
placeholder='Выберите тип'
|
||||
values={cstType ? [{ value: cstType, label: getCstTypeLabel(cstType) }] : []}
|
||||
onChange={data => { setCstType(data.length > 0 ? data[0].value : CstType.BASE); }}
|
||||
/>
|
||||
<div>
|
||||
<TextInput id='alias' label='Имя'
|
||||
singleRow
|
||||
widthClass='w-[7rem]'
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -9,7 +9,7 @@ import TextArea from '../../components/Common/TextArea';
|
|||
import CstStatusInfo from '../../components/Help/InfoCstStatus';
|
||||
import { DumpBinIcon, HelpIcon, PenIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { CstType, EditMode, ICstCreateData, ICstUpdateData, SyntaxTree } from '../../utils/models';
|
||||
import { CstType, EditMode, ICstCreateData, ICstRenameData, ICstUpdateData, SyntaxTree } from '../../utils/models';
|
||||
import { getCstTypificationLabel } from '../../utils/staticUI';
|
||||
import EditorRSExpression from './EditorRSExpression';
|
||||
import ViewSideConstituents from './elements/ViewSideConstituents';
|
||||
|
@ -22,10 +22,11 @@ interface EditorConstituentaProps {
|
|||
onOpenEdit: (cstID: number) => void
|
||||
onShowAST: (expression: string, ast: SyntaxTree) => void
|
||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
||||
onRenameCst: (initial: ICstRenameData) => void
|
||||
onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void
|
||||
}
|
||||
|
||||
function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDeleteCst }: EditorConstituentaProps) {
|
||||
function EditorConstituenta({ activeID, onShowAST, onCreateCst, onRenameCst, onOpenEdit, onDeleteCst }: EditorConstituentaProps) {
|
||||
const { schema, processing, isEditable, cstUpdate } = useRSForm();
|
||||
const activeCst = useMemo(
|
||||
() => {
|
||||
|
@ -112,7 +113,15 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
|
|||
}
|
||||
|
||||
function handleRename() {
|
||||
toast.info('Переименование в разработке');
|
||||
if (!activeID || !activeCst) {
|
||||
return;
|
||||
}
|
||||
const data: ICstRenameData = {
|
||||
id: activeID,
|
||||
alias: activeCst?.alias,
|
||||
cst_type: activeCst.cstType
|
||||
};
|
||||
onRenameCst(data);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -9,11 +9,12 @@ import { Loader } from '../../components/Common/Loader';
|
|||
import { useLibrary } from '../../context/LibraryContext';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { prefixes, TIMEOUT_UI_REFRESH } from '../../utils/constants';
|
||||
import { ICstCreateData, SyntaxTree } from '../../utils/models';
|
||||
import { ICstCreateData, ICstRenameData, SyntaxTree } from '../../utils/models';
|
||||
import { createAliasFor } from '../../utils/staticUI';
|
||||
import DlgCloneRSForm from './DlgCloneRSForm';
|
||||
import DlgCreateCst from './DlgCreateCst';
|
||||
import DlgDeleteCst from './DlgDeleteCst';
|
||||
import DlgRenameCst from './DlgRenameCst';
|
||||
import DlgShowAST from './DlgShowAST';
|
||||
import DlgUploadRSForm from './DlgUploadRSForm';
|
||||
import EditorConstituenta from './EditorConstituenta';
|
||||
|
@ -35,7 +36,7 @@ function RSTabs() {
|
|||
const search = useLocation().search;
|
||||
const {
|
||||
error, schema, loading,
|
||||
cstCreate, cstDelete
|
||||
cstCreate, cstDelete, cstRename
|
||||
} = useRSForm();
|
||||
const { destroySchema } = useLibrary();
|
||||
|
||||
|
@ -55,6 +56,9 @@ function RSTabs() {
|
|||
|
||||
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
|
||||
const [showCreateCst, setShowCreateCst] = useState(false);
|
||||
|
||||
const [renameInitialData, setRenameInitialData] = useState<ICstRenameData>();
|
||||
const [showRenameCst, setShowRenameCst] = useState(false);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (schema) {
|
||||
|
@ -122,6 +126,17 @@ function RSTabs() {
|
|||
}
|
||||
}, [handleCreateCst]);
|
||||
|
||||
const handleRenameCst = useCallback(
|
||||
(data: ICstRenameData) => {
|
||||
cstRename(data, () => toast.success(`Конституента переименована: ${renameInitialData!.alias} -> ${data.alias}`));
|
||||
}, [cstRename, renameInitialData]);
|
||||
|
||||
const promptRenameCst = useCallback(
|
||||
(initialData: ICstRenameData) => {
|
||||
setRenameInitialData(initialData);
|
||||
setShowRenameCst(true);
|
||||
}, []);
|
||||
|
||||
const handleDeleteCst = useCallback(
|
||||
(deleted: number[]) => {
|
||||
if (!schema) {
|
||||
|
@ -205,6 +220,12 @@ function RSTabs() {
|
|||
onCreate={handleCreateCst}
|
||||
initial={createInitialData}
|
||||
/>}
|
||||
{showRenameCst &&
|
||||
<DlgRenameCst
|
||||
hideWindow={() => setShowRenameCst(false)}
|
||||
onRename={handleRenameCst}
|
||||
initial={renameInitialData}
|
||||
/>}
|
||||
{showDeleteCst &&
|
||||
<DlgDeleteCst
|
||||
hideWindow={() => setShowDeleteCst(false)}
|
||||
|
@ -254,6 +275,7 @@ function RSTabs() {
|
|||
onShowAST={onShowAST}
|
||||
onCreateCst={promptCreateCst}
|
||||
onDeleteCst={promptDeleteCst}
|
||||
onRenameCst={promptRenameCst}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { type ErrorInfo } from '../components/BackendError'
|
|||
import { config } from './constants'
|
||||
import {
|
||||
IConstituentaList, IConstituentaMeta,
|
||||
ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstUpdateData,
|
||||
ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstRenameData, ICstUpdateData,
|
||||
ICurrentUser, IExpressionParse, IRSExpression,
|
||||
IRSFormCreateData, IRSFormData,
|
||||
IRSFormMeta, IRSFormUpdateData, IRSFormUploadData, IUserInfo,
|
||||
|
@ -210,6 +210,14 @@ export function patchConstituenta(target: string, request: FrontExchange<ICstUpd
|
|||
});
|
||||
}
|
||||
|
||||
export function patchRenameConstituenta(schema: string, request: FrontExchange<ICstRenameData, IConstituentaMeta>) {
|
||||
AxiosPatch({
|
||||
title: `Renaming constituenta id=${request.data.id} for schema id=${schema}`,
|
||||
endpoint: `/api/rsforms/${schema}/cst-rename/`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function patchMoveConstituenta(schema: string, request: FrontExchange<ICstMovetoData, IRSFormData>) {
|
||||
AxiosPatch({
|
||||
title: `Moving Constituents for RSForm id=${schema}: ${JSON.stringify(request.data.items)} to ${request.data.move_to}`,
|
||||
|
|
Loading…
Reference in New Issue
Block a user