Add frontend for CstRename function

This commit is contained in:
IRBorisov 2023-08-23 01:36:17 +03:00
parent 7322a232d4
commit 24da53bf40
7 changed files with 133 additions and 54 deletions

View File

@ -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}

View File

@ -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}
/>

View File

@ -6,12 +6,13 @@ import { useRSFormDetails } from '../hooks/useRSFormDetails'
import {
type DataCallback, getTRSFile,
patchConstituenta, patchDeleteConstituenta,
patchMoveConstituenta, patchResetAliases, patchRSForm,
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>

View File

@ -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,35 +46,43 @@ 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'>
<div className='flex items-center gap-4 px-2 mb-2 h-fit min-w-[25rem]'>
<ConceptSelect
className='my-2 min-w-[15rem] self-center'
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='Имя конституенты'
<div>
<TextInput id='alias' label='Имя'
singleRow
widthClass='w-[7rem]'
value={alias}
onChange={event => setAlias(event.target.value)}
/>
</div>
</div>
</Modal>
);
}

View File

@ -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 (

View File

@ -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();
@ -56,6 +57,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) {
const oldTitle = document.title
@ -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>

View File

@ -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}`,