From 8952c4a897f5d6613b5df072d6ddef0a28ce6494 Mon Sep 17 00:00:00 2001 From: shats Date: Thu, 2 Oct 2025 19:17:25 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B0=D0=BD=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20UX/UI=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F,=20=D0=B2=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=BF=D0=BB=D0=B0=D0=B2=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=D1=8B,=20=D1=8D?= =?UTF-8?q?=D1=84=D1=84=D0=B5=D0=BA=D1=82=D1=8B=20=D0=BF=D1=80=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D0=B0=D0=BD=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BC=D0=BE=D0=B4=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BE=D0=BA=D0=BE=D0=BD.=20=D0=9E=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B8=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86?= =?UTF-8?q?=D1=8B=20=D1=81=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=BC=D0=B8=20=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=D0=BC=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B2=D0=B8=D0=B7=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=B2=D0=BE=D1=81=D0=BF=D1=80=D0=B8=D1=8F=D1=82?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B8=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.css | 221 ++++++++++++++++ frontend/src/App.jsx | 9 +- frontend/src/Dashboard.jsx | 72 ++--- frontend/src/DomainsNewManager.jsx | 62 +++-- frontend/src/FilterManager.jsx | 324 +++++++++++++++++------ frontend/src/ServerManager.jsx | 150 ++++++++++- frontend/src/components/ConfirmModal.jsx | 71 +++++ frontend/src/components/FormField.jsx | 147 ++++++++++ frontend/src/components/FormModal.jsx | 72 +++++ frontend/src/components/Modal.jsx | 108 ++++++++ frontend/src/hooks/useDebounce.js | 39 +++ frontend/src/hooks/useForm.js | 135 ++++++++++ 12 files changed, 1267 insertions(+), 143 deletions(-) create mode 100644 frontend/src/components/ConfirmModal.jsx create mode 100644 frontend/src/components/FormField.jsx create mode 100644 frontend/src/components/FormModal.jsx create mode 100644 frontend/src/components/Modal.jsx create mode 100644 frontend/src/hooks/useDebounce.js create mode 100644 frontend/src/hooks/useForm.js diff --git a/frontend/src/App.css b/frontend/src/App.css index d7bf046..13a90cd 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -300,3 +300,224 @@ border-color: var(--tblr-border-color); } } + +/* ===== UX/UI Enhancement Animations ===== */ + +/* Плавные переходы для всех интерактивных элементов */ +.transition-all { + transition: all 0.2s ease; +} + +/* Анимация появления карточек */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.card.animate-in { + animation: fadeInUp 0.3s ease-out; +} + +/* Анимация при добавлении элемента в таблицу */ +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.table tbody tr.new-item { + animation: slideInRight 0.3s ease-out; + background-color: rgba(32, 107, 196, 0.1); +} + +/* Пульсация при обновлении */ +@keyframes pulse { + 0%, 100% { + box-shadow: 0 0 0 0 rgba(32, 107, 196, 0.4); + } + 50% { + box-shadow: 0 0 0 6px rgba(32, 107, 196, 0); + } +} + +.badge.updating { + animation: pulse 1.5s infinite; +} + +/* Hover эффекты для интерактивных карточек */ +.card.interactive { + cursor: pointer; + transition: all 0.2s ease; +} + +.card.interactive:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +/* Ripple effect для кнопок */ +@keyframes ripple { + to { + transform: scale(4); + opacity: 0; + } +} + +.btn-ripple { + position: relative; + overflow: hidden; +} + +.btn-ripple::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.5); + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; +} + +.btn-ripple:active::after { + width: 100px; + height: 100px; + transition: 0s; + animation: ripple 0.6s ease-out; +} + +/* Улучшенная анимация skeleton loader */ +@keyframes shimmer { + 0% { + background-position: -1000px 0; + } + 100% { + background-position: 1000px 0; + } +} + +.placeholder-glow .placeholder { + animation: shimmer 2s infinite linear; + background: linear-gradient( + 90deg, + #f8f9fa 0%, + #e9ecef 50%, + #f8f9fa 100% + ); + background-size: 1000px 100%; +} + +/* Анимация модальных окон */ +.modal.fade.show .modal-dialog { + animation: modalSlideIn 0.3s ease-out; +} + +@keyframes modalSlideIn { + from { + opacity: 0; + transform: scale(0.95) translateY(-20px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +/* Плавный скролл */ +html { + scroll-behavior: smooth; +} + +/* Улучшенные focus rings для доступности */ +*:focus-visible { + outline: 2px solid var(--tblr-primary, #206bc4); + outline-offset: 2px; + border-radius: 3px; +} + +button:focus-visible, +.btn:focus-visible { + box-shadow: 0 0 0 3px rgba(32, 107, 196, 0.3); +} + +/* Skip link для клавиатурных пользователей */ +.skip-link { + position: absolute; + top: -40px; + left: 0; + background: var(--tblr-primary, #206bc4); + color: white; + padding: 8px 16px; + text-decoration: none; + z-index: 10000; + border-radius: 0 0 4px 0; + transition: top 0.2s; +} + +.skip-link:focus { + top: 0; +} + +/* Hover эффект для строк таблицы с focus */ +.table tr:focus-within { + background-color: rgba(32, 107, 196, 0.05); +} + +/* Pulse для кнопок загрузки */ +.btn.loading { + position: relative; +} + +.btn.loading::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + border-radius: inherit; + background: rgba(255, 255, 255, 0.2); + transform: translate(-50%, -50%); + animation: pulse-button 1.5s infinite; +} + +@keyframes pulse-button { + 0%, 100% { + opacity: 0; + transform: translate(-50%, -50%) scale(1); + } + 50% { + opacity: 1; + transform: translate(-50%, -50%) scale(1.05); + } +} + +/* Анимация badge появления */ +.badge.animate { + animation: badgePop 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +@keyframes badgePop { + 0% { + transform: scale(0); + } + 50% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + } +} \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3492ce8..04ddf1c 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -216,6 +216,11 @@ function MainLayout() { return (
+ {/* Skip link для доступности */} + + Перейти к основному содержанию + + {/* Верхнее меню Tabler Admin */}
@@ -371,7 +376,7 @@ function MainLayout() {
-
+
} /> } /> @@ -384,7 +389,7 @@ function MainLayout() { } /> } /> -
+