From 2104c0170d6109113d6a9da7ae0cdad1a66fade4 Mon Sep 17 00:00:00 2001 From: shats Date: Fri, 3 Oct 2025 10:00:07 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B0=20Modal=20?= =?UTF-8?q?=D0=BD=D0=B0=20SmallModal=20=D0=B2=20ConfirmDialog,=20ConfirmDi?= =?UTF-8?q?ffModal=20=D0=B8=20ConfirmModal=20=D0=B4=D0=BB=D1=8F=20=D1=83?= =?UTF-8?q?=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=B0=D0=BA=D1=82=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B8=20=D1=83=D0=BD=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81?= =?UTF-8?q?=D0=B0.=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D1=82=D0=B8=D0=BB=D0=B5=D0=B9=20=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=D0=BA=D0=BE=D0=BD=D0=BE=D0=BA=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B7=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=B2=D0=BE=D1=81=D0=BF=D1=80=D0=B8=D1=8F=D1=82=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/ConfirmDialog.jsx | 40 +++--- frontend/src/components/ConfirmDiffModal.jsx | 67 +++++++--- frontend/src/components/ConfirmModal.jsx | 53 ++++---- frontend/src/components/Modal.jsx | 125 ++++++++++++++++--- 4 files changed, 205 insertions(+), 80 deletions(-) diff --git a/frontend/src/components/ConfirmDialog.jsx b/frontend/src/components/ConfirmDialog.jsx index 0aef316..a60b7bb 100644 --- a/frontend/src/components/ConfirmDialog.jsx +++ b/frontend/src/components/ConfirmDialog.jsx @@ -1,9 +1,9 @@ -import Modal from './Modal'; -import { IconAlertTriangle } from '@tabler/icons-react'; +import { SmallModal } from './Modal'; +import { IconAlertTriangle, IconCheck, IconX } from '@tabler/icons-react'; /** * ConfirmDialog - упрощённая версия ConfirmModal для быстрых подтверждений - * Рефакторинг: теперь использует базовый Modal компонент + * Алиас для ConfirmModal с другим API (для обратной совместимости) */ export default function ConfirmDialog({ open, @@ -17,17 +17,18 @@ export default function ConfirmDialog({ size = 'sm', loading = false }) { - const handleConfirm = () => { - onConfirm?.(); - }; - return ( - + {destructive && ( + + )} + {title} + + } footer={ <> } > -
- {destructive && ( -
- -
- )} -
-

{message}

-
+
+

{message}

- + ); } diff --git a/frontend/src/components/ConfirmDiffModal.jsx b/frontend/src/components/ConfirmDiffModal.jsx index 5b00b53..c17c8a9 100644 --- a/frontend/src/components/ConfirmDiffModal.jsx +++ b/frontend/src/components/ConfirmDiffModal.jsx @@ -1,8 +1,9 @@ -import Modal from './Modal'; +import { SmallModal } from './Modal'; +import { IconCheck, IconX, IconRefresh } from '@tabler/icons-react'; /** * ConfirmDiffModal - модальное окно для подтверждения изменений с отображением статистики - * Рефакторинг: теперь использует базовый Modal компонент + * Использует готовый пресет SmallModal для компактного отображения */ function ConfirmDiffModal({ show, diff, onConfirm, onClose, loading = false }) { const added = diff?.added?.length || 0; @@ -10,12 +11,15 @@ function ConfirmDiffModal({ show, diff, onConfirm, onClose, loading = false }) { const changed = diff?.changed?.length || 0; return ( - + + Подтвердить сохранение +
+ } footer={ <> @@ -42,31 +48,54 @@ function ConfirmDiffModal({ show, diff, onConfirm, onClose, loading = false }) { >
-
-
- Добавлено -
{added}
+
+
+
+ +
+
Добавлено
+
{added}
+
+
-
-
- Удалено -
{removed}
+
+
+
+ +
+
Удалено
+
{removed}
+
+
-
-
- Изменено -
{changed}
+
+
+
+ +
+
Изменено
+
{changed}
+
+
- + + {(added > 0 || removed > 0 || changed > 0) && ( +
+
+ Всего будет изменено {added + removed + changed} записей +
+
+ )} + ); } diff --git a/frontend/src/components/ConfirmModal.jsx b/frontend/src/components/ConfirmModal.jsx index 2f0f1b1..188f17c 100644 --- a/frontend/src/components/ConfirmModal.jsx +++ b/frontend/src/components/ConfirmModal.jsx @@ -1,9 +1,9 @@ -import Modal from './Modal'; -import { IconAlertTriangle } from '@tabler/icons-react'; +import { SmallModal } from './Modal'; +import { IconAlertTriangle, IconCheck, IconX } from '@tabler/icons-react'; /** * ConfirmModal - модальное окно подтверждения действия - * Для критичных операций (удаление, отмена и т.д.) + * Использует SmallModal для компактного отображения */ function ConfirmModal({ show, @@ -22,13 +22,28 @@ function ConfirmModal({ onConfirm?.(); }; + const getIconColor = () => { + switch (variant) { + case 'danger': return 'text-danger'; + case 'warning': return 'text-warning'; + case 'success': return 'text-success'; + case 'primary': return 'text-primary'; + default: return 'text-danger'; + } + }; + return ( - + {showIcon && Icon && ( + + )} + {title} +
+ } footer={ <> } > -
- {showIcon && Icon && ( -
- -
- )} -
- {typeof message === 'string' ? ( -

{message}

- ) : ( - message - )} -
+
+ {typeof message === 'string' ?

{message}

: message}
- + ); } export default ConfirmModal; - diff --git a/frontend/src/components/Modal.jsx b/frontend/src/components/Modal.jsx index 713ca56..145be87 100644 --- a/frontend/src/components/Modal.jsx +++ b/frontend/src/components/Modal.jsx @@ -1,18 +1,30 @@ import { useEffect, useRef } from 'react'; -import { IconX } from '@tabler/icons-react'; /** - * Универсальный Modal компонент - * Использует нативный Tabler UI стиль + * Универсальный Modal компонент с гарантированным backdrop + * Единая система для всех модальных окон проекта + * + * @param {boolean} show - Показать/скрыть модалку + * @param {function} onClose - Callback при закрытии + * @param {function} onOpen - Callback при открытии + * @param {string|ReactNode} title - Заголовок модалки + * @param {ReactNode} children - Содержимое + * @param {ReactNode} footer - Футер с кнопками + * @param {string} size - Размер: sm, md, lg, xl + * @param {boolean} backdrop - Показывать backdrop (по умолчанию true) + * @param {boolean} keyboard - Закрывать по ESC (по умолчанию true) + * @param {boolean} scrollable - Прокручиваемое тело + * @param {boolean} centered - Центрировать по вертикали + * @param {string} className - Дополнительные CSS классы */ function Modal({ show, onClose, - onOpen, // колбэк при открытии модалки + onOpen, title, children, footer, - size = 'md', // sm, md, lg, xl + size = 'md', backdrop = true, keyboard = true, scrollable = false, @@ -26,9 +38,7 @@ function Modal({ useEffect(() => { if (show) { document.body.classList.add('modal-open'); - // Trap focus внутри модалки modalRef.current?.focus(); - // Вызов колбэка при открытии onOpen?.(); } else { document.body.classList.remove('modal-open'); @@ -43,7 +53,9 @@ function Modal({ useEffect(() => { if (keyboard && show) { const handleEsc = (e) => { - if (e.key === 'Escape') onClose?.(); + if (e.key === 'Escape') { + onClose?.(); + } }; document.addEventListener('keydown', handleEsc); return () => document.removeEventListener('keydown', handleEsc); @@ -60,13 +72,23 @@ function Modal({ return ( <> - {/* Backdrop */} -
+ {/* Backdrop - гарантированное затемнение */} + {backdrop && ( +
+ )} {/* Modal */}
@@ -109,3 +139,66 @@ function Modal({ export default Modal; +/** + * =================================== + * ГОТОВЫЕ ПРЕСЕТЫ ДЛЯ РАЗНЫХ ЦЕЛЕЙ + * =================================== + */ + +/** + * Компактная модалка для быстрых подтверждений + */ +export function SmallModal({ show, onClose, title, children, footer, ...props }) { + return ( + + {children} + + ); +} + +/** + * Большая модалка для сложного контента + */ +export function LargeModal({ show, onClose, title, children, footer, scrollable = true, ...props }) { + return ( + + {children} + + ); +} + +/** + * Модалка на весь экран + */ +export function FullscreenModal({ show, onClose, title, children, footer, ...props }) { + return ( + + {children} + + ); +}