fix: remove tenant switcher from TopBar, fix aiProactive API double-prefix
- Remove tenant switcher dropdown from TopBar (cleanup unused vars/imports) - Fix aiProactive.ts: remove /api/v1 prefix from apiClient calls (baseURL already includes it) - This fixes 404 errors on /api/v1/api/v1/ai-proactive/suggestions
This commit is contained in:
@@ -21,7 +21,7 @@ export function useSuggestions() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initial load
|
// Initial load
|
||||||
apiClient.get('/api/v1/ai-proactive/suggestions').then(r => {
|
apiClient.get('/ai-proactive/suggestions').then(r => {
|
||||||
setSuggestions(r.data.items);
|
setSuggestions(r.data.items);
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
|
||||||
@@ -41,11 +41,11 @@ export function useSuggestions() {
|
|||||||
|
|
||||||
const dismiss = useCallback(async (id: string) => {
|
const dismiss = useCallback(async (id: string) => {
|
||||||
setSuggestions(prev => prev.filter(s => s.id !== id));
|
setSuggestions(prev => prev.filter(s => s.id !== id));
|
||||||
await apiClient.post(`/api/v1/ai-proactive/suggestions/${id}/dismiss`).catch(() => {});
|
await apiClient.post(`/ai-proactive/suggestions/${id}/dismiss`).catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const act = useCallback(async (id: string, actionIndex: number) => {
|
const act = useCallback(async (id: string, actionIndex: number) => {
|
||||||
const result = await apiClient.post(`/api/v1/ai-proactive/suggestions/${id}/act`, {
|
const result = await apiClient.post(`/ai-proactive/suggestions/${id}/act`, {
|
||||||
action_index: actionIndex
|
action_index: actionIndex
|
||||||
});
|
});
|
||||||
setSuggestions(prev => prev.map(s => s.id === id ? { ...s, is_acted_upon: true } : s));
|
setSuggestions(prev => prev.map(s => s.id === id ? { ...s, is_acted_upon: true } : s));
|
||||||
@@ -59,11 +59,11 @@ export function useProactiveSettings() {
|
|||||||
const [settings, setSettings] = useState<any>(null);
|
const [settings, setSettings] = useState<any>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
apiClient.get('/api/v1/ai-proactive/settings').then(r => setSettings(r.data));
|
apiClient.get('/ai-proactive/settings').then(r => setSettings(r.data));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const update = useCallback(async (updates: any) => {
|
const update = useCallback(async (updates: any) => {
|
||||||
const r = await apiClient.put('/api/v1/ai-proactive/settings', updates);
|
const r = await apiClient.put('/ai-proactive/settings', updates);
|
||||||
setSettings(r.data);
|
setSettings(r.data);
|
||||||
return r.data;
|
return r.data;
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useUIStore } from '@/store/uiStore';
|
import { useUIStore } from '@/store/uiStore';
|
||||||
import { useTenant } from '@/hooks/useTenant';
|
|
||||||
import { useLogout } from '@/api/hooks';
|
import { useLogout } from '@/api/hooks';
|
||||||
import { Avatar } from '@/components/ui/Avatar';
|
import { Avatar } from '@/components/ui/Avatar';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
@@ -16,19 +15,15 @@ export function TopBar() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user } = useAuthStore();
|
const { user } = useAuthStore();
|
||||||
const { toggleSidebar, openAISidebarProactive } = useUIStore();
|
const { toggleSidebar, openAISidebarProactive } = useUIStore();
|
||||||
const { currentTenant, availableTenants, switchTenant, isSwitching } = useTenant();
|
|
||||||
const logoutMutation = useLogout();
|
const logoutMutation = useLogout();
|
||||||
|
|
||||||
const [tenantMenuOpen, setTenantMenuOpen] = useState(false);
|
|
||||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||||
const [notifOpen, setNotifOpen] = useState(false);
|
const [notifOpen, setNotifOpen] = useState(false);
|
||||||
const tenantRef = useRef<HTMLDivElement>(null);
|
|
||||||
const userRef = useRef<HTMLDivElement>(null);
|
const userRef = useRef<HTMLDivElement>(null);
|
||||||
const notifRef = useRef<HTMLDivElement>(null);
|
const notifRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
if (tenantRef.current && !tenantRef.current.contains(e.target as Node)) setTenantMenuOpen(false);
|
|
||||||
if (userRef.current && !userRef.current.contains(e.target as Node)) setUserMenuOpen(false);
|
if (userRef.current && !userRef.current.contains(e.target as Node)) setUserMenuOpen(false);
|
||||||
if (notifRef.current && !notifRef.current.contains(e.target as Node)) setNotifOpen(false);
|
if (notifRef.current && !notifRef.current.contains(e.target as Node)) setNotifOpen(false);
|
||||||
};
|
};
|
||||||
@@ -45,11 +40,6 @@ export function TopBar() {
|
|||||||
navigate('/login');
|
navigate('/login');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTenantSwitch = async (tenantId: string) => {
|
|
||||||
await switchTenant(tenantId);
|
|
||||||
setTenantMenuOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fullName = user ? `${user.first_name} ${user.last_name}` : '';
|
const fullName = user ? `${user.first_name} ${user.last_name}` : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -70,56 +60,6 @@ export function TopBar() {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Tenant switcher */}
|
|
||||||
<div ref={tenantRef} className="relative">
|
|
||||||
<button
|
|
||||||
onClick={() => setTenantMenuOpen(!tenantMenuOpen)}
|
|
||||||
className="flex items-center gap-2 px-3 py-2 rounded-md hover:bg-secondary-100 min-h-touch focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
|
||||||
aria-label={t('topbar.switchTenant')}
|
|
||||||
aria-expanded={tenantMenuOpen}
|
|
||||||
aria-haspopup="listbox"
|
|
||||||
>
|
|
||||||
<svg className="w-4 h-4 text-secondary-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|
||||||
</svg>
|
|
||||||
<span className="text-sm font-medium text-secondary-700 max-w-32 truncate">
|
|
||||||
{currentTenant?.name || t('topbar.tenant')}
|
|
||||||
</span>
|
|
||||||
<svg className="w-4 h-4 text-secondary-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
{tenantMenuOpen && (
|
|
||||||
<ul
|
|
||||||
className="absolute top-full left-0 mt-1 w-56 bg-white rounded-md shadow-lg border border-secondary-200 py-1 z-50"
|
|
||||||
role="listbox"
|
|
||||||
aria-label={t('topbar.switchTenant')}
|
|
||||||
>
|
|
||||||
{availableTenants.map((tenant) => (
|
|
||||||
<li key={tenant.id}>
|
|
||||||
<button
|
|
||||||
onClick={() => handleTenantSwitch(tenant.id)}
|
|
||||||
className={clsx(
|
|
||||||
'w-full text-left px-3 py-2 text-sm hover:bg-secondary-50 min-h-touch flex items-center justify-between focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
|
|
||||||
tenant.id === currentTenant?.id && 'bg-primary-50 text-primary-700'
|
|
||||||
)}
|
|
||||||
role="option"
|
|
||||||
aria-selected={tenant.id === currentTenant?.id}
|
|
||||||
disabled={isSwitching}
|
|
||||||
>
|
|
||||||
{tenant.name}
|
|
||||||
{tenant.id === currentTenant?.id && (
|
|
||||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
<SearchDropdown placeholder={t('topbar.search')} />
|
<SearchDropdown placeholder={t('topbar.search')} />
|
||||||
|
|||||||
Reference in New Issue
Block a user