diff --git a/components/swift/chat/chat-panel.tsx b/components/swift/chat/chat-panel.tsx index 349da16..ce4fcae 100644 --- a/components/swift/chat/chat-panel.tsx +++ b/components/swift/chat/chat-panel.tsx @@ -3,20 +3,30 @@ /** * Chat Panel (v3.0) * - * Chat interface met message list en input. - * Toont chat messages met verschillende types (user, assistant, system, error). + * Chat interface met scrollable message list, auto-scroll, en scroll-lock detection. * * Epic: E2 (Chat Panel & Messages) - * Story: E2.S2 (ChatMessage component) - testing + * Story: E2.S3 (ChatPanel component - scrolling) */ +import { useRef, useEffect, useState, useCallback } from 'react'; +import { ArrowDown } from 'lucide-react'; import { ChatMessage } from './chat-message'; import { useSwiftStore } from '@/stores/swift-store'; +import { cn } from '@/lib/utils'; export function ChatPanel() { const chatMessages = useSwiftStore((s) => s.chatMessages); - // Demo messages for testing E2.S2 (will be removed in E2.S3) + // Refs for scrolling + const scrollContainerRef = useRef(null); + const messagesEndRef = useRef(null); + + // Scroll-lock state + const [isScrolledUp, setIsScrolledUp] = useState(false); + const [showScrollButton, setShowScrollButton] = useState(false); + + // Demo messages for testing scrolling behavior (will be removed when chat input is implemented) const demoMessages = chatMessages.length === 0 ? [ { id: '1', @@ -27,34 +37,120 @@ export function ChatPanel() { { id: '2', type: 'user' as const, - content: 'Notitie voor Jan: medicatie gegeven om 14:00', + content: 'Hoi, ik wil een notitie maken', timestamp: new Date(), }, { id: '3', type: 'assistant' as const, - content: 'Ik begrijp dat je een notitie wilt maken voor Jan over medicatie. Ik open een dagnotitie voor je waarin je dit kunt vastleggen.', + content: 'Natuurlijk! Voor welke patiënt wil je een notitie maken?', timestamp: new Date(), }, { id: '4', - type: 'error' as const, - content: 'Er ging iets mis met de verbinding. Probeer het opnieuw.', + type: 'user' as const, + content: 'Notitie voor Jan: medicatie gegeven om 14:00', + timestamp: new Date(), + }, + { + id: '5', + type: 'assistant' as const, + content: 'Ik begrijp dat je een notitie wilt maken voor Jan over medicatie. Ik open een dagnotitie voor je waarin je dit kunt vastleggen.', + timestamp: new Date(), + }, + { + id: '6', + type: 'user' as const, + content: 'Zoek Marie van den Berg', + timestamp: new Date(), + }, + { + id: '7', + type: 'assistant' as const, + content: 'Ik zoek Marie van den Berg voor je op in het systeem.', + timestamp: new Date(), + }, + { + id: '8', + type: 'user' as const, + content: 'Maak overdracht voor deze dienst', + timestamp: new Date(), + }, + { + id: '9', + type: 'assistant' as const, + content: 'Ik maak een overdracht voor je. Welke dienst bedoel je precies? Nacht, ochtend, middag of avond?', + timestamp: new Date(), + }, + { + id: '10', + type: 'user' as const, + content: 'Avonddienst', + timestamp: new Date(), + }, + { + id: '11', + type: 'assistant' as const, + content: 'Prima! Ik open een overdracht voor de avonddienst. Je kunt hier alle relevante informatie voor de overdracht invullen.', + timestamp: new Date(), + }, + { + id: '12', + type: 'system' as const, + content: 'Dit is een lange conversatie om scrolling te testen', timestamp: new Date(), }, ] : chatMessages; const hasMessages = demoMessages.length > 0; + // Scroll to bottom function + const scrollToBottom = useCallback((behavior: ScrollBehavior = 'smooth') => { + messagesEndRef.current?.scrollIntoView({ behavior }); + setIsScrolledUp(false); + setShowScrollButton(false); + }, []); + + // Detect scroll position (scroll-lock detection) + const handleScroll = useCallback(() => { + if (!scrollContainerRef.current) return; + + const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current; + const isNearBottom = scrollHeight - scrollTop - clientHeight < 100; // 100px threshold + + setIsScrolledUp(!isNearBottom); + + // Show scroll button only if scrolled up AND there are messages + setShowScrollButton(!isNearBottom && hasMessages); + }, [hasMessages]); + + // Auto-scroll to latest message when new message arrives (unless user scrolled up) + useEffect(() => { + if (!isScrolledUp && hasMessages) { + scrollToBottom('smooth'); + } + }, [demoMessages.length, isScrolledUp, hasMessages, scrollToBottom]); + + // Initial scroll to bottom on mount + useEffect(() => { + scrollToBottom('auto'); + }, [scrollToBottom]); + return (
- {/* Chat messages area */} -
+ {/* Chat messages area - scrollable */} +
{hasMessages ? (
{demoMessages.map((message) => ( ))} + {/* Invisible element to scroll to */} +
) : (
@@ -75,6 +171,25 @@ export function ChatPanel() {
)} + + {/* Scroll to bottom button */} + {showScrollButton && ( + + )}
{/* Chat input - placeholder */} @@ -94,7 +209,7 @@ export function ChatPanel() {

- E2.S2 compleet: ChatMessage component werkend ✓ + E2.S3 in progress: Auto-scroll en scroll-lock werkend ✓