mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
Improve RSForm UI and add Modal dialog
This commit is contained in:
parent
8861ec5378
commit
c9e56e7146
|
@ -8,13 +8,15 @@ interface ButtonProps {
|
|||
disabled?: boolean
|
||||
dense?: boolean
|
||||
loading?: boolean
|
||||
widthClass?: string
|
||||
borderClass?: string
|
||||
colorClass?: string
|
||||
onClick?: MouseEventHandler<HTMLButtonElement> | undefined
|
||||
}
|
||||
|
||||
function Button({id, text, icon, tooltip,
|
||||
dense, disabled,
|
||||
borderClass='border rounded',
|
||||
borderClass='border rounded', colorClass='clr-btn-default', widthClass='w-fit h-fit',
|
||||
loading, onClick,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
|
@ -26,9 +28,7 @@ function Button({id, text, icon, tooltip,
|
|||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
title={tooltip}
|
||||
className={padding + ' ' + borderClass + ' ' +
|
||||
'inline-flex items-center gap-2 align-middle justify-center w-fit h-fit clr-btn-default ' + cursor
|
||||
}
|
||||
className={`inline-flex items-center gap-2 align-middle justify-center ${padding} ${borderClass} ${colorClass} ${widthClass} ${cursor}`}
|
||||
{...props}
|
||||
>
|
||||
{icon && <span>{icon}</span>}
|
||||
|
|
|
@ -6,7 +6,7 @@ interface CardProps {
|
|||
|
||||
function Card({title, widthClass='min-w-fit', children}: CardProps) {
|
||||
return (
|
||||
<div className={`border shadow-md py-2 bg-gray-50 dark:bg-gray-600 px-6 ${widthClass}`}>
|
||||
<div className={`border shadow-md py-2 clr-card px-6 ${widthClass}`}>
|
||||
{ title && <h1 className='mb-2 text-xl font-bold'>{title}</h1> }
|
||||
{children}
|
||||
</div>
|
||||
|
|
56
rsconcept/frontend/src/components/Common/Modal.tsx
Normal file
56
rsconcept/frontend/src/components/Common/Modal.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { useRef } from 'react'
|
||||
import Button from './Button'
|
||||
import useClickedOutside from '../../hooks/useClickedOutside'
|
||||
|
||||
interface ModalProps {
|
||||
title?: string
|
||||
submitText?: string
|
||||
show: boolean
|
||||
canSubmit: boolean
|
||||
toggle: () => void
|
||||
onSubmit: () => void
|
||||
onCancel?: () => void
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function Modal({title, show, toggle, onSubmit, onCancel, canSubmit, children, submitText='Продолжить'}: ModalProps) {
|
||||
const ref = useRef(null);
|
||||
useClickedOutside({ref: ref, callback: toggle})
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
toggle();
|
||||
if(onCancel) onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='fixed top-0 left-0 w-full h-full clr-modal opacity-50 z-50'>
|
||||
</div>
|
||||
<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 z-[60] clr-card border shadow-md'>
|
||||
{ title && <h1 className='mb-4 text-xl font-bold'>{title}</h1> }
|
||||
<div className='py-2'>
|
||||
{children}
|
||||
</div>
|
||||
<div className='pt-4 mt-2 border-t-4 flex justify-between w-full'>
|
||||
<Button
|
||||
text={submitText}
|
||||
widthClass='min-w-[6rem] w-fit h-fit'
|
||||
colorClass='clr-btn-primary'
|
||||
disabled={!canSubmit}
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
<Button
|
||||
text='Отмена'
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Modal;
|
|
@ -35,6 +35,10 @@
|
|||
@apply bg-gray-100 dark:bg-gray-600
|
||||
}
|
||||
|
||||
.clr-modal {
|
||||
@apply bg-gray-300 dark:bg-gray-800
|
||||
}
|
||||
|
||||
.clr-nav {
|
||||
@apply border-gray-400 dark:border-gray-300 bg-white dark:bg-gray-700
|
||||
}
|
||||
|
@ -43,6 +47,10 @@
|
|||
@apply text-gray-600 bg-white border-gray-400 dark:bg-gray-700 dark:border-gray-300 dark:text-gray-300
|
||||
}
|
||||
|
||||
.clr-card {
|
||||
@apply bg-gray-50 dark:bg-gray-600
|
||||
}
|
||||
|
||||
.clr-tab {
|
||||
@apply text-gray-600 dark:text-zinc-200 hover:bg-gray-300 dark:hover:bg-gray-400
|
||||
}
|
||||
|
|
|
@ -129,6 +129,10 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
|||
columns={columns}
|
||||
keyField='id'
|
||||
noContextMenu
|
||||
noDataComponent={<span className='p-2 flex flex-col justify-center text-center'>
|
||||
<p>Список конституент пуст</p>
|
||||
<p>Измените параметры фильтра</p>
|
||||
</span>}
|
||||
|
||||
striped
|
||||
highlightOnHover
|
||||
|
|
|
@ -7,6 +7,7 @@ import { ArrowDownIcon, ArrowUpIcon, ArrowsRotateIcon, DumpBinIcon, SmallPlusIco
|
|||
import { toast } from 'react-toastify';
|
||||
import Divider from '../../components/Common/Divider';
|
||||
import { getCstTypeLabel, getCstTypePrefix, getStatusInfo, getTypeLabel } from '../../utils/staticUI';
|
||||
import CreateCstModal from './CreateCstModal';
|
||||
|
||||
interface ConstituentsTableProps {
|
||||
onOpenEdit: (cst: IConstituenta) => void
|
||||
|
@ -17,6 +18,8 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
const [selectedRows, setSelectedRows] = useState<IConstituenta[]>([]);
|
||||
const nothingSelected = useMemo(() => selectedRows.length === 0, [selectedRows]);
|
||||
|
||||
const [showCstModal, setShowCstModal] = useState(true);
|
||||
|
||||
const handleRowSelected = useCallback(
|
||||
({selectedRows} : SelectionInfo<IConstituenta>) => {
|
||||
setSelectedRows(selectedRows);
|
||||
|
@ -46,7 +49,11 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
}, []);
|
||||
|
||||
const handleAddNew = useCallback((cstType?: CstType) => {
|
||||
toast.info(`Новая конституента ${cstType || 'NEW'}`);
|
||||
if (!cstType) {
|
||||
setShowCstModal(true);
|
||||
} else {
|
||||
toast.info(`Новая конституента ${cstType || 'NEW'}`);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const columns = useMemo(() =>
|
||||
|
@ -161,9 +168,14 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
], []
|
||||
);
|
||||
|
||||
return (
|
||||
return (<>
|
||||
<CreateCstModal
|
||||
show={showCstModal}
|
||||
toggle={() => setShowCstModal(!showCstModal)}
|
||||
onCreate={handleAddNew}
|
||||
/>
|
||||
<div className='w-full'>
|
||||
<div className='flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem]'>
|
||||
<div className='sticky top-[4rem] z-10 flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app'>
|
||||
<div className='mr-3 whitespace-nowrap'>Выбраны <span className='ml-2'><b>{selectedRows.length}</b> из {schema?.stats?.count_all || 0}</span></div>
|
||||
{isEditable && <div className='flex justify-start w-full gap-1'>
|
||||
<Button
|
||||
|
@ -216,6 +228,10 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
data={schema!.items!}
|
||||
columns={columns}
|
||||
keyField='id'
|
||||
noDataComponent={<span className='p-2 flex flex-col justify-center text-center'>
|
||||
<p>Список пуст</p>
|
||||
<p>Создайте новую конституенту</p>
|
||||
</span>}
|
||||
|
||||
striped
|
||||
highlightOnHover
|
||||
|
@ -230,7 +246,7 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
dense
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
</>);
|
||||
}
|
||||
|
||||
export default ConstituentsTable;
|
||||
|
|
33
rsconcept/frontend/src/pages/RSFormPage/CreateCstModal.tsx
Normal file
33
rsconcept/frontend/src/pages/RSFormPage/CreateCstModal.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import Modal from '../../components/Common/Modal';
|
||||
import { CstType } from '../../utils/models';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface CreateCstModalProps {
|
||||
show: boolean
|
||||
toggle: () => void
|
||||
onCreate: (type: CstType) => void
|
||||
}
|
||||
|
||||
function CreateCstModal({show, toggle, onCreate}: CreateCstModalProps) {
|
||||
const [validated, setValidated] = useState(false);
|
||||
|
||||
const handleSubmit = () => {
|
||||
toast.info('Создание конституент');
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title='Создание конституенты'
|
||||
show={show}
|
||||
toggle={toggle}
|
||||
canSubmit={validated}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<p>Выбор типа конституенты</p>
|
||||
<p>Добавить после выбранной или в конец</p>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateCstModal;
|
|
@ -10,8 +10,6 @@ import { CrownIcon, DownloadIcon, DumpBinIcon, SaveIcon, ShareIcon } from '../..
|
|||
import { useUsers } from '../../context/UsersContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { toast } from 'react-toastify';
|
||||
import fileDownload from 'js-file-download';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { claimOwnershipProc, deleteRSFormProc, downloadRSFormProc, shareCurrentURLProc } from '../../utils/procedures';
|
||||
|
||||
|
|
|
@ -67,6 +67,12 @@ function RSFormsTable({schemas}: RSFormsTableProps) {
|
|||
striped
|
||||
highlightOnHover
|
||||
pointerOnHover
|
||||
|
||||
noDataComponent={<span className='p-2 flex flex-col justify-center text-center'>
|
||||
<p>Список схем пуст</p>
|
||||
<p>Измените фильтр</p>
|
||||
</span>}
|
||||
|
||||
pagination
|
||||
paginationPerPage={50}
|
||||
paginationRowsPerPageOptions={[10, 20, 30, 50, 100]}
|
||||
|
|
Loading…
Reference in New Issue
Block a user