167 lines
5.1 KiB
TypeScript
167 lines
5.1 KiB
TypeScript
import React, { useEffect } from "react";
|
||
import { useNavigate } from "react-router-dom";
|
||
import { Icon } from "@iconify/react";
|
||
import { useAppSelector, useAppDispatch } from "../../store/hooks";
|
||
import { clearAuth } from "../../store/slices/authSlice";
|
||
import {
|
||
setSelectedDate,
|
||
setSelectedTag,
|
||
setSearchQuery,
|
||
} from "../../store/slices/notesSlice";
|
||
import { authApi } from "../../api/authApi";
|
||
import { userApi } from "../../api/userApi";
|
||
import { ThemeToggle } from "../common/ThemeToggle";
|
||
import { setUser, setAiSettings } from "../../store/slices/profileSlice";
|
||
import { setAccentColor as setAccentColorAction } from "../../store/slices/uiSlice";
|
||
import { setAccentColor } from "../../utils/colorUtils";
|
||
|
||
interface HeaderProps {
|
||
onFilterChange?: (hasFilters: boolean) => void;
|
||
onToggleSidebar?: () => void;
|
||
}
|
||
|
||
export const Header: React.FC<HeaderProps> = ({
|
||
onFilterChange,
|
||
onToggleSidebar,
|
||
}) => {
|
||
const navigate = useNavigate();
|
||
const dispatch = useAppDispatch();
|
||
const username = useAppSelector((state) => state.auth.username);
|
||
const user = useAppSelector((state) => state.profile.user);
|
||
const selectedDate = useAppSelector((state) => state.notes.selectedDate);
|
||
const selectedTag = useAppSelector((state) => state.notes.selectedTag);
|
||
const searchQuery = useAppSelector((state) => state.notes.searchQuery);
|
||
|
||
useEffect(() => {
|
||
loadUserInfo();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const hasFilters = !!(selectedDate || selectedTag || searchQuery);
|
||
onFilterChange?.(hasFilters);
|
||
}, [selectedDate, selectedTag, searchQuery, onFilterChange]);
|
||
|
||
const loadUserInfo = async () => {
|
||
try {
|
||
const userData = await userApi.getProfile();
|
||
dispatch(setUser(userData));
|
||
|
||
// Устанавливаем цвет акцента из профиля пользователя
|
||
const accent = userData.accent_color || "#007bff";
|
||
dispatch(setAccentColorAction(accent));
|
||
setAccentColor(accent);
|
||
|
||
// Загружаем AI настройки
|
||
try {
|
||
const aiSettings = await userApi.getAiSettings();
|
||
dispatch(setAiSettings(aiSettings));
|
||
} catch (aiError) {
|
||
console.error("Ошибка загрузки AI настроек:", aiError);
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка загрузки информации о пользователе:", error);
|
||
}
|
||
};
|
||
|
||
const handleLogout = async () => {
|
||
try {
|
||
await authApi.logout();
|
||
dispatch(clearAuth());
|
||
navigate("/");
|
||
} catch (error) {
|
||
console.error("Ошибка выхода:", error);
|
||
dispatch(clearAuth());
|
||
navigate("/");
|
||
}
|
||
};
|
||
|
||
const handleClearFilters = () => {
|
||
dispatch(setSelectedDate(null));
|
||
dispatch(setSelectedTag(null));
|
||
dispatch(setSearchQuery(""));
|
||
};
|
||
|
||
const hasFilters = !!(selectedDate || selectedTag || searchQuery);
|
||
|
||
// Формируем список активных фильтров
|
||
const getActiveFilters = () => {
|
||
const filters: string[] = [];
|
||
|
||
if (searchQuery) {
|
||
filters.push(`Поиск: "${searchQuery}"`);
|
||
}
|
||
|
||
if (selectedDate) {
|
||
filters.push(`Дата: ${selectedDate}`);
|
||
}
|
||
|
||
if (selectedTag) {
|
||
filters.push(`Тег: #${selectedTag}`);
|
||
}
|
||
|
||
return filters;
|
||
};
|
||
|
||
const activeFilters = getActiveFilters();
|
||
|
||
return (
|
||
<>
|
||
{/* Кнопка мобильного меню */}
|
||
{onToggleSidebar && (
|
||
<button className="mobile-menu-btn" onClick={onToggleSidebar}>
|
||
<Icon icon="mdi:menu" />
|
||
</button>
|
||
)}
|
||
|
||
<header className="notes-header">
|
||
<div className="notes-header-left">
|
||
<span>
|
||
<Icon icon="mdi:note-text" /> Мои заметки
|
||
</span>
|
||
{hasFilters && (
|
||
<div
|
||
className="filter-indicator"
|
||
style={{ display: "inline-block" }}
|
||
>
|
||
Фильтр: {activeFilters.join(", ")}{" "}
|
||
<button onClick={handleClearFilters}>✕</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="user-info">
|
||
{user?.avatar ? (
|
||
<div
|
||
className="user-avatar-mini"
|
||
style={{ display: "block" }}
|
||
title="Перейти в профиль"
|
||
onClick={() => navigate("/profile")}
|
||
>
|
||
<img src={user.avatar} alt="Аватар" loading="lazy" />
|
||
</div>
|
||
) : (
|
||
<div
|
||
className="user-avatar-mini user-avatar-placeholder-mini"
|
||
style={{ display: "flex" }}
|
||
title="Перейти в профиль"
|
||
onClick={() => navigate("/profile")}
|
||
>
|
||
<Icon icon="mdi:account" />
|
||
</div>
|
||
)}
|
||
<ThemeToggle />
|
||
<button
|
||
className="settings-icon-btn"
|
||
title="Настройки"
|
||
onClick={() => navigate("/settings")}
|
||
>
|
||
<Icon icon="mdi:cog" />
|
||
</button>
|
||
<button className="logout-btn" title="Выйти" onClick={handleLogout}>
|
||
<Icon icon="mdi:logout" />
|
||
</button>
|
||
</div>
|
||
</header>
|
||
</>
|
||
);
|
||
};
|