Files
blackroad-os-web/app/(app)/conversations/[id]/page.tsx
Alexa Louise 2ab0d60400 Initial Phase 1 Alpha build for app.blackroad.io
This commit establishes the foundation for the BlackRoad OS web application,
the primary SaaS product for human-AI collaboration.

1. Authentication System
   - Login and signup pages with email/password
   - Zustand state management for auth
   - Workspace-based user model
   - Protected route handling

2. App Shell
   - Sidebar navigation (Conversations, Agents, Governance, Account)
   - App header with workspace and user info
   - Layout with authentication guards
   - Responsive design with Tailwind CSS

3. Conversation Interface
   - Chat UI with message bubbles
   - User and assistant message rendering
   - Real-time timestamp display
   - Loading states and animations
   - Placeholder for WebSocket integration

4. Core Dependencies
   - Next.js 16 with App Router
   - Zustand for state management
   - TanStack Query (ready for API calls)
   - Lucide React icons
   - Tailwind CSS 4 with custom utilities

5. Workspace Management
   - Multi-tenant workspace structure
   - Workspace store with Zustand
   - Plan-based workspace model (Free, Pro, Enterprise)

Structure:
app/
  ├── (auth)/          # Authentication routes
  │   ├── login/
  │   └── signup/
  ├── (app)/           # Protected app routes
  │   ├── conversations/[id]/
  │   ├── workspace/
  │   └── layout.tsx
  └── page.tsx         # Redirects to login

components/
  ├── Sidebar.tsx      # Main navigation
  └── AppHeader.tsx    # Workspace header

stores/
  ├── auth-store.ts    # Authentication state
  └── workspace-store.ts

lib/
  └── cn.ts            # Tailwind merge utility

Next Steps:
- Integrate WebSocket streaming for real-time AI responses
- Connect to BlackRoad OS backend API
- Add agent management interface
- Build governance center UI
- Deploy to app.blackroad.io

Phase 1 Alpha Target: Jan 25, 2026

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 18:49:56 -06:00

152 lines
5.3 KiB
TypeScript

'use client';
import { useState, useRef, useEffect } from 'react';
import { Send, Bot, User } from 'lucide-react';
import { useParams } from 'next/navigation';
interface Message {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: Date;
}
export default function ConversationPage() {
const params = useParams();
const [messages, setMessages] = useState<Message[]>([
{
id: '1',
role: 'assistant',
content: 'Hello! I\'m Lucidia, your AI companion built on BlackRoad OS. How can I help you today?',
timestamp: new Date(),
},
]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim() || isLoading) return;
const userMessage: Message = {
id: Date.now().toString(),
role: 'user',
content: input,
timestamp: new Date(),
};
setMessages((prev) => [...prev, userMessage]);
setInput('');
setIsLoading(true);
// TODO: Replace with actual WebSocket/API call
setTimeout(() => {
const assistantMessage: Message = {
id: (Date.now() + 1).toString(),
role: 'assistant',
content: 'I received your message: "' + userMessage.content + '". This is a placeholder response. WebSocket streaming will be integrated next.',
timestamp: new Date(),
};
setMessages((prev) => [...prev, assistantMessage]);
setIsLoading(false);
}, 1000);
};
return (
<div className="flex flex-col h-full">
{/* Messages area */}
<div className="flex-1 overflow-y-auto px-4 py-6">
<div className="max-w-3xl mx-auto space-y-6">
{messages.map((message) => (
<div
key={message.id}
className={`flex gap-4 ${
message.role === 'user' ? 'justify-end' : 'justify-start'
}`}
>
{message.role === 'assistant' && (
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-gradient-to-br from-blue-800 to-blue-600 flex items-center justify-center text-white">
<Bot className="h-5 w-5" />
</div>
)}
<div
className={`max-w-[70%] rounded-lg px-4 py-3 ${
message.role === 'user'
? 'bg-blue-800 text-white'
: 'bg-white border border-gray-200 text-gray-900'
}`}
>
<p className="text-sm whitespace-pre-wrap">{message.content}</p>
<p
className={`text-xs mt-2 ${
message.role === 'user' ? 'text-blue-100' : 'text-gray-500'
}`}
>
{message.timestamp.toLocaleTimeString()}
</p>
</div>
{message.role === 'user' && (
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-gradient-to-br from-gray-700 to-gray-500 flex items-center justify-center text-white">
<User className="h-5 w-5" />
</div>
)}
</div>
))}
{isLoading && (
<div className="flex gap-4">
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-gradient-to-br from-blue-800 to-blue-600 flex items-center justify-center text-white">
<Bot className="h-5 w-5" />
</div>
<div className="bg-white border border-gray-200 rounded-lg px-4 py-3">
<div className="flex gap-1">
<div className="h-2 w-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
<div className="h-2 w-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
<div className="h-2 w-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
</div>
{/* Input area */}
<div className="border-t border-gray-200 bg-white px-4 py-4">
<form onSubmit={handleSubmit} className="max-w-3xl mx-auto">
<div className="flex gap-4">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
disabled={isLoading}
className="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-800 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="px-6 py-3 bg-blue-800 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
<Send className="h-5 w-5" />
Send
</button>
</div>
</form>
</div>
</div>
);
}