dark theme
This commit is contained in:
parent
8951edbc9b
commit
118bdb058b
@ -12,6 +12,7 @@ import CampaignPage from './pages/CampaignPage';
|
|||||||
import MainLayout from './components/MainLayout';
|
import MainLayout from './components/MainLayout';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { useUser } from './context/UserContext';
|
import { useUser } from './context/UserContext';
|
||||||
|
import { ThemeProvider } from './context/ThemeContext';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { user, token, isCheckingAuth } = useUser();
|
const { user, token, isCheckingAuth } = useUser();
|
||||||
@ -21,6 +22,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
@ -123,6 +125,7 @@ function App() {
|
|||||||
<Route path="*" element={<Navigate to="/dashboard" replace />} />
|
<Route path="*" element={<Navigate to="/dashboard" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
|
import ThemeToggle from './ThemeToggle';
|
||||||
|
|
||||||
const Header = ({ user, onLogout }) => {
|
const Header = ({ user, onLogout }) => {
|
||||||
return (
|
return (
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<div className={styles.right}>
|
<div className={styles.right}>
|
||||||
|
<ThemeToggle />
|
||||||
<span className={styles.user}>{user?.email || 'Аккаунт'}</span>
|
<span className={styles.user}>{user?.email || 'Аккаунт'}</span>
|
||||||
<button className={styles.logout} onClick={onLogout}>Выйти</button>
|
<button className={styles.logout} onClick={onLogout}>Выйти</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
.header {
|
.header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
border-bottom: 1.5px solid #e5e7eb;
|
border-bottom: 1.5px solid var(--border-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@ -38,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logout:hover {
|
.logout:hover {
|
||||||
background: linear-gradient(90deg, #3730a3 0%, #06b6d4 100%);
|
background: linear-gradient(90deg, #4f46e5 0%, #06b6d4 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Адаптивные стили для мобильных устройств */
|
/* Адаптивные стили для мобильных устройств */
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
.menu {
|
.menu {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f3f4f6;
|
background: var(--card-background);
|
||||||
border-right: 1.5px solid #e5e7eb;
|
border-right: 1.5px solid var(--border-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0 0 24px 0;
|
padding: 0 0 24px 0;
|
||||||
@ -12,10 +12,10 @@
|
|||||||
.project {
|
.project {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
padding: 32px 24px 16px 24px;
|
padding: 32px 24px 16px 24px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
border-bottom: 1px solid #e5e7eb;
|
border-bottom: 1px solid var(--border-color);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,20 +42,20 @@ ul {
|
|||||||
li {
|
li {
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #374151;
|
color: var(--text-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-left: 3px solid transparent;
|
border-left: 3px solid transparent;
|
||||||
transition: background 0.15s, border 0.15s, color 0.15s;
|
transition: background 0.15s, border 0.15s, color 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
li:hover {
|
li:hover {
|
||||||
background: #e0e7ff;
|
background: var(--hover-background);
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
background: #e0e7ff;
|
background: var(--hover-background);
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
border-left: 3px solid #6366f1;
|
border-left: 3px solid #6366f1;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ li:hover {
|
|||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 2px 0 10px var(--shadow-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu.open {
|
.menu.open {
|
||||||
|
|||||||
36
frontend/src/components/ThemeToggle.js
Normal file
36
frontend/src/components/ThemeToggle.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTheme } from '../context/ThemeContext';
|
||||||
|
import styles from './ThemeToggle.module.css';
|
||||||
|
|
||||||
|
const ThemeToggle = () => {
|
||||||
|
const { theme, toggleTheme, isDark } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={styles.themeToggle}
|
||||||
|
onClick={toggleTheme}
|
||||||
|
aria-label={`Переключить на ${isDark ? 'светлую' : 'темную'} тему`}
|
||||||
|
title={`Переключить на ${isDark ? 'светлую' : 'темную'} тему`}
|
||||||
|
>
|
||||||
|
{isDark ? (
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<circle cx="12" cy="12" r="5"/>
|
||||||
|
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||||
|
<line x1="12" y1="21" x2="12" y2="23"/>
|
||||||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
||||||
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||||
|
<line x1="1" y1="12" x2="3" y2="12"/>
|
||||||
|
<line x1="21" y1="12" x2="23" y2="12"/>
|
||||||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
||||||
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeToggle;
|
||||||
50
frontend/src/components/ThemeToggle.module.css
Normal file
50
frontend/src/components/ThemeToggle.module.css
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
.themeToggle {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: var(--card-background);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themeToggle:hover {
|
||||||
|
background-color: var(--hover-background);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themeToggle:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themeToggle svg {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.themeToggle:hover svg {
|
||||||
|
transform: rotate(15deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивные стили */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.themeToggle {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.themeToggle {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
frontend/src/context/ThemeContext.js
Normal file
67
frontend/src/context/ThemeContext.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
const ThemeContext = createContext();
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useTheme must be used within a ThemeProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemeProvider = ({ children }) => {
|
||||||
|
const [theme, setTheme] = useState(() => {
|
||||||
|
// Проверяем сохраненную тему в localStorage
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
if (savedTheme) {
|
||||||
|
return savedTheme;
|
||||||
|
}
|
||||||
|
// Если нет сохраненной темы, используем системную
|
||||||
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Сохраняем тему в localStorage
|
||||||
|
localStorage.setItem('theme', theme);
|
||||||
|
|
||||||
|
// Применяем тему к документу
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
|
||||||
|
// Обновляем CSS переменные
|
||||||
|
const root = document.documentElement;
|
||||||
|
if (theme === 'dark') {
|
||||||
|
root.style.setProperty('--background-color', '#1a1a1a');
|
||||||
|
root.style.setProperty('--text-color', '#ffffff');
|
||||||
|
root.style.setProperty('--border-color', '#333333');
|
||||||
|
root.style.setProperty('--card-background', '#2d2d2d');
|
||||||
|
root.style.setProperty('--hover-background', '#3a3a3a');
|
||||||
|
root.style.setProperty('--secondary-text', '#a0a0a0');
|
||||||
|
root.style.setProperty('--shadow-color', 'rgba(0, 0, 0, 0.3)');
|
||||||
|
} else {
|
||||||
|
root.style.setProperty('--background-color', '#ffffff');
|
||||||
|
root.style.setProperty('--text-color', '#000000');
|
||||||
|
root.style.setProperty('--border-color', '#e5e7eb');
|
||||||
|
root.style.setProperty('--card-background', '#ffffff');
|
||||||
|
root.style.setProperty('--hover-background', '#f9fafb');
|
||||||
|
root.style.setProperty('--secondary-text', '#6b7280');
|
||||||
|
root.style.setProperty('--shadow-color', 'rgba(0, 0, 0, 0.1)');
|
||||||
|
}
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
theme,
|
||||||
|
toggleTheme,
|
||||||
|
isDark: theme === 'dark'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -6,6 +6,9 @@ body {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
@ -18,6 +21,27 @@ code {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CSS переменные для тем */
|
||||||
|
:root {
|
||||||
|
--background-color: #ffffff;
|
||||||
|
--text-color: #000000;
|
||||||
|
--border-color: #e5e7eb;
|
||||||
|
--card-background: #ffffff;
|
||||||
|
--hover-background: #f9fafb;
|
||||||
|
--secondary-text: #6b7280;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--background-color: #1a1a1a;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--border-color: #333333;
|
||||||
|
--card-background: #2d2d2d;
|
||||||
|
--hover-background: #3a3a3a;
|
||||||
|
--secondary-text: #a0a0a0;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
/* Медиа-запросы для разных устройств */
|
/* Медиа-запросы для разных устройств */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
html {
|
html {
|
||||||
@ -39,19 +63,7 @@ code {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Поддержка темной темы */
|
/* Плавные переходы для всех элементов */
|
||||||
@media (prefers-color-scheme: dark) {
|
* {
|
||||||
:root {
|
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
--background-color: #1a1a1a;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--border-color: #333333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
--background-color: #ffffff;
|
|
||||||
--text-color: #000000;
|
|
||||||
--border-color: #e5e7eb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import styles from './Login.module.css';
|
import styles from './Login.module.css';
|
||||||
import { useUser } from '../context/UserContext';
|
import { useUser } from '../context/UserContext';
|
||||||
|
import ThemeToggle from '../components/ThemeToggle';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
@ -37,6 +38,9 @@ const Login = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
|
<div className={styles.themeToggleContainer}>
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
<form onSubmit={handleSubmit} className={styles.form}>
|
<form onSubmit={handleSubmit} className={styles.form}>
|
||||||
<h2 className={styles.title}>Вход в систему</h2>
|
<h2 className={styles.title}>Вход в систему</h2>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -5,6 +5,17 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
background: linear-gradient(135deg, #e0e7ff 0%, #f0fdfa 100%);
|
background: linear-gradient(135deg, #e0e7ff 0%, #f0fdfa 100%);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .wrapper {
|
||||||
|
background: linear-gradient(135deg, #1e1b4b 0%, #0f172a 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themeToggleContainer {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
@ -15,9 +26,9 @@
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
padding: 36px;
|
padding: 36px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid var(--border-color);
|
||||||
transition: box-shadow 0.3s;
|
transition: box-shadow 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +37,7 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
@ -34,11 +45,12 @@
|
|||||||
.input {
|
.input {
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #c7d2fe;
|
border: 1px solid var(--border-color);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border 0.2s;
|
transition: border 0.2s;
|
||||||
background: #f8fafc;
|
background: var(--card-background);
|
||||||
|
color: var(--text-color);
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +122,11 @@
|
|||||||
padding-top: 60px;
|
padding-top: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.themeToggleContainer {
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
@ -137,6 +154,11 @@
|
|||||||
padding-top: 40px;
|
padding-top: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.themeToggleContainer {
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
padding: 24px 20px;
|
padding: 24px 20px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
.dashboard {
|
.dashboard {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f8fafc;
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -16,6 +16,7 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Адаптивные стили для планшетов */
|
/* Адаптивные стили для планшетов */
|
||||||
@ -58,45 +59,46 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 3px var(--shadow-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableHeader {
|
.tableHeader {
|
||||||
background: #f3f4f6;
|
background: var(--hover-background);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom: 2px solid #e5e7eb;
|
border-bottom: 2px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableHeaderCell {
|
.tableHeaderCell {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #374151;
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableRow {
|
.tableRow {
|
||||||
border-bottom: 1px solid #e5e7eb;
|
border-bottom: 1px solid var(--border-color);
|
||||||
transition: background-color 0.15s;
|
transition: background-color 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableRow:hover {
|
.tableRow:hover {
|
||||||
background-color: #f9fafb;
|
background-color: var(--hover-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableCell {
|
.tableCell {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #374151;
|
color: var(--text-color);
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableCellActions {
|
.tableCellActions {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,13 +128,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buttonSecondary {
|
.buttonSecondary {
|
||||||
background: #f3f4f6;
|
background: var(--hover-background);
|
||||||
color: #374151;
|
color: var(--text-color);
|
||||||
border: 1px solid #d1d5db;
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttonSecondary:hover {
|
.buttonSecondary:hover {
|
||||||
background: #e5e7eb;
|
background: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttonDanger {
|
.buttonDanger {
|
||||||
@ -179,16 +181,17 @@
|
|||||||
.formLabel {
|
.formLabel {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #374151;
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.formInput {
|
.formInput {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border: 1.5px solid #d1d5db;
|
border: 1.5px solid var(--border-color);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: border-color 0.2s;
|
transition: border-color 0.2s;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.formInput:focus {
|
.formInput:focus {
|
||||||
@ -199,14 +202,15 @@
|
|||||||
|
|
||||||
.formTextarea {
|
.formTextarea {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border: 1.5px solid #d1d5db;
|
border: 1.5px solid var(--border-color);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
transition: border-color 0.2s;
|
transition: border-color 0.2s;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.formTextarea:focus {
|
.formTextarea:focus {
|
||||||
@ -217,10 +221,11 @@
|
|||||||
|
|
||||||
.formSelect {
|
.formSelect {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border: 1.5px solid #d1d5db;
|
border: 1.5px solid var(--border-color);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
|
color: var(--text-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.2s;
|
transition: border-color 0.2s;
|
||||||
}
|
}
|
||||||
@ -251,7 +256,7 @@
|
|||||||
.pageTitle {
|
.pageTitle {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: var(--text-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +399,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
color: #6b7280;
|
color: var(--secondary-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
@ -419,7 +424,7 @@
|
|||||||
.emptyState {
|
.emptyState {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 40px 20px;
|
padding: 40px 20px;
|
||||||
color: #9ca3af;
|
color: var(--secondary-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.emptyStateIcon {
|
.emptyStateIcon {
|
||||||
@ -432,12 +437,12 @@
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: #6b7280;
|
color: var(--secondary-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.emptyStateText {
|
.emptyStateText {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #9ca3af;
|
color: var(--secondary-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Стили для главной страницы с аналитикой */
|
/* Стили для главной страницы с аналитикой */
|
||||||
@ -449,19 +454,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.statCard {
|
.statCard {
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px var(--shadow-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
transition: transform 0.2s, box-shadow 0.2s;
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.statCard:hover {
|
.statCard:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 16px var(--shadow-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.statIcon {
|
.statIcon {
|
||||||
@ -485,24 +491,25 @@
|
|||||||
.statNumber {
|
.statNumber {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: var(--text-color);
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statLabel {
|
.statLabel {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #6b7280;
|
color: var(--secondary-text);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.additionalStats {
|
.additionalStats {
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px var(--shadow-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.statRow {
|
.statRow {
|
||||||
@ -516,7 +523,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
border-bottom: 1px solid #f3f4f6;
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.statItem:last-child {
|
.statItem:last-child {
|
||||||
@ -525,28 +532,29 @@
|
|||||||
|
|
||||||
.statItem .statLabel {
|
.statItem .statLabel {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #6b7280;
|
color: var(--secondary-text);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statItem .statValue {
|
.statItem .statValue {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #111827;
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
background: #fff;
|
background: var(--card-background);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px var(--shadow-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sectionTitle {
|
.sectionTitle {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #111827;
|
color: var(--text-color);
|
||||||
margin: 0 0 20px 0;
|
margin: 0 0 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,9 +571,9 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background: #f9fafb;
|
background: var(--hover-background);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.campaignInfo,
|
.campaignInfo,
|
||||||
@ -579,13 +587,13 @@
|
|||||||
.deliveryEmail {
|
.deliveryEmail {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #111827;
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.campaignDate,
|
.campaignDate,
|
||||||
.deliveryDate {
|
.deliveryDate {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #6b7280;
|
color: var(--secondary-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.campaignStatus,
|
.campaignStatus,
|
||||||
@ -635,7 +643,7 @@
|
|||||||
|
|
||||||
.statusclicked {
|
.statusclicked {
|
||||||
background: #e0e7ff;
|
background: #e0e7ff;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Адаптивные стили для главной страницы */
|
/* Адаптивные стили для главной страницы */
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
background: #e0e7ff;
|
background: #e0e7ff;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Адаптивные стили для планшетов */
|
/* Адаптивные стили для планшетов */
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.title {
|
.title {
|
||||||
margin: 0 0 18px 0;
|
margin: 0 0 18px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #3730a3;
|
color: #6366f1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user