Admin Assistant Guide
Building the AI-powered admin assistant with navigation and form-filling capabilities.
Admin Assistant Guide
The Admin Assistant is an AI-powered sidebar that helps administrators navigate the dashboard and fill out forms. It uses the Vercel AI SDK with streaming responses for a responsive user experience.
Features
- Conversational Interface - Natural language chat in a slide-out sidebar
- Smart Navigation - AI can redirect users to relevant pages
- Form Autopilot - AI can fill form fields with generated content
- Streaming Responses - Real-time typewriter effect for responses
- Context Awareness - Knows which page and form the user is on
Architecture
Components
Assistant Sidebar
The main chat interface that slides in from the right:
'use client';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@repo/ui/components/ui/sheet';
import { Button } from '@repo/ui/components/ui/button';
import { Input } from '@repo/ui/components/ui/input';
import { ScrollArea } from '@repo/ui/components/ui/scroll-area';
import { useChatStream } from './use-chat-stream';
export function AssistantSidebar({ isOpen, onClose }) {
const router = useRouter();
const [input, setInput] = useState('');
const { messages, isLoading, sendMessage, clearMessages } = useChatStream({
api: '/api/admin-assistant',
onNavigate: (path) => router.push(path),
onFormField: (update) => {
// Handle form field updates
},
});
return (
<Sheet open={isOpen} onOpenChange={(open) => !open && onClose()}>
<SheetContent side="right" className="w-[400px]">
<SheetHeader>
<SheetTitle>BISO Assistant</SheetTitle>
</SheetHeader>
<ScrollArea className="flex-1">
{messages.map((message) => (
<AssistantMessage key={message.id} message={message} />
))}
</ScrollArea>
<form onSubmit={(e) => { e.preventDefault(); sendMessage(input); setInput(''); }}>
<Input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask me anything..."
/>
<Button type="submit" disabled={isLoading}>Send</Button>
</form>
</SheetContent>
</Sheet>
);
}Assistant Trigger
A button in the header to open the assistant:
'use client';
import { Button } from '@repo/ui/components/ui/button';
import { Sparkles } from 'lucide-react';
export function AssistantTrigger({ onClick }) {
return (
<Button variant="ghost" size="icon" onClick={onClick}>
<Sparkles className="h-5 w-5" />
<span className="sr-only">Open AI Assistant</span>
</Button>
);
}Chat Stream Hook
Handles streaming responses and action parsing:
'use client';
export function useChatStream({ api, onNavigate, onFormField, formContext }) {
const [messages, setMessages] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const sendMessage = useCallback(async (content: string) => {
// Add user message
const userMessage = { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: content }] };
setMessages(prev => [...prev, userMessage]);
setIsLoading(true);
try {
const response = await fetch(api, {
method: 'POST',
body: JSON.stringify({ messages: [...messages, userMessage], formContext }),
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
let fullText = '';
// Add assistant message placeholder
const assistantMessage = { id: crypto.randomUUID(), role: 'assistant', parts: [{ type: 'text', text: '' }] };
setMessages(prev => [...prev, assistantMessage]);
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
// Parse SSE data and handle actions...
}
} finally {
setIsLoading(false);
}
}, [api, messages, formContext]);
return { messages, isLoading, sendMessage, clearMessages: () => setMessages([]) };
}API Route
The server-side handler that processes requests:
import { openai } from "@ai-sdk/openai";
import { streamText, convertToModelMessages, stepCountIs } from "ai";
import { createNavigationTool, defaultAdminRoutes } from "@repo/ai/tools/navigation";
import { createFormFillerTool, eventFormFields } from "@repo/ai/tools/form-filler";
import { ADMIN_ASSISTANT_PROMPT } from "@repo/ai/prompts";
export const maxDuration = 60;
export async function POST(req: Request) {
const { messages, formContext } = await req.json();
// Create tools
const navigate = createNavigationTool(defaultAdminRoutes);
const tools: Record<string, unknown> = { navigate };
// Add form filler if context provided
if (formContext) {
tools.fillFormFields = createFormFillerTool(formContext.fields);
} else {
tools.fillFormFields = createFormFillerTool(eventFormFields);
}
const result = streamText({
model: openai("gpt-5-mini"),
messages: convertToModelMessages(messages),
stopWhen: stepCountIs(10),
system: ADMIN_ASSISTANT_PROMPT,
tools,
});
return result.toUIMessageStreamResponse();
}Integration with Admin Layout
Add the assistant to the admin layout:
import { AssistantSidebar } from './assistant/assistant-sidebar';
import { AssistantTrigger } from './assistant/assistant-trigger';
export function AdminLayout({ children }) {
const [isAssistantOpen, setIsAssistantOpen] = useState(false);
return (
<div className="flex h-screen">
<nav>{/* Sidebar navigation */}</nav>
<div className="flex-1">
<header className="flex items-center justify-between p-4">
<Breadcrumb />
<div className="flex items-center gap-2">
<AssistantTrigger onClick={() => setIsAssistantOpen(true)} />
<ModeToggle />
</div>
</header>
<main>{children}</main>
</div>
<AssistantSidebar
isOpen={isAssistantOpen}
onClose={() => setIsAssistantOpen(false)}
/>
</div>
);
}Form Autopilot Integration
Connect the assistant to forms for autopilot filling:
Define Form Fields
Create a field definition for your form:
const eventFields: FormFieldInfo[] = [
{ id: 'translations.en.title', type: 'text', label: 'English Title', required: true },
{ id: 'translations.en.description', type: 'textarea', label: 'English Description' },
// ... more fields
];Use the Form Autopilot Hook
Connect the hook to your form:
import { useFormAutopilot } from '@repo/ai/hooks/use-form-autopilot';
function EventEditor() {
const form = useForm<FormValues>();
const { getFormContext, handleFormStream, isActive } = useFormAutopilot({
formId: 'event-editor',
formName: 'Event Editor',
fields: eventFields,
onSetValue: (fieldId, value) => form.setValue(fieldId, value),
onGetValue: (fieldId) => form.getValues(fieldId),
});
// Pass formContext to the assistant
// Handle form stream events from AI
}Pass Context to Assistant
Include form context in your chat requests:
const { messages, sendMessage } = useChatStream({
api: '/api/admin-assistant',
formContext: getFormContext(),
onFormField: handleFormStream,
});System Prompt
The admin assistant uses a specialized system prompt:
export const ADMIN_ASSISTANT_PROMPT = `You are BISO Assistant, an AI helper for the BISO admin dashboard.
## Your Capabilities
- Navigate users to different pages in the admin dashboard
- Fill out forms with content based on user descriptions
- Answer questions about the admin system
- Provide guidance on creating events, posts, and other content
## Guidelines
- Be concise and helpful
- When asked to create something, navigate to the appropriate page first
- When filling forms, ask clarifying questions if needed
- Support both English and Norwegian content
- Always confirm actions before taking them
## Available Pages
- /admin - Dashboard
- /admin/events - Events list
- /admin/events/new - Create event
- /admin/posts - Posts list
- /admin/shop - Shop management
- /admin/users - User management
`;Example Interactions
Creating an Event
User: "Create a new event"
Assistant: [Navigates to /admin/events/new]
"I've taken you to the event creation page. What kind of event would you like to create?"
User: "A networking event for students on December 15th"
Assistant: [Fills title, date fields]
"I've filled in the basic details. Would you like me to add a description?"
User: "Yes, make it sound professional"
Assistant: [Streams description into textarea]
"Done! I've added a professional description. Would you like to add a location or ticket price?"Navigating the Dashboard
User: "Show me all orders"
Assistant: [Navigates to /admin/shop/orders]
"Here are all your orders. You can filter by status or search for specific orders."
User: "How do I add a new product?"
Assistant: "To add a new product, go to Shop → Products → Add Product. Would you like me to take you there?"
User: "Yes please"
Assistant: [Navigates to /admin/shop/products/new]
"Here's the product creation form. What product would you like to add?"Customization
Custom System Prompt
Extend the default prompt with additional context:
import { ADMIN_ASSISTANT_PROMPT } from '@repo/ai/prompts';
const customPrompt = `${ADMIN_ASSISTANT_PROMPT}
## Additional Context
- Current user: ${userName}
- User role: ${userRole}
- Current page: ${currentPath}
- Organization: BISO (Business and IT Student Organization)
`;Custom Routes
Add routes for your specific application:
const customRoutes: RouteInfo[] = [
...defaultAdminRoutes,
{
path: '/admin/custom-feature',
label: 'Custom Feature',
description: 'Your custom admin feature',
requiredRoles: ['Admin'],
},
];
const navigate = createNavigationTool(customRoutes);Custom Form Fields
Define fields for your specific forms:
const productFormFields: FormFieldInfo[] = [
{ id: 'name', type: 'text', label: 'Product Name', required: true },
{ id: 'description', type: 'textarea', label: 'Description' },
{ id: 'price', type: 'number', label: 'Price (NOK)' },
{ id: 'category', type: 'select', label: 'Category', options: [...] },
{ id: 'inStock', type: 'checkbox', label: 'In Stock' },
];Troubleshooting
Assistant Not Responding
- Check that
OPENAI_API_KEYis set in environment variables - Verify the API route is accessible at
/api/admin-assistant - Check browser console for network errors
Navigation Not Working
- Ensure
onNavigatecallback is provided touseChatStream - Verify the route exists in
defaultAdminRoutesor custom routes - Check that the user has permission to access the target page
Form Fields Not Filling
- Verify field IDs match between form and field definitions
- Check that
onFormFieldcallback is handling updates - Ensure form context is being passed to the API
OpenAI has rate limits. For production use, implement proper error handling and consider adding retry logic for failed requests.
Next Steps
- AI Tools Reference - Detailed tool documentation
- AI Package Overview - Package introduction
- Admin App Overview - Admin application guide
