Skip to content

Commit a64410f

Browse files
authored
Feature/frontend fix (#211)
* fix: PAGI-61 * fix: PAGI-64
1 parent 0798800 commit a64410f

File tree

13 files changed

+666
-686
lines changed

13 files changed

+666
-686
lines changed

frontend/package-lock.json

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/features/flows/agents/flow-agents.tsx

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { zodResolver } from '@hookform/resolvers/zod';
22
import debounce from 'lodash/debounce';
3-
import { Bot, ListFilter, Search, X } from 'lucide-react';
4-
import { useEffect, useMemo, useRef, useState } from 'react';
3+
import { Bot, ChevronDown, ListFilter, Search, X } from 'lucide-react';
4+
import { useEffect, useMemo, useState } from 'react';
55
import { useForm } from 'react-hook-form';
66
import { z } from 'zod';
77

88
import { Button } from '@/components/ui/button';
99
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
1010
import { Form, FormControl, FormField } from '@/components/ui/form';
1111
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
12+
import { useAutoScroll } from '@/hooks/use-auto-scroll';
1213
import { useFlow } from '@/providers/flow-provider';
1314

1415
import FlowTasksDropdown from '../flow-tasks-dropdown';
@@ -28,14 +29,9 @@ const FlowAgents = () => {
2829
const { flowData, flowId } = useFlow();
2930

3031
const logs = useMemo(() => flowData?.agentLogs ?? [], [flowData?.agentLogs]);
31-
const agentsEndRef = useRef<HTMLDivElement>(null);
32-
33-
// Separate state for immediate input value and debounced search value
3432
const [debouncedSearchValue, setDebouncedSearchValue] = useState('');
3533

36-
const scrollAgents = () => {
37-
agentsEndRef.current?.scrollIntoView();
38-
};
34+
const { containerRef, endRef, hasNewMessages, isScrolledToBottom, scrollToEnd } = useAutoScroll(logs, flowId);
3935

4036
const form = useForm<z.infer<typeof searchFormSchema>>({
4137
defaultValues: {
@@ -139,12 +135,6 @@ const FlowAgents = () => {
139135

140136
const hasLogs = filteredLogs && filteredLogs.length > 0;
141137

142-
useEffect(() => {
143-
if (hasLogs) {
144-
scrollAgents();
145-
}
146-
}, [logs, hasLogs]);
147-
148138
// Reset filters handler
149139
const handleResetFilters = () => {
150140
form.reset({
@@ -213,15 +203,35 @@ const FlowAgents = () => {
213203
</div>
214204

215205
{hasLogs ? (
216-
<div className="flex flex-1 flex-col gap-4 overflow-auto">
217-
{filteredLogs.map((log) => (
218-
<FlowAgent
219-
key={log.id}
220-
log={log}
221-
searchValue={debouncedSearchValue}
222-
/>
223-
))}
224-
<div ref={agentsEndRef} />
206+
<div className="relative flex-1 overflow-y-hidden">
207+
<div
208+
className="flex h-full flex-col gap-4 overflow-y-auto"
209+
ref={containerRef}
210+
>
211+
{filteredLogs.map((log) => (
212+
<FlowAgent
213+
key={log.id}
214+
log={log}
215+
searchValue={debouncedSearchValue}
216+
/>
217+
))}
218+
<div ref={endRef} />
219+
</div>
220+
221+
{!isScrolledToBottom && (
222+
<Button
223+
className="absolute right-4 bottom-4 z-10 shadow-md hover:shadow-lg"
224+
onClick={() => scrollToEnd()}
225+
size="icon-sm"
226+
type="button"
227+
variant="outline"
228+
>
229+
<ChevronDown />
230+
{hasNewMessages && (
231+
<span className="bg-primary absolute -top-1.5 -right-1.5 size-3 rounded-full" />
232+
)}
233+
</Button>
234+
)}
225235
</div>
226236
) : hasActiveFilters ? (
227237
<Empty>

frontend/src/features/flows/messages/flow-assistant-messages.tsx

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Form, FormControl, FormField } from '@/components/ui/form';
1717
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
1818
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
1919
import { StatusType } from '@/graphql/types';
20-
import { useChatScroll } from '@/hooks/use-chat-scroll';
20+
import { useAutoScroll } from '@/hooks/use-auto-scroll';
2121
import { Log } from '@/lib/log';
2222
import { cn } from '@/lib/utils';
2323
import { formatName } from '@/lib/utils/format';
@@ -306,8 +306,16 @@ const FlowAssistantMessages = ({ className }: FlowAssistantMessagesProps) => {
306306
const [isSubmitting, setIsSubmitting] = useState(false);
307307
const [isCanceling, setIsCanceling] = useState(false);
308308

309-
const { containerRef, endRef, hasNewMessages, isScrolledToBottom, scrollToEnd } = useChatScroll(
310-
logs ?? [],
309+
const selectedAssistantLogs = useMemo(() => {
310+
if (!logs?.length || !selectedAssistantId) {
311+
return [];
312+
}
313+
314+
return logs.filter((log) => log.assistantId === selectedAssistantId);
315+
}, [logs, selectedAssistantId]);
316+
317+
const { containerRef, endRef, hasNewMessages, isScrolledToBottom, scrollToEnd } = useAutoScroll(
318+
selectedAssistantLogs,
311319
selectedAssistantId ?? null,
312320
);
313321

@@ -380,37 +388,20 @@ const FlowAssistantMessages = ({ className }: FlowAssistantMessagesProps) => {
380388
return !!searchValue.trim();
381389
}, [searchValue]);
382390

383-
// Memoize filtered logs to avoid recomputing on every render
384-
// Use debouncedSearchValue for filtering to improve performance
385391
const filteredLogs = useMemo(() => {
386-
if (!logs) {
387-
return [];
388-
}
389-
390-
// First filter by selected assistant
391-
let assistantFilteredLogs = logs;
392-
393-
if (selectedAssistantId) {
394-
assistantFilteredLogs = logs.filter((log) => log.assistantId === selectedAssistantId);
395-
} else {
396-
// If no assistant is selected, show no logs
397-
assistantFilteredLogs = [];
398-
}
399-
400-
// Then filter by search query if present
401392
const search = debouncedSearchValue.toLowerCase().trim();
402393

403394
if (!search) {
404-
return assistantFilteredLogs;
395+
return selectedAssistantLogs;
405396
}
406397

407-
return assistantFilteredLogs.filter(
398+
return selectedAssistantLogs.filter(
408399
(log) =>
409400
log.message.toLowerCase().includes(search) ||
410401
(log.result && log.result.toLowerCase().includes(search)) ||
411402
(log.thinking && log.thinking.toLowerCase().includes(search)),
412403
);
413-
}, [logs, debouncedSearchValue, selectedAssistantId]);
404+
}, [selectedAssistantLogs, debouncedSearchValue]);
414405

415406
// Handlers for interacting with assistant
416407
const handleAssistantDelete = (assistantId: string) => {
@@ -617,14 +608,18 @@ const FlowAssistantMessages = ({ className }: FlowAssistantMessagesProps) => {
617608
<div ref={endRef} />
618609
</div>
619610

620-
{hasNewMessages && !isScrolledToBottom && (
611+
{!isScrolledToBottom && (
621612
<Button
622-
className="absolute right-4 bottom-4 z-10 size-9 rounded-full shadow-md hover:shadow-lg"
613+
className="absolute right-4 bottom-4 z-10 shadow-md hover:shadow-lg"
623614
onClick={() => scrollToEnd()}
624-
size="icon"
615+
size="icon-sm"
625616
type="button"
617+
variant="outline"
626618
>
627619
<ChevronDown />
620+
{hasNewMessages && (
621+
<span className="bg-primary absolute -top-1.5 -right-1.5 size-3 rounded-full" />
622+
)}
628623
</Button>
629624
)}
630625
</div>

0 commit comments

Comments
 (0)