feat: add header channel selector & adding channel modal menu

This commit is contained in:
ivannoskov
2025-12-01 05:57:28 +03:00
parent d4672ea32b
commit d2cc1c39b3
22 changed files with 3473 additions and 892 deletions

View File

@@ -0,0 +1,101 @@
"use client";
// ============================================================================
// Target Channel Provider
// ============================================================================
import React, { createContext, useContext, useState, useEffect } from "react";
import { channelsApi } from "@/lib/api";
import type { TargetChannel } from "@/lib/types/api";
interface TargetChannelContextType {
channels: TargetChannel[];
selectedChannel: TargetChannel | null;
setSelectedChannel: (channel: TargetChannel | null) => void;
isLoading: boolean;
error: string | null;
refreshChannels: () => Promise<void>;
}
const TargetChannelContext = createContext<TargetChannelContextType | undefined>(
undefined
);
export const useTargetChannel = () => {
const context = useContext(TargetChannelContext);
if (!context) {
throw new Error(
"useTargetChannel must be used within TargetChannelProvider"
);
}
return context;
};
export const TargetChannelProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [channels, setChannels] = useState<TargetChannel[]>([]);
const [selectedChannel, setSelectedChannelState] = useState<TargetChannel | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const loadChannels = async () => {
try {
setIsLoading(true);
setError(null);
const response = await channelsApi.list();
const activeChannels = response.target_channels.filter((c) => c.is_active);
setChannels(activeChannels);
// Загружаем сохраненный канал из localStorage
const savedChannelId = localStorage.getItem("selected_channel_id");
if (savedChannelId && activeChannels.length > 0) {
const savedChannel = activeChannels.find((c) => c.id === savedChannelId);
if (savedChannel) {
setSelectedChannelState(savedChannel);
} else {
// Если сохраненный канал не найден, выбираем первый
setSelectedChannelState(activeChannels[0]);
localStorage.setItem("selected_channel_id", activeChannels[0].id);
}
} else if (activeChannels.length > 0) {
// Если нет сохраненного канала, выбираем первый
setSelectedChannelState(activeChannels[0]);
localStorage.setItem("selected_channel_id", activeChannels[0].id);
}
} catch (err: any) {
setError(err?.error?.message || "Ошибка загрузки каналов");
} finally {
setIsLoading(false);
}
};
const setSelectedChannel = (channel: TargetChannel | null) => {
setSelectedChannelState(channel);
if (channel) {
localStorage.setItem("selected_channel_id", channel.id);
} else {
localStorage.removeItem("selected_channel_id");
}
};
useEffect(() => {
loadChannels();
}, []);
return (
<TargetChannelContext.Provider
value={{
channels,
selectedChannel,
setSelectedChannel,
isLoading,
error,
refreshChannels: loadChannels,
}}
>
{children}
</TargetChannelContext.Provider>
);
};