Skip to content

Instantly share code, notes, and snippets.

@moosh3
Created May 1, 2025 12:25
Show Gist options
  • Save moosh3/379aeb8864bb9ce86f6aed1b689cd77a to your computer and use it in GitHub Desktop.
Save moosh3/379aeb8864bb9ce86f6aed1b689cd77a to your computer and use it in GitHub Desktop.
import React, { useState } from 'react';
import { Users, Bot, FileText, Search, Trash2, CheckCircle, XCircle } from 'lucide-react';
import Button from '../components/ui/Button';
import Tabs from '../components/ui/Tabs';
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
createdAt: Date;
lastLogin?: Date;
}
interface PromptRequest {
id: string;
firstName: string;
lastName: string;
jobTitle: string;
promptRequest: string;
useCase: string;
status: 'pending' | 'approved' | 'rejected';
createdAt: Date;
}
const mockUsers: User[] = [
{
id: '1',
name: 'John Doe',
email: '[email protected]',
role: 'admin',
createdAt: new Date(2024, 0, 15),
lastLogin: new Date(2024, 2, 20),
},
{
id: '2',
name: 'Jane Smith',
email: '[email protected]',
role: 'user',
createdAt: new Date(2024, 1, 1),
lastLogin: new Date(2024, 2, 19),
},
];
const mockPromptRequests: PromptRequest[] = [
{
id: '1',
firstName: 'Michael',
lastName: 'Johnson',
jobTitle: 'Sales Manager',
promptRequest: 'Need a prompt for analyzing customer feedback',
useCase: 'Analyzing customer feedback to improve product features',
status: 'pending',
createdAt: new Date(2024, 2, 15),
},
{
id: '2',
firstName: 'Sarah',
lastName: 'Williams',
jobTitle: 'Marketing Director',
promptRequest: 'Content generation for social media',
useCase: 'Creating engaging social media posts',
status: 'approved',
createdAt: new Date(2024, 2, 10),
},
];
const Admin: React.FC = () => {
const [activeTab, setActiveTab] = useState('users');
const [searchQuery, setSearchQuery] = useState('');
const [users, setUsers] = useState<User[]>(mockUsers);
const [promptRequests, setPromptRequests] = useState<PromptRequest[]>(mockPromptRequests);
const handleDeleteUser = (userId: string) => {
setUsers(users.filter(user => user.id !== userId));
};
const handleUpdateUserRole = (userId: string, newRole: 'admin' | 'user') => {
setUsers(users.map(user =>
user.id === userId ? { ...user, role: newRole } : user
));
};
const handlePromptRequestAction = (requestId: string, action: 'approved' | 'rejected') => {
setPromptRequests(requests =>
requests.map(request =>
request.id === requestId ? { ...request, status: action } : request
)
);
};
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
user.email.toLowerCase().includes(searchQuery.toLowerCase())
);
const filteredPromptRequests = promptRequests.filter(request =>
request.firstName.toLowerCase().includes(searchQuery.toLowerCase()) ||
request.lastName.toLowerCase().includes(searchQuery.toLowerCase()) ||
request.promptRequest.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<div className="flex h-full flex-col bg-background">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<h1 className="text-xl font-semibold text-text">Admin Panel</h1>
</div>
<div className="flex-1 overflow-hidden p-6">
<div className="mb-6">
<Tabs
tabs={[
{ id: 'users', label: 'Users' },
{ id: 'prompts', label: 'Prompt Requests' },
{ id: 'agents', label: 'Agents' },
]}
activeTab={activeTab}
onTabChange={setActiveTab}
/>
</div>
<div className="mb-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" />
<input
type="text"
placeholder={`Search ${activeTab}...`}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full rounded-md border border-border bg-background pl-10 pr-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
/>
</div>
</div>
{activeTab === 'users' && (
<div className="rounded-lg border border-border">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-border bg-background-secondary">
<th className="px-6 py-3 text-left text-sm font-medium text-text">Name</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Email</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Role</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Created</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Last Login</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{filteredUsers.map((user) => (
<tr key={user.id} className="bg-background hover:bg-background-secondary">
<td className="px-6 py-4">
<div className="flex items-center">
<div className="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center">
<span className="text-sm font-medium text-primary">
{user.name.charAt(0)}
</span>
</div>
<span className="ml-3 text-sm text-text">{user.name}</span>
</div>
</td>
<td className="px-6 py-4 text-sm text-text-secondary">{user.email}</td>
<td className="px-6 py-4">
<select
value={user.role}
onChange={(e) => handleUpdateUserRole(user.id, e.target.value as 'admin' | 'user')}
className="rounded-md border border-border bg-background px-2 py-1 text-sm text-text"
>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</td>
<td className="px-6 py-4 text-sm text-text-secondary">
{user.createdAt.toLocaleDateString()}
</td>
<td className="px-6 py-4 text-sm text-text-secondary">
{user.lastLogin?.toLocaleDateString() || 'Never'}
</td>
<td className="px-6 py-4">
<button
onClick={() => handleDeleteUser(user.id)}
className="text-red-500 hover:text-red-600"
>
<Trash2 className="h-4 w-4" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{activeTab === 'prompts' && (
<div className="space-y-4">
{filteredPromptRequests.map((request) => (
<div
key={request.id}
className="rounded-lg border border-border bg-background p-6"
>
<div className="mb-4 flex items-center justify-between">
<div>
<h3 className="text-lg font-medium text-text">
{request.firstName} {request.lastName}
</h3>
<p className="text-sm text-text-secondary">{request.jobTitle}</p>
</div>
<div className="flex items-center space-x-2">
<span className={`rounded-full px-3 py-1 text-sm font-medium ${
request.status === 'pending' ? 'bg-yellow-100 text-yellow-800' :
request.status === 'approved' ? 'bg-green-100 text-green-800' :
'bg-red-100 text-red-800'
}`}>
{request.status.charAt(0).toUpperCase() + request.status.slice(1)}
</span>
<span className="text-sm text-text-secondary">
{request.createdAt.toLocaleDateString()}
</span>
</div>
</div>
<div className="mb-4">
<h4 className="mb-1 text-sm font-medium text-text">Prompt Request</h4>
<p className="text-text-secondary">{request.promptRequest}</p>
</div>
<div className="mb-4">
<h4 className="mb-1 text-sm font-medium text-text">Use Case</h4>
<p className="text-text-secondary">{request.useCase}</p>
</div>
{request.status === 'pending' && (
<div className="flex justify-end space-x-3">
<Button
variant="outline"
onClick={() => handlePromptRequestAction(request.id, 'rejected')}
icon={<XCircle className="h-4 w-4" />}
>
Reject
</Button>
<Button
variant="primary"
onClick={() => handlePromptRequestAction(request.id, 'approved')}
icon={<CheckCircle className="h-4 w-4" />}
>
Approve
</Button>
</div>
)}
</div>
))}
</div>
)}
{activeTab === 'agents' && (
<div className="rounded-lg border border-border">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-border bg-background-secondary">
<th className="px-6 py-3 text-left text-sm font-medium text-text">Name</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Category</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Created By</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Created At</th>
<th className="px-6 py-3 text-left text-sm font-medium text-text">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{/* Add agent rows here when implementing agent management */}
</tbody>
</table>
</div>
</div>
)}
</div>
</div>
);
};
export default Admin;
import React, { useState } from 'react';
import Sidebar from './components/Sidebar';
import Dashboard from './pages/Dashboard';
import Chat from './components/Chat';
import Content from './pages/Content';
import Runs from './pages/Runs';
import RunConfig from './pages/RunConfig';
import RunOutput from './pages/RunOutput';
import Teams from './pages/Teams';
import Prompts from './pages/Prompts';
import Settings from './pages/Settings';
import Landing from './pages/Landing';
import WorkflowEditor from './components/workflow/WorkflowEditor';
import { Agent } from './types';
import { useAuthStore } from './stores/authStore';
function App() {
const { user } = useAuthStore();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [currentChat, setCurrentChat] = useState<Agent | null>(null);
const [currentView, setCurrentView] = useState<'dashboard' | 'chat' | 'content' | 'runs' | 'run-config' | 'run-output' | 'teams' | 'workflows' | 'prompts' | 'settings'>('dashboard');
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
const handleStartChat = (agent: Agent) => {
setCurrentChat(agent);
setCurrentView('chat');
};
const handleNavigate = (view: 'dashboard' | 'chat' | 'content' | 'runs' | 'teams' | 'workflows' | 'prompts' | 'settings') => {
setCurrentView(view);
if (view === 'chat' && !currentChat) {
setCurrentChat({
id: 'default-agent',
name: 'AI Assistant',
description: 'Your general-purpose AI assistant',
icon: 'default',
category: 'popular'
});
}
};
const handleAgentSelect = (agent: Agent) => {
setSelectedAgent(agent);
setCurrentView('run-config');
};
const handleRunAgent = (config: any) => {
setCurrentView('run-output');
};
if (!user) {
return <Landing onSignIn={() => useAuthStore.getState().signIn('[email protected]', 'password')} />;
}
return (
<div className="flex h-screen overflow-hidden">
<Sidebar
collapsed={sidebarCollapsed}
onToggle={() => setSidebarCollapsed(!sidebarCollapsed)}
onStartChat={handleStartChat}
onNavigate={handleNavigate}
currentView={currentView}
/>
<div className="flex-1 overflow-auto">
{currentView === 'chat' ? (
<Chat
agent={currentChat!}
onBack={() => {
setCurrentView('dashboard');
setCurrentChat(null);
}}
/>
) : currentView === 'content' ? (
<Content />
) : currentView === 'runs' ? (
<Runs />
) : currentView === 'teams' ? (
<Teams />
) : currentView === 'workflows' ? (
<WorkflowEditor />
) : currentView === 'prompts' ? (
<Prompts />
) : currentView === 'settings' ? (
<Settings />
) : currentView === 'run-config' && selectedAgent ? (
<RunConfig
agent={selectedAgent}
onBack={() => {
setCurrentView('dashboard');
setSelectedAgent(null);
}}
onRun={handleRunAgent}
/>
) : currentView === 'run-output' && selectedAgent ? (
<RunOutput
agent={selectedAgent}
onBack={() => {
setCurrentView('dashboard');
setSelectedAgent(null);
}}
/>
) : (
<Dashboard
onStartChat={handleStartChat}
onAgentSelect={handleAgentSelect}
/>
)}
</div>
</div>
);
}
export default App;
import React, { useState } from 'react';
import { Camera, User, Settings as SettingsIcon } from 'lucide-react';
import Button from '../components/ui/Button';
import Tabs from '../components/ui/Tabs';
import Admin from './Admin';
import { useAuthStore } from '../stores/authStore';
const Settings: React.FC = () => {
const { user } = useAuthStore();
const [activeTab, setActiveTab] = useState('profile');
const [name, setName] = useState(user?.name || '');
const [email, setEmail] = useState(user?.email || '');
const [avatar, setAvatar] = useState(user?.avatar_url || '');
const handleSave = () => {
// Handle saving user profile
console.log('Saving profile:', { name, email, avatar });
};
return (
<div className="flex h-full flex-col bg-background">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<div className="flex items-center space-x-3">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10">
<SettingsIcon className="h-4 w-4 text-primary" />
</div>
<h1 className="text-xl font-semibold text-text">Settings</h1>
</div>
</div>
<div className="flex-1 overflow-hidden">
<div className="h-full flex">
<div className="w-64 border-r border-border bg-background p-4">
<Tabs
tabs={[
{ id: 'profile', label: 'Profile' },
...(user?.role === 'admin' ? [{ id: 'admin', label: 'Admin' }] : []),
]}
activeTab={activeTab}
onTabChange={setActiveTab}
/>
</div>
<div className="flex-1 p-6 overflow-y-auto">
{activeTab === 'profile' ? (
<div className="max-w-2xl">
<div className="mb-8">
<div className="flex items-center space-x-4">
<div className="relative">
{avatar ? (
<img
src={avatar}
alt={name}
className="h-20 w-20 rounded-full object-cover"
/>
) : (
<div className="flex h-20 w-20 items-center justify-center rounded-full bg-primary/10">
<User className="h-8 w-8 text-primary" />
</div>
)}
<button className="absolute bottom-0 right-0 rounded-full bg-white p-1.5 shadow-md hover:bg-gray-50">
<Camera className="h-4 w-4 text-gray-600" />
</button>
</div>
<div>
<h2 className="text-lg font-medium text-text">{name}</h2>
<p className="text-sm text-text-secondary">{email}</p>
</div>
</div>
</div>
<div className="space-y-6">
<div>
<label className="mb-1 block text-sm font-medium text-text">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-text">
Email
</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
/>
</div>
<div>
<Button variant="primary" onClick={handleSave}>
Save Changes
</Button>
</div>
</div>
</div>
) : activeTab === 'admin' && user?.role === 'admin' ? (
<Admin />
) : null}
</div>
</div>
</div>
</div>
);
};
export default Settings;
export interface User {
id: string;
email: string;
name: string;
avatar_url?: string;
role?: 'admin' | 'user';
}
export interface Agent {
id: string;
name: string;
description: string;
icon: string;
category: AgentCategory;
isCustom?: boolean;
createdAt?: Date;
schedule?: Schedule;
variables?: AgentVariable[];
instructions?: string;
}
export type AgentCategory =
| 'customer-service'
| 'sales'
| 'engineering'
| 'support'
| 'marketing'
| 'it'
| 'hr'
| 'popular';
export interface AgentVariable {
name: string;
description: string;
value?: string;
}
export interface Schedule {
enabled: boolean;
frequency: 'hourly' | 'daily' | 'weekly' | 'monthly';
interval: number;
time?: string;
days?: number[];
date?: number;
timezone: string;
lastRun?: Date;
nextRun?: Date;
}
export interface Team {
id: string;
name: string;
description: string;
type: 'route' | 'coordinate' | 'collaborate';
agents: Agent[];
createdAt: Date;
}
export interface Run {
id: string;
startTime: Date;
endTime?: Date;
triggeredBy: {
id: string;
name: string;
avatar?: string;
};
status: 'running' | 'completed' | 'failed';
duration?: number;
logs: string[];
input: Record<string, any>;
output?: Record<string, any>;
error?: string;
resourceUsage?: {
cpuTime: number;
memoryUsage: number;
apiCalls: number;
};
}
export interface AgentRun extends Run {
agentId: string;
agentName: string;
}
export interface TeamRun extends Run {
teamId: string;
teamName: string;
teamType: Team['type'];
agentRuns: AgentRun[];
}
export interface FilterOption {
id: string;
label: string;
value: string;
}
export interface TabOption {
id: string;
label: string;
count?: number;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment