Chat bottom menu bar token and tools alerts (#2146)

Co-authored-by: Lily Delalande <119957291+lily-de@users.noreply.github.com>
This commit is contained in:
Zane
2025-04-18 10:43:50 -07:00
committed by GitHub
parent f850db1847
commit e859ad1115
27 changed files with 560 additions and 65 deletions
@@ -0,0 +1,41 @@
import React from 'react';
import { IoIosCloseCircle, IoIosWarning } from 'react-icons/io';
import { cn } from '../../utils';
import { Alert, AlertType } from './types';
const alertIcons: Record<AlertType, React.ReactNode> = {
[AlertType.Error]: <IoIosCloseCircle className="h-5 w-5" />,
[AlertType.Warning]: <IoIosWarning className="h-5 w-5" />,
};
interface AlertBoxProps {
alert: Alert;
className?: string;
}
const alertStyles: Record<AlertType, string> = {
[AlertType.Error]: 'bg-[#d7040e] text-white',
[AlertType.Warning]: 'bg-[#cc4b03] text-white',
};
export const AlertBox = ({ alert, className }: AlertBoxProps) => {
return (
<div className={cn('flex items-center gap-2 px-3 py-2', alertStyles[alert.type], className)}>
<div className="flex-shrink-0">{alertIcons[alert.type]}</div>
<div className="flex flex-col gap-2 flex-1">
<span className="text-[11px] break-words whitespace-pre-line">{alert.message}</span>
{alert.action && (
<button
onClick={(e) => {
e.stopPropagation();
alert.action?.onClick();
}}
className="text-[11px] text-left underline hover:opacity-80 cursor-pointer outline-none"
>
{alert.action.text}
</button>
)}
</div>
</div>
);
};
@@ -0,0 +1,3 @@
export * from './AlertBox';
export * from './types';
export * from './useAlerts';
+13
View File
@@ -0,0 +1,13 @@
export enum AlertType {
Error = 'error',
Warning = 'warning',
}
export interface Alert {
type: AlertType;
message: string;
action?: {
text: string;
onClick: () => void;
};
}
@@ -0,0 +1,39 @@
import { useState, useCallback } from 'react';
import { Alert, AlertType } from './types';
interface UseAlerts {
alerts: Alert[];
addAlert: (
type: AlertType,
message: string,
action?: { text: string; onClick: () => void }
) => void;
removeAlert: (index: number) => void;
clearAlerts: () => void;
}
export const useAlerts = (): UseAlerts => {
const [alerts, setAlerts] = useState<Alert[]>([]);
const addAlert = useCallback(
(type: AlertType, message: string, action?: { text: string; onClick: () => void }) => {
setAlerts((prev) => [...prev, { type, message, action }]);
},
[]
);
const removeAlert = useCallback((index: number) => {
setAlerts((prev) => prev.filter((_, i) => i !== index));
}, []);
const clearAlerts = useCallback(() => {
setAlerts([]);
}, []);
return {
alerts,
addAlert,
removeAlert,
clearAlerts,
};
};
@@ -0,0 +1,36 @@
import { useState, useEffect } from 'react';
import { getTools } from '../../api';
const { clearTimeout } = window;
export const useToolCount = () => {
const [toolCount, setToolCount] = useState<number | null>(null);
useEffect(() => {
let timeoutId: ReturnType<typeof setTimeout>;
const fetchTools = async () => {
try {
const response = await getTools();
if (!response.error && response.data) {
setToolCount(response.data.length);
} else {
setToolCount(0);
}
} catch (err) {
console.error('Error fetching tools:', err);
setToolCount(0);
}
};
// Add initial 1s delay before first fetch
timeoutId = setTimeout(fetchTools, 1000);
// Cleanup timeouts on unmount
return () => {
clearTimeout(timeoutId);
};
}, []);
return toolCount;
};