ui: adjust styling and rm logs
This commit is contained in:
parent
dca5e54811
commit
9fec22319b
7 changed files with 129 additions and 75 deletions
|
|
@ -103,7 +103,8 @@ const HIGHLIGHTS_PATH = routes.highlights();
|
|||
const KAI_PATH = routes.kai();
|
||||
|
||||
function PrivateRoutes() {
|
||||
const { projectsStore, userStore, integrationsStore, searchStore } = useStore();
|
||||
const { projectsStore, userStore, integrationsStore, searchStore } =
|
||||
useStore();
|
||||
const onboarding = userStore.onboarding;
|
||||
const scope = userStore.scopeState;
|
||||
const { tenantId } = userStore.account;
|
||||
|
|
@ -127,8 +128,12 @@ function PrivateRoutes() {
|
|||
|
||||
React.useEffect(() => {
|
||||
if (!searchStore.urlParsed) return;
|
||||
debounceCall(() => searchStore.fetchSessions(true), 250)()
|
||||
}, [searchStore.urlParsed, searchStore.instance.filters, searchStore.instance.eventsOrder]);
|
||||
debounceCall(() => searchStore.fetchSessions(true), 250)();
|
||||
}, [
|
||||
searchStore.urlParsed,
|
||||
searchStore.instance.filters,
|
||||
searchStore.instance.eventsOrder,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Loader loading className="flex-1" />}>
|
||||
|
|
@ -166,13 +171,13 @@ function PrivateRoutes() {
|
|||
case '/integrations/slack':
|
||||
client.post('integrations/slack/add', {
|
||||
code: location.search.split('=')[1],
|
||||
state: tenantId
|
||||
state: tenantId,
|
||||
});
|
||||
break;
|
||||
case '/integrations/msteams':
|
||||
client.post('integrations/msteams/add', {
|
||||
code: location.search.split('=')[1],
|
||||
state: tenantId
|
||||
state: tenantId,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -197,7 +202,7 @@ function PrivateRoutes() {
|
|||
withSiteId(DASHBOARD_PATH, siteIdList),
|
||||
withSiteId(DASHBOARD_SELECT_PATH, siteIdList),
|
||||
withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList),
|
||||
withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList)
|
||||
withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList),
|
||||
]}
|
||||
component={enhancedComponents.Dashboard}
|
||||
/>
|
||||
|
|
@ -258,7 +263,7 @@ function PrivateRoutes() {
|
|||
withSiteId(FFLAG_READ_PATH, siteIdList),
|
||||
withSiteId(FFLAG_CREATE_PATH, siteIdList),
|
||||
withSiteId(NOTES_PATH, siteIdList),
|
||||
withSiteId(BOOKMARKS_PATH, siteIdList)
|
||||
withSiteId(BOOKMARKS_PATH, siteIdList),
|
||||
]}
|
||||
component={enhancedComponents.SessionsOverview}
|
||||
/>
|
||||
|
|
@ -274,12 +279,14 @@ function PrivateRoutes() {
|
|||
path={withSiteId(LIVE_SESSION_PATH, siteIdList)}
|
||||
component={enhancedComponents.LiveSession}
|
||||
/>
|
||||
{hasAi ? <Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(KAI_PATH, siteIdList)}
|
||||
component={enhancedComponents.Kai}
|
||||
/> : null}
|
||||
{hasAi ? (
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(KAI_PATH, siteIdList)}
|
||||
component={enhancedComponents.Kai}
|
||||
/>
|
||||
) : null}
|
||||
{Object.entries(routes.redirects).map(([fr, to]) => (
|
||||
<Redirect key={fr} exact strict from={fr} to={to} />
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@ function KaiChat() {
|
|||
const params = new URLSearchParams(location.search);
|
||||
const threadIdFromUrl = params.get('threadId');
|
||||
|
||||
React.useEffect(() => {
|
||||
// Reset chat state and clear URL params when project changes
|
||||
setSection('intro');
|
||||
setThreadId(null);
|
||||
history.replace({ search: '' });
|
||||
}, [activeSiteId]);
|
||||
|
||||
const openChats = () => {
|
||||
showModal(
|
||||
<ChatsModal
|
||||
|
|
|
|||
|
|
@ -1,53 +1,66 @@
|
|||
import AiService from "@/services/AiService";
|
||||
import AiService from '@/services/AiService';
|
||||
|
||||
export default class KaiService extends AiService {
|
||||
getKaiChats = async (projectId: string): Promise<{ title: string, threadId: string }[]> => {
|
||||
getKaiChats = async (
|
||||
projectId: string,
|
||||
): Promise<{ title: string; threadId: string }[]> => {
|
||||
const r = await this.client.get(`/kai/${projectId}/chats`);
|
||||
if (!r.ok) {
|
||||
throw new Error('Failed to fetch chats');
|
||||
}
|
||||
const data = await r.json();
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
deleteKaiChat = async (projectId: string, threadId: string): Promise<boolean> => {
|
||||
deleteKaiChat = async (
|
||||
projectId: string,
|
||||
threadId: string,
|
||||
): Promise<boolean> => {
|
||||
const r = await this.client.delete(`/kai/${projectId}/chats/${threadId}`);
|
||||
if (!r.ok) {
|
||||
throw new Error('Failed to delete chat');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
getKaiChat = async (projectId: string, threadId: string): Promise<{ role: string, content: string, message_id: any, duration?: number }[]> => {
|
||||
getKaiChat = async (
|
||||
projectId: string,
|
||||
threadId: string,
|
||||
): Promise<
|
||||
{ role: string; content: string; message_id: any; duration?: number }[]
|
||||
> => {
|
||||
const r = await this.client.get(`/kai/${projectId}/chats/${threadId}`);
|
||||
if (!r.ok) {
|
||||
throw new Error('Failed to fetch chat');
|
||||
}
|
||||
const data = await r.json();
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
createKaiChat = async (projectId: string): Promise<number> => {
|
||||
const r = await this.client.get(`/kai/${projectId}/chat/new`)
|
||||
const r = await this.client.get(`/kai/${projectId}/chat/new`);
|
||||
if (!r.ok) {
|
||||
throw new Error('Failed to create chat');
|
||||
}
|
||||
const data = await r.json();
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
feedback = async (positive: boolean | null, messageId: string, projectId: string) => {
|
||||
feedback = async (
|
||||
positive: boolean | null,
|
||||
messageId: string,
|
||||
projectId: string,
|
||||
) => {
|
||||
const r = await this.client.post(`/kai/${projectId}/messages/feedback`, {
|
||||
message_id: messageId,
|
||||
value: positive,
|
||||
user_id: userId,
|
||||
message_id: messageId,
|
||||
value: positive,
|
||||
});
|
||||
if (!r.ok) {
|
||||
throw new Error('Failed to send feedback');
|
||||
}
|
||||
|
||||
return await r.json()
|
||||
}
|
||||
return await r.json();
|
||||
};
|
||||
|
||||
cancelGeneration = async (projectId: string, threadId: string) => {
|
||||
const r = await this.client.post(`/kai/${projectId}/cancel/${threadId}`);
|
||||
|
|
@ -57,5 +70,5 @@ export default class KaiService extends AiService {
|
|||
|
||||
const data = await r.json();
|
||||
return data;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ export class ChatManager {
|
|||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
});
|
||||
socket.onAny((e) => console.log('event', e));
|
||||
|
||||
this.socket = socket;
|
||||
}
|
||||
|
|
@ -68,11 +67,9 @@ export class ChatManager {
|
|||
titleCallback: (title: string) => void;
|
||||
}) => {
|
||||
this.socket.on('chunk', (msg: BotChunk) => {
|
||||
console.log('Received message:', msg);
|
||||
msgCallback(msg);
|
||||
});
|
||||
this.socket.on('title', (msg: { content: string }) => {
|
||||
console.log('Received title:', msg);
|
||||
titleCallback(msg.content);
|
||||
});
|
||||
this.socket.on(
|
||||
|
|
|
|||
|
|
@ -2,8 +2,15 @@ import React from 'react';
|
|||
import { Icon, CopyButton } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import Markdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Loader, ThumbsUp, ThumbsDown, ListRestart, FileDown, Clock } from 'lucide-react';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import {
|
||||
Loader,
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
ListRestart,
|
||||
FileDown,
|
||||
Clock,
|
||||
} from 'lucide-react';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { kaiStore } from '../KaiStore';
|
||||
import { toast } from 'react-toastify';
|
||||
|
|
@ -27,36 +34,37 @@ export function ChatMsg({
|
|||
const [isProcessing, setIsProcessing] = React.useState(false);
|
||||
const bodyRef = React.useRef<HTMLDivElement>(null);
|
||||
const onRetry = () => {
|
||||
kaiStore.editMessage(text)
|
||||
}
|
||||
kaiStore.editMessage(text);
|
||||
};
|
||||
const onFeedback = (feedback: 'like' | 'dislike', messageId: string) => {
|
||||
kaiStore.sendMsgFeedback(feedback, messageId);
|
||||
};
|
||||
|
||||
const onExport = () => {
|
||||
setIsProcessing(true);
|
||||
import('jspdf').then(({ jsPDF }) => {
|
||||
const doc = new jsPDF();
|
||||
import('jspdf')
|
||||
.then(({ jsPDF }) => {
|
||||
const doc = new jsPDF();
|
||||
|
||||
doc.html(bodyRef.current, {
|
||||
callback: function(doc) {
|
||||
doc.save('document.pdf');
|
||||
},
|
||||
margin: [10, 10, 10, 10],
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 190, // Target width
|
||||
windowWidth: 675 // Window width for rendering
|
||||
doc.html(bodyRef.current, {
|
||||
callback: function (doc) {
|
||||
doc.save('document.pdf');
|
||||
},
|
||||
margin: [10, 10, 10, 10],
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 190, // Target width
|
||||
windowWidth: 675, // Window width for rendering
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('Error exporting message:', e);
|
||||
toast.error('Failed to export message');
|
||||
})
|
||||
.finally(() => {
|
||||
setIsProcessing(false);
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('Error exporting message:', e);
|
||||
toast.error('Failed to export message');
|
||||
})
|
||||
.finally(() => {
|
||||
setIsProcessing(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -82,7 +90,7 @@ export function ChatMsg({
|
|||
</div>
|
||||
)}
|
||||
<div className={'mt-1 flex flex-col'}>
|
||||
<div className='markdown-body' ref={bodyRef}>
|
||||
<div className="markdown-body" ref={bodyRef}>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{text}</Markdown>
|
||||
</div>
|
||||
{isUser ? (
|
||||
|
|
@ -99,18 +107,31 @@ export function ChatMsg({
|
|||
) : null
|
||||
) : (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{duration ? (
|
||||
<MsgDuration duration={duration} />
|
||||
) : null}
|
||||
<div className='ml-auto' />
|
||||
<IconButton tooltip="Like this answer" onClick={() => onFeedback('like', messageId)}>
|
||||
{duration ? <MsgDuration duration={duration} /> : null}
|
||||
<div className="ml-auto" />
|
||||
<IconButton
|
||||
tooltip="Like this answer"
|
||||
onClick={() => onFeedback('like', messageId)}
|
||||
>
|
||||
<ThumbsUp size={16} />
|
||||
</IconButton>
|
||||
<IconButton tooltip="Dislike this answer" onClick={() => onFeedback('dislike', messageId)}>
|
||||
<IconButton
|
||||
tooltip="Dislike this answer"
|
||||
onClick={() => onFeedback('dislike', messageId)}
|
||||
>
|
||||
<ThumbsDown size={16} />
|
||||
</IconButton>
|
||||
<CopyButton getHtml={() => bodyRef.current?.innerHTML} content={text} isIcon format={'text/html'} />
|
||||
<IconButton processing={isProcessing} tooltip="Export as PDF" onClick={onExport}>
|
||||
<CopyButton
|
||||
getHtml={() => bodyRef.current?.innerHTML}
|
||||
content={text}
|
||||
isIcon
|
||||
format={'text/html'}
|
||||
/>
|
||||
<IconButton
|
||||
processing={isProcessing}
|
||||
tooltip="Export as PDF"
|
||||
onClick={onExport}
|
||||
>
|
||||
<FileDown size={16} />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
|
@ -133,23 +154,35 @@ function IconButton({
|
|||
}) {
|
||||
return (
|
||||
<Tooltip title={tooltip}>
|
||||
<Button onClick={onClick} type="text" icon={children} size='small' loading={processing} />
|
||||
<Button
|
||||
onClick={onClick}
|
||||
type="text"
|
||||
icon={children}
|
||||
size="small"
|
||||
loading={processing}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatNotice({ content, duration }: { content: string, duration?: number }) {
|
||||
export function ChatNotice({
|
||||
content,
|
||||
duration,
|
||||
}: {
|
||||
content: string;
|
||||
duration?: number;
|
||||
}) {
|
||||
const startTime = React.useRef(duration ? Date.now() - duration : Date.now());
|
||||
const [activeDuration, setDuration] = React.useState(duration ?? 0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setDuration(Math.round((Date.now() - startTime.current)));
|
||||
setDuration(Math.round(Date.now() - startTime.current));
|
||||
}, 250);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
return (
|
||||
<div className='flex flex-col gap-1 items-start p-2 rounded-lg bg-gray-lightest border-gray-light w-fit '>
|
||||
<div className="flex flex-col gap-1 items-start p-2 rounded-lg bg-gray-lightest border-gray-light w-fit ">
|
||||
<div className="flex gap-2 items-start">
|
||||
<div className={'animate-spin mt-1'}>
|
||||
<Loader size={14} />
|
||||
|
|
@ -165,9 +198,7 @@ function MsgDuration({ duration }: { duration: number }) {
|
|||
return (
|
||||
<div className="text-disabled-text text-sm flex items-center gap-1">
|
||||
<Clock size={14} />
|
||||
<span className="leading-none">
|
||||
{durationFormatted(duration)}
|
||||
</span>
|
||||
<span className="leading-none">{durationFormatted(duration)}</span>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
import { MODULES } from 'Components/Client/Modules';
|
||||
import { Icon } from 'UI';
|
||||
import SVG from 'UI/SVG';
|
||||
import { hasAi } from 'App/utils/split-utils'
|
||||
import { hasAi } from 'App/utils/split-utils';
|
||||
|
||||
import { useStore } from 'App/mstore';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
|
|
|||
|
|
@ -398,7 +398,6 @@ class SearchStore {
|
|||
force: boolean = false,
|
||||
bookmarked: boolean = false,
|
||||
): Promise<void> => {
|
||||
console.log(this.searchInProgress)
|
||||
if (this.searchInProgress) return;
|
||||
const filter = this.instance.toSearch();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue