174 lines
5.2 KiB
TypeScript
174 lines
5.2 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useRef } from 'react'
|
|
import { ChatMessage } from './ChatMessage'
|
|
import { ChatInput } from './ChatInput'
|
|
import { useChatStream } from './useChatStream'
|
|
import { useToast } from '@/components/ui/use-toast'
|
|
import { Bot, Trash2, RotateCcw } from 'lucide-react'
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
interface ChatPanelProps {
|
|
className?: string
|
|
}
|
|
|
|
export function ChatPanel({ className }: ChatPanelProps) {
|
|
const { toast } = useToast()
|
|
const messagesEndRef = useRef<HTMLDivElement>(null)
|
|
|
|
const {
|
|
messages,
|
|
isLoading,
|
|
isStreaming,
|
|
error,
|
|
toolsExecuting,
|
|
sendMessage,
|
|
clearChat,
|
|
} = useChatStream({
|
|
onError: (error) => {
|
|
toast({
|
|
title: 'Fel',
|
|
description: error,
|
|
variant: 'destructive',
|
|
})
|
|
},
|
|
})
|
|
|
|
// Auto-scroll to bottom when new messages arrive
|
|
useEffect(() => {
|
|
if (messagesEndRef.current) {
|
|
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
|
|
}
|
|
}, [messages])
|
|
|
|
const handleClearChat = () => {
|
|
clearChat()
|
|
toast({
|
|
title: 'Chatten rensad',
|
|
description: 'En ny konversation har startats.',
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className={`flex flex-col h-full ${className || ''}`}>
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between px-4 py-3 border-b bg-muted/30">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center">
|
|
<Bot className="h-4 w-4 text-primary" />
|
|
</div>
|
|
<div>
|
|
<h3 className="font-medium text-sm">AI-assistent</h3>
|
|
<p className="text-xs text-muted-foreground">
|
|
Skatt, moms och bokföring
|
|
</p>
|
|
</div>
|
|
</div>
|
|
{messages.length > 0 && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleClearChat}
|
|
className="text-muted-foreground hover:text-foreground"
|
|
>
|
|
<RotateCcw className="h-4 w-4 mr-1" />
|
|
Ny chatt
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Messages */}
|
|
<div className="flex-1 overflow-y-auto">
|
|
{messages.length === 0 ? (
|
|
<div className="flex flex-col items-center justify-center h-full text-center px-6 py-8">
|
|
<div className="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center mb-4">
|
|
<Bot className="h-6 w-6 text-primary" />
|
|
</div>
|
|
<h4 className="font-medium mb-2">Hur kan jag hjälpa dig?</h4>
|
|
<p className="text-sm text-muted-foreground max-w-xs">
|
|
Jag kan svara på frågor om skatt, moms, bokföring och andra
|
|
ekonomiska frågor för företagare.
|
|
</p>
|
|
<div className="mt-6 space-y-2 w-full max-w-xs">
|
|
<SuggestionButton
|
|
onClick={() => sendMessage('Hur går det för mitt företag?')}
|
|
disabled={isLoading}
|
|
>
|
|
Hur går det för mitt företag?
|
|
</SuggestionButton>
|
|
<SuggestionButton
|
|
onClick={() => sendMessage('Visa mina senaste fakturor')}
|
|
disabled={isLoading}
|
|
>
|
|
Visa mina senaste fakturor
|
|
</SuggestionButton>
|
|
<SuggestionButton
|
|
onClick={() => sendMessage('Hur ser min resultaträkning ut?')}
|
|
disabled={isLoading}
|
|
>
|
|
Hur ser min resultaträkning ut?
|
|
</SuggestionButton>
|
|
<SuggestionButton
|
|
onClick={() => sendMessage('Vad kan jag dra av som företagare?')}
|
|
disabled={isLoading}
|
|
>
|
|
Vad kan jag dra av som företagare?
|
|
</SuggestionButton>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="py-4">
|
|
{messages.map((message, index) => (
|
|
<ChatMessage
|
|
key={message.id}
|
|
message={message}
|
|
isStreaming={
|
|
isStreaming &&
|
|
index === messages.length - 1 &&
|
|
message.role === 'assistant'
|
|
}
|
|
toolsExecuting={
|
|
isStreaming &&
|
|
index === messages.length - 1 &&
|
|
message.role === 'assistant'
|
|
? toolsExecuting
|
|
: undefined
|
|
}
|
|
/>
|
|
))}
|
|
<div ref={messagesEndRef} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Input */}
|
|
<ChatInput
|
|
onSend={sendMessage}
|
|
disabled={isStreaming}
|
|
isLoading={isLoading}
|
|
placeholder="Ställ en fråga om skatt, moms eller bokföring..."
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function SuggestionButton({
|
|
children,
|
|
onClick,
|
|
disabled,
|
|
}: {
|
|
children: React.ReactNode
|
|
onClick: () => void
|
|
disabled?: boolean
|
|
}) {
|
|
return (
|
|
<button
|
|
onClick={onClick}
|
|
disabled={disabled}
|
|
className="w-full text-left text-sm px-3 py-2 rounded-lg border border-border/60 hover:bg-muted/50 hover:border-border transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{children}
|
|
</button>
|
|
)
|
|
}
|