Skip to content

Commit 8da6c7d

Browse files
committed
Merge branch 'development'
2 parents 0e2fd2c + 1e27441 commit 8da6c7d

File tree

4 files changed

+92
-68
lines changed

4 files changed

+92
-68
lines changed

packages/bytebot-ui/src/app/tasks/page.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
"use client";
22

3-
import React, { useEffect, useState, useMemo } from "react";
3+
import React, { useEffect, useState } from "react";
44
import { useRouter, useSearchParams } from "next/navigation";
55
import { Header } from "@/components/layout/Header";
66
import { TaskItem } from "@/components/tasks/TaskItem";
77
import { TaskTabs, TabKey, TAB_CONFIGS } from "@/components/tasks/TaskTabs";
88
import { Pagination } from "@/components/ui/pagination";
99
import { fetchTasks, fetchTaskCounts } from "@/utils/taskUtils";
10-
import { Task, TaskStatus } from "@/types";
10+
import { Task } from "@/types";
1111
import { Button } from "@/components/ui/button";
1212
import Link from "next/link";
13+
import { Suspense } from "react";
1314

14-
export default function Tasks() {
15+
function TasksPageContent() {
1516
const router = useRouter();
1617
const searchParams = useSearchParams();
17-
18+
1819
const [tasks, setTasks] = useState<Task[]>([]);
1920
const [isLoading, setIsLoading] = useState(true);
20-
21+
2122
// Initialize activeTab from URL params
2223
const getInitialTab = (): TabKey => {
2324
const tabParam = searchParams.get("tab");
@@ -26,7 +27,7 @@ export default function Tasks() {
2627
}
2728
return "ALL";
2829
};
29-
30+
3031
const [activeTab, setActiveTab] = useState<TabKey>(getInitialTab);
3132
const [currentPage, setCurrentPage] = useState(1);
3233
const [totalPages, setTotalPages] = useState(0);
@@ -43,11 +44,12 @@ export default function Tasks() {
4344
const loadTasks = async () => {
4445
setIsLoading(true);
4546
try {
46-
const statuses = activeTab === "ALL" ? undefined : TAB_CONFIGS[activeTab].statuses;
47-
const result = await fetchTasks({
48-
page: currentPage,
47+
const statuses =
48+
activeTab === "ALL" ? undefined : TAB_CONFIGS[activeTab].statuses;
49+
const result = await fetchTasks({
50+
page: currentPage,
4951
limit: PAGE_SIZE,
50-
statuses
52+
statuses,
5153
});
5254
setTasks(result.tasks);
5355
setTotal(result.total);
@@ -78,29 +80,29 @@ export default function Tasks() {
7880
// Sync activeTab with URL params when they change
7981
useEffect(() => {
8082
const tabParam = searchParams.get("tab");
81-
const newTab: TabKey = (tabParam && Object.keys(TAB_CONFIGS).includes(tabParam))
82-
? tabParam as TabKey
83-
: "ALL";
84-
83+
const newTab: TabKey =
84+
tabParam && Object.keys(TAB_CONFIGS).includes(tabParam)
85+
? (tabParam as TabKey)
86+
: "ALL";
87+
8588
if (newTab !== activeTab) {
8689
setActiveTab(newTab);
8790
setCurrentPage(1);
8891
}
8992
}, [searchParams, activeTab]);
9093

91-
9294
const handleTabChange = (tab: TabKey) => {
9395
setActiveTab(tab);
9496
setCurrentPage(1);
95-
97+
9698
// Update URL with the new tab
9799
const newSearchParams = new URLSearchParams(searchParams);
98100
if (tab === "ALL") {
99101
newSearchParams.delete("tab");
100102
} else {
101103
newSearchParams.set("tab", tab);
102104
}
103-
105+
104106
const newUrl = `/tasks${newSearchParams.toString() ? `?${newSearchParams.toString()}` : ""}`;
105107
router.push(newUrl, { scroll: false });
106108
};
@@ -116,7 +118,7 @@ export default function Tasks() {
116118
<main className="flex-1 overflow-scroll px-6 pt-6 pb-10">
117119
<div className="mx-auto max-w-3xl">
118120
<h1 className="mb-6 text-xl font-medium">Tasks</h1>
119-
121+
120122
{!isLoading && (
121123
<TaskTabs
122124
activeTab={activeTab}
@@ -153,7 +155,7 @@ export default function Tasks() {
153155
<TaskItem key={task.id} task={task} />
154156
))}
155157
</div>
156-
158+
157159
{totalPages > 1 && (
158160
<Pagination
159161
currentPage={currentPage}
@@ -170,3 +172,20 @@ export default function Tasks() {
170172
</div>
171173
);
172174
}
175+
176+
function TasksPageFallback() {
177+
return (
178+
<div className="p-8 text-center">
179+
<div className="border-bytebot-bronze-light-5 border-t-bytebot-bronze mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-4"></div>
180+
<p className="text-gray-500">Loading tasks...</p>
181+
</div>
182+
);
183+
}
184+
185+
export default function TasksPage() {
186+
return (
187+
<Suspense fallback={<TasksPageFallback />}>
188+
<TasksPageContent />
189+
</Suspense>
190+
);
191+
}

packages/bytebot-ui/src/components/messages/ChatContainer.tsx

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useRef, useEffect, useCallback, Fragment } from "react";
2-
import { Role, TaskStatus, GroupedMessages } from "@/types";
2+
import { Role, TaskStatus } from "@/types";
33
import { MessageGroup } from "./MessageGroup";
44
import { TextShimmer } from "../ui/text-shimmer";
55
import { MessageAvatar } from "./MessageAvatar";
@@ -13,7 +13,6 @@ interface ChatContainerProps {
1313
taskId: string;
1414
}
1515

16-
1716
export function ChatContainer({
1817
scrollRef,
1918
taskId,
@@ -76,9 +75,9 @@ export function ChatContainer({
7675
};
7776

7877
return (
79-
<div className="h-full flex flex-col bg-bytebot-bronze-light-3">
78+
<div className="bg-bytebot-bronze-light-3 flex h-full flex-col">
8079
{isLoadingSession ? (
81-
<div className="flex h-full items-center justify-center min-h-80 bg-bytebot-bronze-light-3 border border-bytebot-bronze-light-7 rounded-lg overflow-hidden">
80+
<div className="bg-bytebot-bronze-light-3 border-bytebot-bronze-light-7 flex h-full min-h-80 items-center justify-center overflow-hidden rounded-lg border">
8281
<Loader size={32} />
8382
</div>
8483
) : groupedMessages.length > 0 ? (
@@ -87,23 +86,28 @@ export function ChatContainer({
8786
<div className="flex-1 overflow-y-auto">
8887
{groupedMessages.map((group, groupIndex) => (
8988
<Fragment key={groupIndex}>
90-
<MessageGroup group={group} messageIdToIndex={messageIdToIndex} taskStatus={taskStatus} />
89+
<MessageGroup
90+
group={group}
91+
messageIdToIndex={messageIdToIndex}
92+
taskStatus={taskStatus}
93+
/>
9194
</Fragment>
9295
))}
9396

94-
{taskStatus === TaskStatus.RUNNING && control === Role.ASSISTANT && (
95-
<div className="flex items-center justify-start gap-4 px-4 py-3 bg-bytebot-bronze-light-3 border-x border-bytebot-bronze-light-7">
96-
<MessageAvatar role={Role.ASSISTANT} />
97-
<div className="flex items-center justify-start gap-2">
98-
<div className="flex h-full items-center justify-center py-2">
99-
<Loader size={20} />
97+
{taskStatus === TaskStatus.RUNNING &&
98+
control === Role.ASSISTANT && (
99+
<div className="bg-bytebot-bronze-light-3 border-bytebot-bronze-light-7 flex items-center justify-start gap-4 border-x px-4 py-3">
100+
<MessageAvatar role={Role.ASSISTANT} />
101+
<div className="flex items-center justify-start gap-2">
102+
<div className="flex h-full items-center justify-center py-2">
103+
<Loader size={20} />
104+
</div>
105+
<TextShimmer className="text-sm" duration={2}>
106+
Bytebot is working...
107+
</TextShimmer>
100108
</div>
101-
<TextShimmer className="text-sm" duration={2}>
102-
Bytebot is working...
103-
</TextShimmer>
104109
</div>
105-
</div>
106-
)}
110+
)}
107111

108112
{/* Loading indicator for infinite scroll at bottom */}
109113
{isLoadingMoreMessages && (
@@ -118,17 +122,17 @@ export function ChatContainer({
118122

119123
{/* Fixed chat input at bottom */}
120124
{[TaskStatus.RUNNING, TaskStatus.NEEDS_HELP].includes(taskStatus) && (
121-
<div className="flex-shrink-0 z-10 bg-bytebot-bronze-light-3">
122-
<div className="p-2 border-x border-b border-bytebot-bronze-light-7 rounded-b-lg">
123-
<div className="bg-bytebot-bronze-light-2 border border-bytebot-bronze-light-7 rounded-lg p-2">
124-
<ChatInput
125-
input={input}
126-
isLoading={isLoading}
127-
onInputChange={setInput}
128-
onSend={handleAddMessage}
129-
minLines={1}
130-
placeholder="Add more details to your task..."
131-
/>
125+
<div className="bg-bytebot-bronze-light-3 z-10 flex-shrink-0">
126+
<div className="border-bytebot-bronze-light-7 rounded-b-lg border-x border-b p-2">
127+
<div className="bg-bytebot-bronze-light-2 border-bytebot-bronze-light-7 rounded-lg border p-2">
128+
<ChatInput
129+
input={input}
130+
isLoading={isLoading}
131+
onInputChange={setInput}
132+
onSend={handleAddMessage}
133+
minLines={1}
134+
placeholder="Add more details to your task..."
135+
/>
132136
</div>
133137
</div>
134138
</div>

packages/bytebot-ui/src/components/tasks/TaskItem.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
Tick02Icon,
88
CancelCircleIcon,
99
AlertCircleIcon,
10-
TimeScheduleIcon,
1110
} from "@hugeicons/core-free-icons";
1211
import { Loader } from "@/components/ui/loader";
1312
import Link from "next/link";
@@ -58,14 +57,12 @@ export const TaskItem: React.FC<TaskItemProps> = ({ task }) => {
5857
const date = new Date(dateString);
5958
const today = new Date();
6059

61-
const isToday =
60+
const isToday =
6261
date.getDate() === today.getDate() &&
6362
date.getMonth() === today.getMonth() &&
6463
date.getFullYear() === today.getFullYear();
6564

66-
const formatString = isToday
67-
? `'Today' h:mma`
68-
: "MMMM d, yyyy h:mma";
65+
const formatString = isToday ? `'Today' h:mma` : "MMMM d, yyyy h:mma";
6966

7067
const formatted = format(date, formatString).toLowerCase();
7168
return capitalizeFirstChar(formatted);
@@ -87,33 +84,28 @@ export const TaskItem: React.FC<TaskItemProps> = ({ task }) => {
8784

8885
return (
8986
<div className="flex items-center justify-center">
90-
<HugeiconsIcon
91-
icon={icon}
92-
className={`h-5 w-5 ${color}`}
93-
/>
87+
<HugeiconsIcon icon={icon} className={`h-5 w-5 ${color}`} />
9488
</div>
9589
);
9690
};
9791

9892
return (
9993
<Link href={`/tasks/${task.id}`} className="block">
10094
<div className="bg-bytebot-bronze-light-2 border-bytebot-bronze-light-7 hover:bg-bytebot-bronze-light-3 flex min-h-24 items-start rounded-lg border p-5 transition-colors">
101-
<div className="flex-1 space-y-2 mb-0.5">
95+
<div className="mb-0.5 flex-1 space-y-2">
10296
<div className="flex items-center justify-start space-x-2">
10397
<StatusIcon status={task.status} />
10498
<div className="text-byhtebot-bronze-dark-7 text-sm font-medium">
10599
{capitalizeFirstChar(task.description)}
106100
</div>
107101
</div>
108-
<div className="text-xs flex items-center justify-start space-x-1.5 ml-7">
102+
<div className="ml-7 flex items-center justify-start space-x-1.5 text-xs">
109103
<span className="text-bytebot-bronze-light-10">
110104
{formatDate(task.createdAt)}
111105
</span>
112106
{task.user && (
113107
<>
114-
<span className="text-bytebot-bronze-light-10">
115-
116-
</span>
108+
<span className="text-bytebot-bronze-light-10"></span>
117109
<span className="text-bytebot-bronze-light-10">
118110
{task.user.name || task.user.email}
119111
</span>

packages/bytebot-ui/src/components/tasks/TaskTabs.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ interface TaskTabsProps {
1818

1919
interface TabConfig {
2020
label: string;
21-
icon: any;
21+
icon:
22+
| typeof Tick02Icon
23+
| typeof CursorProgress04Icon
24+
| typeof MultiplicationSignIcon
25+
| typeof ListViewIcon;
2226
color: string;
2327
statuses: TaskStatus[];
2428
}
@@ -34,7 +38,12 @@ const TAB_CONFIGS: Record<TabKey, TabConfig> = {
3438
label: "Active",
3539
icon: CursorProgress04Icon,
3640
color: "text-bytebot-bronze-light-10",
37-
statuses: [TaskStatus.PENDING, TaskStatus.RUNNING, TaskStatus.NEEDS_HELP, TaskStatus.NEEDS_REVIEW],
41+
statuses: [
42+
TaskStatus.PENDING,
43+
TaskStatus.RUNNING,
44+
TaskStatus.NEEDS_HELP,
45+
TaskStatus.NEEDS_REVIEW,
46+
],
3847
},
3948
COMPLETED: {
4049
label: "Completed",
@@ -58,20 +67,20 @@ export const TaskTabs: React.FC<TaskTabsProps> = ({
5867
const tabs = Object.entries(TAB_CONFIGS) as [TabKey, TabConfig][];
5968

6069
return (
61-
<div className="border-b border-bytebot-bronze-light-7 mb-6">
70+
<div className="border-bytebot-bronze-light-7 mb-6 border-b">
6271
<div className="flex overflow-x-auto">
6372
{tabs.map(([tabKey, config]) => {
6473
const isActive = activeTab === tabKey;
6574
const count = taskCounts[tabKey] || 0;
66-
75+
6776
return (
6877
<button
6978
key={tabKey}
7079
onClick={() => onTabChange(tabKey)}
71-
className={`flex items-center space-x-2 px-4 py-3 border-b-2 transition-colors whitespace-nowrap cursor-pointer ${
80+
className={`flex cursor-pointer items-center space-x-2 border-b-2 px-4 py-3 whitespace-nowrap transition-colors ${
7281
isActive
7382
? "border-bytebot-bronze-dark-7 text-bytebot-bronze-dark-7"
74-
: "border-transparent text-bytebot-bronze-light-10 hover:text-bytebot-bronze-dark-7"
83+
: "text-bytebot-bronze-light-10 hover:text-bytebot-bronze-dark-7 border-transparent"
7584
}`}
7685
>
7786
<HugeiconsIcon
@@ -81,7 +90,7 @@ export const TaskTabs: React.FC<TaskTabsProps> = ({
8190
<span className="text-sm font-medium">{config.label}</span>
8291
{count > 0 && (
8392
<span
84-
className={`text-xs px-2 py-0.5 rounded-full ${
93+
className={`rounded-full px-2 py-0.5 text-xs ${
8594
isActive
8695
? "bg-bytebot-bronze-dark-7 text-white"
8796
: "bg-bytebot-bronze-light-7 text-bytebot-bronze-light-11"
@@ -100,4 +109,4 @@ export const TaskTabs: React.FC<TaskTabsProps> = ({
100109

101110
// Export the TabKey type and TAB_CONFIGS for use in other components
102111
export type { TabKey };
103-
export { TAB_CONFIGS };
112+
export { TAB_CONFIGS };

0 commit comments

Comments
 (0)