Improve RSForm UI and add Modal dialog

This commit is contained in:
IRBorisov 2023-07-22 12:24:14 +03:00
parent 8861ec5378
commit c9e56e7146
9 changed files with 132 additions and 11 deletions

View File

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

View File

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

View 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;

View File

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

View File

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

View File

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

View 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;

View File

@ -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';

View File

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