|
|
@@ -1,4 +1,5 @@
|
|
|
|
|
|
+import { useState, useEffect } from "react";
|
|
|
import {
|
|
|
Sidebar,
|
|
|
SidebarContent,
|
|
|
@@ -7,20 +8,55 @@ import {
|
|
|
SidebarMenu,
|
|
|
SidebarMenuButton,
|
|
|
SidebarMenuItem,
|
|
|
+ SidebarMenuSub,
|
|
|
+ SidebarMenuSubItem,
|
|
|
+ SidebarMenuSubButton,
|
|
|
SidebarHeader,
|
|
|
SidebarFooter,
|
|
|
} from "@/components/ui/sidebar";
|
|
|
import { useNavigate } from "react-router-dom";
|
|
|
-import { LayoutDashboard, Phone, BarChart3, Settings, CreditCard, Layers3, PhoneCall, LogOut } from "lucide-react";
|
|
|
+import { LayoutDashboard, Phone, BarChart3, Settings, CreditCard, Layers3, PhoneCall, LogOut, Brain, Database, Store, ChevronDown } from "lucide-react";
|
|
|
import { useAuth } from "@/components/context/AuthContext";
|
|
|
+import { useShop } from "@/components/context/ShopContext";
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
import { LanguageSelector } from "./LanguageSelector";
|
|
|
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
|
|
+import { API_URL } from "@/lib/config";
|
|
|
|
|
|
export function AppSidebar() {
|
|
|
const { t } = useTranslation();
|
|
|
const navigate = useNavigate();
|
|
|
const currentPath = window.location.pathname;
|
|
|
const { logout } = useAuth();
|
|
|
+ const { selectedShop, setSelectedShop, stores, setStores } = useShop();
|
|
|
+ const [isAIMenuOpen, setIsAIMenuOpen] = useState(false);
|
|
|
+
|
|
|
+ // Fetch stores on component mount
|
|
|
+ useEffect(() => {
|
|
|
+ const fetchStores = async () => {
|
|
|
+ try {
|
|
|
+ const sessionData = localStorage.getItem('session_data');
|
|
|
+ if (!sessionData) return;
|
|
|
+
|
|
|
+ const session = JSON.parse(sessionData);
|
|
|
+ const response = await fetch(`${API_URL}/api/stores`, {
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${session.session.access_token}`,
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response.ok) {
|
|
|
+ const data = await response.json();
|
|
|
+ setStores(data.stores || []);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error fetching stores:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ fetchStores();
|
|
|
+ }, [setStores]);
|
|
|
|
|
|
const menuItems = [
|
|
|
{
|
|
|
@@ -56,11 +92,6 @@ export function AppSidebar() {
|
|
|
];
|
|
|
|
|
|
const configItems = [
|
|
|
- {
|
|
|
- title: t('sidebar.aiConfig'),
|
|
|
- icon: Settings,
|
|
|
- url: "/ai-config",
|
|
|
- },
|
|
|
{
|
|
|
title: "Billing & Plan",
|
|
|
icon: CreditCard,
|
|
|
@@ -68,15 +99,49 @@ export function AppSidebar() {
|
|
|
},
|
|
|
];
|
|
|
|
|
|
+ const handleShopChange = (shopId: string) => {
|
|
|
+ const shop = stores.find(s => s.id === shopId);
|
|
|
+ if (shop) {
|
|
|
+ setSelectedShop(shop);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
<Sidebar className="border-r border-slate-700/50 bg-slate-900 text-white">
|
|
|
- <SidebarHeader className="p-6 bg-slate-900">
|
|
|
+ <SidebarHeader className="p-4 bg-slate-900 space-y-4">
|
|
|
<div className="flex items-center gap-3">
|
|
|
<img src="/uploads/e0ddbf09-622c-426a-851f-149776e300c0.png" alt="ShopCall.ai" className="w-8 h-8" />
|
|
|
<span className="text-xl font-bold text-white">ShopCall.ai</span>
|
|
|
</div>
|
|
|
+
|
|
|
+ {/* Shop Selector */}
|
|
|
+ {stores.length > 0 && (
|
|
|
+ <div className="pt-2">
|
|
|
+ <div className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 px-1">
|
|
|
+ {t('sidebar.activeShop') || 'Active Shop'}
|
|
|
+ </div>
|
|
|
+ <Select value={selectedShop?.id || ''} onValueChange={handleShopChange}>
|
|
|
+ <SelectTrigger className="bg-slate-800 border-slate-600 text-white hover:bg-slate-700">
|
|
|
+ <div className="flex items-center gap-2 overflow-hidden">
|
|
|
+ <Store className="w-4 h-4 text-cyan-400 flex-shrink-0" />
|
|
|
+ <SelectValue placeholder="Select shop" />
|
|
|
+ </div>
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent className="bg-slate-800 border-slate-600">
|
|
|
+ {stores.map((store) => (
|
|
|
+ <SelectItem key={store.id} value={store.id} className="text-white hover:bg-slate-700">
|
|
|
+ <div className="flex flex-col items-start">
|
|
|
+ <div className="font-medium">{store.store_name || 'Unnamed Store'}</div>
|
|
|
+ <div className="text-xs text-slate-400 capitalize">{store.platform_name}</div>
|
|
|
+ </div>
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</SidebarHeader>
|
|
|
-
|
|
|
+
|
|
|
<SidebarContent className="px-3 bg-slate-900">
|
|
|
<SidebarGroup>
|
|
|
<SidebarGroupContent>
|
|
|
@@ -85,8 +150,8 @@ export function AppSidebar() {
|
|
|
const isActive = currentPath === item.url;
|
|
|
return (
|
|
|
<SidebarMenuItem key={item.title}>
|
|
|
- <SidebarMenuButton
|
|
|
- asChild
|
|
|
+ <SidebarMenuButton
|
|
|
+ asChild
|
|
|
className={`w-full justify-start text-slate-300 hover:text-white hover:bg-slate-800/50 ${
|
|
|
isActive ? 'bg-cyan-500 text-white hover:bg-cyan-600' : ''
|
|
|
}`}
|
|
|
@@ -103,7 +168,70 @@ export function AppSidebar() {
|
|
|
</SidebarGroupContent>
|
|
|
</SidebarGroup>
|
|
|
|
|
|
- <SidebarGroup className="mt-8">
|
|
|
+ {/* AI Assistant Section */}
|
|
|
+ <SidebarGroup className="mt-4">
|
|
|
+ <div className="px-3 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">
|
|
|
+ {t('sidebar.aiAssistant') || 'AI Assistant'}
|
|
|
+ </div>
|
|
|
+ <SidebarGroupContent>
|
|
|
+ <SidebarMenu>
|
|
|
+ <Collapsible open={isAIMenuOpen} onOpenChange={setIsAIMenuOpen}>
|
|
|
+ <SidebarMenuItem>
|
|
|
+ <CollapsibleTrigger asChild>
|
|
|
+ <SidebarMenuButton className="w-full justify-start text-slate-300 hover:text-white hover:bg-slate-800/50">
|
|
|
+ <div className="flex items-center gap-3 px-3 py-2 rounded-lg cursor-pointer w-full">
|
|
|
+ <Brain className="w-4 h-4" />
|
|
|
+ <span className="flex-1">{t('sidebar.aiMenu') || 'AI Configuration'}</span>
|
|
|
+ <ChevronDown className={`w-4 h-4 transition-transform ${isAIMenuOpen ? 'rotate-180' : ''}`} />
|
|
|
+ </div>
|
|
|
+ </SidebarMenuButton>
|
|
|
+ </CollapsibleTrigger>
|
|
|
+ <CollapsibleContent>
|
|
|
+ <SidebarMenuSub>
|
|
|
+ <SidebarMenuSubItem>
|
|
|
+ <SidebarMenuSubButton
|
|
|
+ asChild
|
|
|
+ className={`text-slate-300 hover:text-white hover:bg-slate-800/50 ${
|
|
|
+ currentPath === '/ai-config' ? 'bg-cyan-500/20 text-cyan-400' : ''
|
|
|
+ }`}
|
|
|
+ >
|
|
|
+ <a onClick={() => navigate('/ai-config')} className="flex items-center gap-2 cursor-pointer">
|
|
|
+ <Settings className="w-3 h-3" />
|
|
|
+ <span>{t('sidebar.configuration') || 'Configuration'}</span>
|
|
|
+ </a>
|
|
|
+ </SidebarMenuSubButton>
|
|
|
+ </SidebarMenuSubItem>
|
|
|
+ <SidebarMenuSubItem>
|
|
|
+ <SidebarMenuSubButton
|
|
|
+ asChild
|
|
|
+ className={`text-slate-300 hover:text-white hover:bg-slate-800/50 ${
|
|
|
+ currentPath === '/manage-store-data' ? 'bg-cyan-500/20 text-cyan-400' : ''
|
|
|
+ }`}
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ onClick={() => {
|
|
|
+ if (selectedShop) {
|
|
|
+ navigate(`/manage-store-data?shop=${selectedShop.id}`);
|
|
|
+ } else {
|
|
|
+ navigate('/manage-store-data');
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ className="flex items-center gap-2 cursor-pointer"
|
|
|
+ >
|
|
|
+ <Database className="w-3 h-3" />
|
|
|
+ <span>{t('sidebar.knowledgeBase') || 'Knowledge Base'}</span>
|
|
|
+ </a>
|
|
|
+ </SidebarMenuSubButton>
|
|
|
+ </SidebarMenuSubItem>
|
|
|
+ </SidebarMenuSub>
|
|
|
+ </CollapsibleContent>
|
|
|
+ </SidebarMenuItem>
|
|
|
+ </Collapsible>
|
|
|
+ </SidebarMenu>
|
|
|
+ </SidebarGroupContent>
|
|
|
+ </SidebarGroup>
|
|
|
+
|
|
|
+ <SidebarGroup className="mt-4">
|
|
|
<div className="px-3 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">
|
|
|
{t('sidebar.configuration')}
|
|
|
</div>
|
|
|
@@ -113,8 +241,8 @@ export function AppSidebar() {
|
|
|
const isActive = currentPath === item.url;
|
|
|
return (
|
|
|
<SidebarMenuItem key={item.title}>
|
|
|
- <SidebarMenuButton
|
|
|
- asChild
|
|
|
+ <SidebarMenuButton
|
|
|
+ asChild
|
|
|
className={`w-full justify-start text-slate-300 hover:text-white hover:bg-slate-800/50 ${
|
|
|
isActive ? 'bg-cyan-500 text-white hover:bg-cyan-600' : ''
|
|
|
}`}
|