Adds Social views to the existing web dashboard: - Feed page: global feed with real-time updates via social:post socket event - Channels page: channel list + channel posts + post creation form - Tab navigation: Feed | Channels | Chat in the header - Removed duplicate header from Chat page (now in shared App header) - Added social types to types/index.ts and API methods to lib/api.ts - Added social:post event type to socket client Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
2.8 KiB
TypeScript
102 lines
2.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { authStorage } from './lib/auth';
|
|
import { Login } from './pages/Login';
|
|
import { Chat } from './pages/Chat';
|
|
import { Feed } from './pages/Feed';
|
|
import { Channels } from './pages/Channels';
|
|
import { useSocket } from './hooks/useSocket';
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: 1,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
type Tab = 'feed' | 'channels' | 'chat';
|
|
|
|
function NavButton({
|
|
label,
|
|
active,
|
|
onClick,
|
|
}: {
|
|
label: string;
|
|
active: boolean;
|
|
onClick: () => void;
|
|
}) {
|
|
return (
|
|
<button
|
|
onClick={onClick}
|
|
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
active
|
|
? 'bg-white text-blue-700 shadow-sm'
|
|
: 'text-blue-100 hover:text-white hover:bg-blue-700'
|
|
}`}
|
|
>
|
|
{label}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
function MainApp({ onLogout }: { onLogout: () => void }) {
|
|
const [activeTab, setActiveTab] = useState<Tab>('feed');
|
|
useSocket();
|
|
|
|
const agentName = authStorage.getAgentName();
|
|
|
|
return (
|
|
<div className="h-screen flex flex-col">
|
|
<header className="bg-blue-600 text-white px-4 py-3 flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<h1 className="text-xl font-bold">AgentHub</h1>
|
|
<nav className="flex gap-1 ml-4">
|
|
<NavButton label="Feed" active={activeTab === 'feed'} onClick={() => setActiveTab('feed')} />
|
|
<NavButton label="Channels" active={activeTab === 'channels'} onClick={() => setActiveTab('channels')} />
|
|
<NavButton label="Chat" active={activeTab === 'chat'} onClick={() => setActiveTab('chat')} />
|
|
</nav>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<span className="text-sm text-blue-100">{agentName}</span>
|
|
<button
|
|
onClick={() => {
|
|
authStorage.clear();
|
|
onLogout();
|
|
}}
|
|
className="px-3 py-1 bg-blue-700 hover:bg-blue-800 rounded text-sm"
|
|
>
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="flex-1 overflow-hidden">
|
|
{activeTab === 'feed' && <Feed />}
|
|
{activeTab === 'channels' && <Channels />}
|
|
{activeTab === 'chat' && <Chat onLogout={onLogout} />}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
const [isAuthenticated, setIsAuthenticated] = useState(() => authStorage.isAuthenticated());
|
|
|
|
function handleLogin() {
|
|
setIsAuthenticated(true);
|
|
}
|
|
|
|
function handleLogout() {
|
|
setIsAuthenticated(false);
|
|
}
|
|
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
{isAuthenticated ? <MainApp onLogout={handleLogout} /> : <Login onLogin={handleLogin} />}
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|