Files
brachnha-insight/src/app/(session)/chat/page.tsx
Max 3fbbb1a93b Initial commit: Brachnha Insight project setup
- Next.js 14+ with App Router and TypeScript
- Tailwind CSS and ShadCN UI styling
- Zustand state management
- Dexie.js for IndexedDB (local-first data)
- Auth.js v5 for authentication
- BMAD framework integration

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-26 12:28:43 +07:00

119 lines
4.5 KiB
TypeScript

"use client";
import { useEffect } from 'react';
import { ChatWindow } from '@/components/features/chat/chat-window';
import { ChatInput } from '@/components/features/chat/chat-input';
import { useSessionStore, useActiveSessionId, useTeacherStatus } from '@/store/use-session';
import { ChatService } from '@/services/chat-service';
import { toast } from 'sonner';
import { Button } from "@/components/ui/button";
import { DraftViewSheet } from "@/components/features/draft/DraftViewSheet";
import { useChatStore } from "@/lib/store/chat-store";
import { CheckCircle, Loader2, ArrowLeft, Sparkles } from "lucide-react";
import Link from "next/link";
export default function ChatPage() {
const activeSessionId = useActiveSessionId();
const teacherStatus = useTeacherStatus();
const { setActiveSession } = useSessionStore((s) => s.actions);
const isDrafting = useChatStore((s) => s.isDrafting);
// Initialize Session on Mount
useEffect(() => {
const initSession = async () => {
if (!activeSessionId) {
try {
const newSessionId = await ChatService.createSession();
setActiveSession(newSessionId);
} catch (error) {
console.error("Failed to create session:", error);
toast.error("Failed to start session. Check your database.");
}
}
};
initSession();
}, [activeSessionId, setActiveSession]);
const handleSend = async (message: string) => {
if (!activeSessionId) return;
try {
await ChatService.sendMessage(message, activeSessionId);
} catch (error: any) {
console.error(error);
if (error.message === 'AI Provider not configured') {
toast.error("Please configure your AI Provider in Settings", {
action: {
label: "Go to Settings",
onClick: () => window.location.href = '/settings'
}
});
} else {
toast.error("Failed to send message. Please check connection.");
}
}
};
const handleFinishSession = async () => {
if (!activeSessionId) return;
try {
toast.info("Generating your learning summary...");
// Ensure store has latest messages for this session
await useChatStore.getState().hydrate(activeSessionId);
// Trigger Ghostwriter
await useChatStore.getState().generateDraft(activeSessionId);
} catch (error) {
console.error("Failed to generate draft:", error);
toast.error("Failed to generate summary. Please try again.");
}
};
return (
<div className="flex flex-col h-screen bg-background">
{/* Session Header */}
<div className="flex items-center justify-between px-4 py-3 bg-white border-b border-slate-200 shrink-0">
<div className="flex items-center gap-3">
<Link href="/" className="text-slate-500 hover:text-slate-700 transition-colors">
<ArrowLeft className="w-5 h-5" />
</Link>
<div className="font-medium text-slate-700">
Current Session
</div>
</div>
<Button
onClick={handleFinishSession}
disabled={isDrafting}
variant="default"
size="sm"
className="bg-indigo-600 hover:bg-indigo-700"
>
{isDrafting ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Drafting...
</>
) : (
<>
<Sparkles className="w-4 h-4 mr-2" />
Draft Post
</>
)}
</Button>
</div>
{/* Chat Messages - Scrollable Area */}
<div className="flex-1 overflow-hidden">
<ChatWindow sessionId={activeSessionId} />
</div>
<DraftViewSheet />
{/* Chat Input - Fixed at Bottom */}
<div className="shrink-0 bg-white border-t border-slate-200">
<ChatInput onSend={handleSend} isLoading={teacherStatus !== 'idle'} />
</div>
</div>
);
}