fix:readded deleted code, fixed build
This commit is contained in:
parent
5808bd4bee
commit
239fd328bc
11 changed files with 243 additions and 102 deletions
|
|
@ -43,10 +43,10 @@ export async function fetchAttributes(
|
|||
request: ApiRequestFunction,
|
||||
entityType: string
|
||||
): Promise<AttributeDefinition[]> {
|
||||
const data = await request<any>({
|
||||
const data = await request({
|
||||
url: `/api/attributes/${entityType}`,
|
||||
method: 'get'
|
||||
});
|
||||
}) as any;
|
||||
|
||||
// Extract attributes from response - check if response.data.attributes exists, otherwise check if response.data is an array
|
||||
let attrs: AttributeDefinition[] = [];
|
||||
|
|
@ -81,10 +81,10 @@ export async function fetchConnectionAttributes(request: ApiRequestFunction): Pr
|
|||
* Endpoint: GET /api/attributes/FileItem
|
||||
*/
|
||||
export async function fetchFileAttributes(request: ApiRequestFunction): Promise<AttributeDefinition[]> {
|
||||
const data = await request<AttributeDefinition[] | { attributes: AttributeDefinition[] }>({
|
||||
const data = await request({
|
||||
url: '/api/attributes/FileItem',
|
||||
method: 'get'
|
||||
});
|
||||
}) as AttributeDefinition[] | { attributes: AttributeDefinition[] };
|
||||
|
||||
// Handle different response formats
|
||||
if (Array.isArray(data)) {
|
||||
|
|
|
|||
|
|
@ -206,12 +206,12 @@ export async function stopChatbotApi(
|
|||
request: ApiRequestFunction,
|
||||
workflowId: string
|
||||
): Promise<ChatbotWorkflow> {
|
||||
const data = await request<ChatbotWorkflow>({
|
||||
const data = await request({
|
||||
url: `/api/chatbot/${workflowId}/stop`,
|
||||
method: 'post'
|
||||
});
|
||||
|
||||
return data;
|
||||
return data as ChatbotWorkflow;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -229,11 +229,11 @@ export async function getChatbotThreadsApi(
|
|||
|
||||
console.log(`[getChatbotThreadsApi] Fetching threads with params:`, requestParams);
|
||||
|
||||
const data = await request<any>({
|
||||
const data = await request({
|
||||
url: '/api/chatbot/threads',
|
||||
method: 'get',
|
||||
params: requestParams
|
||||
});
|
||||
}) as any;
|
||||
|
||||
console.log(`[getChatbotThreadsApi] Full response:`, JSON.stringify(data, null, 2));
|
||||
console.log(`[getChatbotThreadsApi] Response structure:`, {
|
||||
|
|
@ -265,11 +265,11 @@ export async function getChatbotThreadApi(
|
|||
): Promise<{ workflow: ChatbotWorkflow; chatData: { items: ChatDataItem[] } }> {
|
||||
console.log(`[getChatbotThreadApi] Fetching thread with workflowId: ${workflowId}`);
|
||||
|
||||
const data = await request<{ workflow: ChatbotWorkflow; chatData: { items: ChatDataItem[] } }>({
|
||||
const data = await request({
|
||||
url: '/api/chatbot/threads',
|
||||
method: 'get',
|
||||
params: { workflowId }
|
||||
});
|
||||
}) as { workflow: ChatbotWorkflow; chatData: { items: ChatDataItem[] } };
|
||||
|
||||
console.log(`[getChatbotThreadApi] Full response for workflowId ${workflowId}:`, JSON.stringify(data, null, 2));
|
||||
console.log(`[getChatbotThreadApi] Response structure:`, {
|
||||
|
|
@ -279,7 +279,7 @@ export async function getChatbotThreadApi(
|
|||
hasItems: !!data.chatData?.items,
|
||||
chatDataKeys: data.chatData ? Object.keys(data.chatData) : [],
|
||||
itemsLength: Array.isArray(data.chatData?.items) ? data.chatData.items.length : 'not an array',
|
||||
chatDataTypes: Array.isArray(data.chatData?.items) ? data.chatData.items.map(item => item?.type).filter(Boolean) : []
|
||||
chatDataTypes: Array.isArray(data.chatData?.items) ? data.chatData.items.map((item: ChatDataItem) => item?.type).filter(Boolean) : []
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -301,7 +301,7 @@ export async function deleteChatbotWorkflowApi(
|
|||
workflowId: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
await request<any>({
|
||||
await request({
|
||||
url: `/api/chatbot/${workflowId}`,
|
||||
method: 'delete'
|
||||
});
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ export function FormGeneratorControls({
|
|||
{/* Delete Controls - Show when items are selected */}
|
||||
{selectable && selectedCount > 0 && (
|
||||
<div className={styles.deleteControlsIntegrated}>
|
||||
{selectedCount === 1 && onDeleteSingle && !(selectedCount === _displayData.length && _displayData.length > 0) && (
|
||||
{selectedCount === 1 && onDeleteSingle && !(selectedCount === displayData.length && displayData.length > 0) && (
|
||||
<Button
|
||||
onClick={onDeleteSingle}
|
||||
variant="primary"
|
||||
|
|
@ -172,7 +172,7 @@ export function FormGeneratorControls({
|
|||
{t('formgen.delete.single', 'Delete')}
|
||||
</Button>
|
||||
)}
|
||||
{(selectedCount > 1 || (selectedCount === _displayData.length && _displayData.length > 0)) && onDeleteMultiple && (
|
||||
{(selectedCount > 1 || (selectedCount === displayData.length && displayData.length > 0)) && onDeleteMultiple && (
|
||||
<Button
|
||||
onClick={onDeleteMultiple}
|
||||
variant="primary"
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
padding: 0;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.selectAllCheckbox {
|
||||
|
|
@ -293,13 +294,8 @@
|
|||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
padding-top: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.listItem:hover .itemActions,
|
||||
.listItem.selected .itemActions {
|
||||
opacity: 1;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.itemFields {
|
||||
|
|
|
|||
|
|
@ -701,74 +701,81 @@ export function FormGeneratorList<T extends Record<string, any>>({
|
|||
) : (
|
||||
<>
|
||||
{/* Select All Header */}
|
||||
{selectable && displayData.length > 0 && (
|
||||
{selectable && (
|
||||
<div className={styles.listHeader}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={(() => {
|
||||
const selectableIndices = displayData
|
||||
.map((row, index) => ({ row, index }))
|
||||
.filter(({ row }) => !isItemSelectable || isItemSelectable(row))
|
||||
.map(({ index }) => index);
|
||||
return selectedItems.size === selectableIndices.length && selectableIndices.length > 0;
|
||||
})()}
|
||||
onChange={handleSelectAll}
|
||||
title={t('formgen.select.all', 'Select all items')}
|
||||
className={styles.selectAllCheckbox}
|
||||
/>
|
||||
{displayData.length > 0 && (
|
||||
<>
|
||||
{hasSelectedItems && (onDeleteMultiple || onDelete) ? (
|
||||
// Show delete button when items are selected
|
||||
<div className={styles.headerDeleteButtonContainer}>
|
||||
{isConfirmingDelete ? (
|
||||
<div className={actionButtonStyles.deleteConfirmButtons}>
|
||||
<button
|
||||
onClick={handleConfirmDelete}
|
||||
className={`${actionButtonStyles.actionButton} ${actionButtonStyles.confirmButton}`}
|
||||
title={t('formgen.delete.confirm', 'Confirm delete')}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<span className={actionButtonStyles.actionIcon}>
|
||||
<IoIosCheckmark />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCancelDelete}
|
||||
className={`${actionButtonStyles.actionButton} ${actionButtonStyles.cancelButton}`}
|
||||
title={t('formgen.delete.cancel', 'Cancel delete')}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<span className={actionButtonStyles.actionIcon}>
|
||||
<IoIosClose />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleDeleteMultipleClick}
|
||||
className={`${actionButtonStyles.actionButton} ${actionButtonStyles.delete} ${styles.headerDeleteButton} ${isDeleting ? actionButtonStyles.loading : ''}`}
|
||||
title={allItemsSelected
|
||||
? t('formgen.delete.all', `Delete all ${selectedItems.size} items`)
|
||||
: t('formgen.delete.multiple', `Delete ${selectedItems.size} selected items`)}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<span className={actionButtonStyles.actionIcon}>
|
||||
<IoIosTrash />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
// Show select all checkbox when no items are selected
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={(() => {
|
||||
const selectableIndices = displayData
|
||||
.map((row, index) => ({ row, index }))
|
||||
.filter(({ row }) => !isItemSelectable || isItemSelectable(row))
|
||||
.map(({ index }) => index);
|
||||
return selectedItems.size === selectableIndices.length && selectableIndices.length > 0;
|
||||
})()}
|
||||
onChange={handleSelectAll}
|
||||
title={t('formgen.select.all', 'Select all items')}
|
||||
className={styles.selectAllCheckbox}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{title && (
|
||||
<h3 className={styles.listTitle}>{title}</h3>
|
||||
)}
|
||||
{title && data.length > 0 && (
|
||||
<span className={styles.listCount}>({data.length})</span>
|
||||
)}
|
||||
{hasSelectedItems && (onDeleteMultiple || onDelete) && (
|
||||
<div className={styles.headerDeleteButtonContainer}>
|
||||
{isConfirmingDelete ? (
|
||||
<div className={actionButtonStyles.deleteConfirmButtons}>
|
||||
<button
|
||||
onClick={handleConfirmDelete}
|
||||
className={`${actionButtonStyles.actionButton} ${actionButtonStyles.confirmButton}`}
|
||||
title={t('formgen.delete.confirm', 'Confirm delete')}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<span className={actionButtonStyles.actionIcon}>
|
||||
<IoIosCheckmark />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCancelDelete}
|
||||
className={`${actionButtonStyles.actionButton} ${actionButtonStyles.cancelButton}`}
|
||||
title={t('formgen.delete.cancel', 'Cancel delete')}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<span className={actionButtonStyles.actionIcon}>
|
||||
<IoIosClose />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleDeleteMultipleClick}
|
||||
className={`${actionButtonStyles.actionButton} ${actionButtonStyles.delete} ${styles.headerDeleteButton} ${isDeleting ? actionButtonStyles.loading : ''}`}
|
||||
title={allItemsSelected
|
||||
? t('formgen.delete.all', `Delete all ${selectedItems.size} items`)
|
||||
: t('formgen.delete.multiple', `Delete ${selectedItems.size} selected items`)}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<span className={actionButtonStyles.actionIcon}>
|
||||
<IoIosTrash />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{headerButton && (
|
||||
<div className={styles.headerButtonWrapper}>
|
||||
{headerButton}
|
||||
</div>
|
||||
)}
|
||||
{sortable && (
|
||||
{sortable && displayData.length > 0 && (
|
||||
<div className={styles.sortControls}>
|
||||
{detectedFields.map(field => (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { GenericPageData, PageButton, PageContent, resolveLanguageText, SettingsFieldConfig, SettingsSectionConfig, GenericDataHook } from './pageInterface';
|
||||
import { FormGenerator } from '../../components/FormGenerator';
|
||||
import { FormGenerator, FormGeneratorList } from '../../components/FormGenerator';
|
||||
import { FormGeneratorForm, AttributeDefinition } from '../../components/FormGenerator/FormGeneratorForm';
|
||||
import { Button, UploadButton, CreateButton, TextField, Messages, ChatMessage, LogMessage, DropdownSelect, Log, WorkflowStatus, Tabs } from '../../components/UiComponents';
|
||||
import { Popup } from '../../components/UiComponents/Popup';
|
||||
|
|
@ -10,6 +10,7 @@ import { DragDropOverlay } from '../../components/UiComponents/DragDropOverlay';
|
|||
import { useLanguage } from '../../providers/language/LanguageContext';
|
||||
import { usePermissions } from '../../hooks/usePermissions';
|
||||
import { FiPaperclip } from 'react-icons/fi';
|
||||
import { IoMdAdd } from 'react-icons/io';
|
||||
import type { WorkflowFile } from '../../hooks/playground/useDashboardInputForm';
|
||||
import styles from '../../styles/pages.module.css';
|
||||
|
||||
|
|
@ -172,9 +173,11 @@ const TabsRenderer: React.FC<{
|
|||
label: resolveLanguageText(tab.label, t),
|
||||
content: (
|
||||
<>
|
||||
{tab.content.map((nestedContent, index) =>
|
||||
renderContent(nestedContent, index)
|
||||
)}
|
||||
{tab.content.map((nestedContent, index) => (
|
||||
<React.Fragment key={nestedContent.id || `${tab.id}-${index}`}>
|
||||
{renderContent(nestedContent)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}));
|
||||
|
|
@ -1435,7 +1438,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
|
|||
<div style={{ flexShrink: 0, display: 'flex', gap: '8px' }}>
|
||||
<UploadButton
|
||||
onUpload={hookData.handleFileUpload ? async (file: File) => {
|
||||
const result = await hookData.handleFileUpload!(file);
|
||||
await hookData.handleFileUpload!(file);
|
||||
// Error handling is done in the hook
|
||||
} : async () => {}}
|
||||
disabled={hookData.isSubmitting || false}
|
||||
|
|
@ -1774,8 +1777,8 @@ const PageRenderer: React.FC<PageRendererProps> = ({
|
|||
const gap = columnsConfig.gap || '1rem';
|
||||
|
||||
return (
|
||||
<div
|
||||
key={content.id}
|
||||
<div
|
||||
key={content.id}
|
||||
className={styles.columnsContainer}
|
||||
style={{
|
||||
display: 'grid',
|
||||
|
|
@ -1785,15 +1788,152 @@ const PageRenderer: React.FC<PageRendererProps> = ({
|
|||
>
|
||||
{columnsConfig.columns.map((column, colIndex) => (
|
||||
<div key={column.id} className={styles.column}>
|
||||
{column.content.map((nestedContent, index) =>
|
||||
renderContent(nestedContent, `${colIndex}-${index}`)
|
||||
)}
|
||||
{column.content.map((nestedContent, index) => (
|
||||
<React.Fragment key={nestedContent.id || `${colIndex}-${index}`}>
|
||||
{renderContent(nestedContent)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
case 'chatHistory': {
|
||||
const config = content.chatHistoryConfig || {};
|
||||
const threads = (hookData as any)?.threads || [];
|
||||
const selectedThreadId = (hookData as any)?.selectedThreadId || null;
|
||||
const threadsLoading = (hookData as any)?.threadsLoading || false;
|
||||
const threadsError = (hookData as any)?.threadsError || null;
|
||||
const selectThread = (hookData as any)?.selectThread;
|
||||
const handleDelete = (hookData as any)?.handleDelete;
|
||||
const deletingItems = (hookData as any)?.deletingItems || new Set();
|
||||
const startNewChat = (hookData as any)?.startNewChat;
|
||||
|
||||
// Get thread preview text for display
|
||||
const getThreadPreview = (thread: any): string => {
|
||||
if (thread.name) return thread.name;
|
||||
if (thread.firstMessage) return thread.firstMessage.substring(0, 50) + (thread.firstMessage.length > 50 ? '...' : '');
|
||||
return t('chat_history.no_message_content', 'No message content available');
|
||||
};
|
||||
|
||||
// Prepare data for FormGeneratorList - add a display name field
|
||||
const threadsWithDisplayName = threads.map((thread: any) => ({
|
||||
...thread,
|
||||
displayName: getThreadPreview(thread)
|
||||
}));
|
||||
|
||||
// Define fields for FormGeneratorList - only show creation time
|
||||
const fields = [
|
||||
{
|
||||
key: 'displayName',
|
||||
label: '', // No label needed, will be shown as first field
|
||||
type: 'string' as const
|
||||
},
|
||||
{
|
||||
key: 'startedAt',
|
||||
label: '', // No label needed, will be shown as metadata
|
||||
type: 'date' as const
|
||||
}
|
||||
];
|
||||
|
||||
// Ensure hookData has required properties for DeleteActionButton
|
||||
const enhancedHookData = {
|
||||
...hookData,
|
||||
refetch: (hookData as any)?.refetch || (hookData as any)?.loadThreads || (() => {}),
|
||||
handleDelete: handleDelete || (() => Promise.resolve(false)),
|
||||
removeOptimistically: (hookData as any)?.removeOptimistically || (hookData as any)?.removeThreadOptimistically,
|
||||
deletingItems: deletingItems
|
||||
};
|
||||
|
||||
// Configure action buttons - delete button (always show)
|
||||
const actionButtons = [
|
||||
{
|
||||
type: 'delete' as const,
|
||||
disabled: (row: any) => !handleDelete || deletingItems.has(row.id),
|
||||
loading: (row: any) => deletingItems.has(row.id),
|
||||
title: t('chat_history.delete_tooltip', 'Delete workflow'),
|
||||
operationName: 'handleDelete',
|
||||
loadingStateName: 'deletingItems'
|
||||
}
|
||||
];
|
||||
|
||||
// Handle item click to select thread (checkbox and delete button stop propagation)
|
||||
const handleItemClick = (row: any) => {
|
||||
if (!deletingItems.has(row.id) && selectThread) {
|
||||
selectThread(row.id);
|
||||
}
|
||||
};
|
||||
|
||||
// Get data attributes for styling selected items
|
||||
const getItemDataAttributes = (row: any) => {
|
||||
return {
|
||||
'selected-thread-id': row.id === selectedThreadId ? 'true' : 'false'
|
||||
};
|
||||
};
|
||||
|
||||
// Show error state if there's an error
|
||||
if (threadsError) {
|
||||
return (
|
||||
<div key={content.id} className={styles.chatHistorySection}>
|
||||
<div className={styles.chatHistoryError}>
|
||||
<p>{t('chat_history.error_loading', 'Error loading workflows:')} {threadsError}</p>
|
||||
{(hookData as any)?.loadThreads && (
|
||||
<button
|
||||
onClick={() => (hookData as any).loadThreads()}
|
||||
className={styles.retryButton}
|
||||
>
|
||||
{t('chat_history.try_again', 'Try Again')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={content.id} className={styles.chatHistorySection}>
|
||||
<FormGeneratorList
|
||||
data={threadsWithDisplayName}
|
||||
fields={fields}
|
||||
title={t('chat_history.title', 'Chat History')}
|
||||
loading={threadsLoading}
|
||||
emptyMessage={config.emptyMessage
|
||||
? resolveLanguageText(config.emptyMessage, t)
|
||||
: t('chat_history.empty_state', 'No workflows available')}
|
||||
onItemClick={handleItemClick}
|
||||
actionButtons={actionButtons}
|
||||
onDelete={handleDelete}
|
||||
onDeleteMultiple={handleDelete ? async (rowsToDelete: any[]) => {
|
||||
// Handle multiple delete
|
||||
for (const rowToDelete of rowsToDelete) {
|
||||
await handleDelete(rowToDelete.id);
|
||||
}
|
||||
} : undefined}
|
||||
hookData={enhancedHookData}
|
||||
getItemDataAttributes={getItemDataAttributes}
|
||||
searchable={false}
|
||||
filterable={false}
|
||||
sortable={false}
|
||||
pagination={false}
|
||||
selectable={true}
|
||||
className={styles.chatHistoryList}
|
||||
headerButton={startNewChat ? (
|
||||
<Button
|
||||
onClick={() => startNewChat()}
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon={IoMdAdd}
|
||||
className={styles.chatHistoryNewChatButton}
|
||||
>
|
||||
{t('chat_history.new_chat', 'New Chat')}
|
||||
</Button>
|
||||
) : undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const PekMapView: React.FC = () => {
|
|||
const adjacentSet = new Map<string, any>();
|
||||
selectedParcels.forEach(parcel => {
|
||||
if (parcel.adjacent_parcels) {
|
||||
parcel.adjacent_parcels.forEach(adj => {
|
||||
parcel.adjacent_parcels.forEach((adj: { id: string }) => {
|
||||
if (!adjacentSet.has(adj.id)) {
|
||||
adjacentSet.set(adj.id, adj);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export interface SettingsConfig {
|
|||
// Content section for paragraphs
|
||||
export interface PageContent {
|
||||
id: string;
|
||||
type: 'paragraph' | 'heading' | 'list' | 'code' | 'divider' | 'custom' | 'table' | 'inputForm' | 'messages' | 'settings' | 'log' | 'tabs' | 'columns';
|
||||
type: 'paragraph' | 'heading' | 'list' | 'code' | 'divider' | 'custom' | 'table' | 'inputForm' | 'messages' | 'settings' | 'log' | 'tabs' | 'columns' | 'chatHistory';
|
||||
content?: string | LanguageText; // Optional for dividers
|
||||
level?: number; // For headings (1-6)
|
||||
items?: (string | LanguageText)[]; // For lists
|
||||
|
|
@ -150,6 +150,10 @@ export interface PageContent {
|
|||
logConfig?: {
|
||||
emptyMessage?: string | LanguageText;
|
||||
};
|
||||
// Chat history-specific properties
|
||||
chatHistoryConfig?: {
|
||||
emptyMessage?: string | LanguageText;
|
||||
};
|
||||
// Tabs-specific properties
|
||||
tabsConfig?: {
|
||||
tabs: Array<{
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ export function useChatbot() {
|
|||
processedLogsRef.current.clear(); // Clear processed logs tracking
|
||||
|
||||
// Reset thinking message refs
|
||||
const thinkingId = thinkingMessageIdRef.current;
|
||||
thinkingMessageIdRef.current = null;
|
||||
thinkingLogsRef.current = [];
|
||||
|
||||
|
|
@ -189,13 +188,8 @@ export function useChatbot() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if this is an assistant message that should clear thinking message
|
||||
const messageRole = messageData.role?.toLowerCase();
|
||||
const isAssistantMessage = messageRole === 'assistant' || messageRole === 'ai' || messageRole === 'system';
|
||||
|
||||
// ALWAYS clear thinking message when ANY message arrives (not just assistant)
|
||||
// The thinking message should disappear when the real message comes
|
||||
const thinkingId = thinkingMessageIdRef.current;
|
||||
|
||||
// Check if we've already processed this message
|
||||
const messageId = messageData.id;
|
||||
|
|
@ -367,7 +361,7 @@ export function useChatbot() {
|
|||
}, []);
|
||||
|
||||
// Handle file upload
|
||||
const handleFileUpload = useCallback(async (file: File): Promise<{ success: boolean; data?: any }> => {
|
||||
const handleFileUpload = useCallback(async (file: File): Promise<{ success: boolean; data: any }> => {
|
||||
setUploadError(null);
|
||||
setUploadingFile(true);
|
||||
|
||||
|
|
@ -417,7 +411,7 @@ export function useChatbot() {
|
|||
console.error('File upload failed:', err);
|
||||
const errorMessage = err.message || 'Failed to upload file';
|
||||
setUploadError(errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
return { success: false, data: null };
|
||||
} finally {
|
||||
setUploadingFile(false);
|
||||
}
|
||||
|
|
@ -517,9 +511,6 @@ export function useChatbot() {
|
|||
|
||||
console.log('[handleSubmit] Final requestBody:', JSON.stringify(requestBody, null, 2));
|
||||
|
||||
// Track if workflow was created in this request
|
||||
let workflowCreated = false;
|
||||
|
||||
// Clear thinking message when starting a new request
|
||||
clearThinkingMessage();
|
||||
processedLogsRef.current.clear(); // Clear processed logs for new request
|
||||
|
|
@ -553,7 +544,6 @@ export function useChatbot() {
|
|||
// New workflow created - select it automatically
|
||||
setWorkflowId(messageData.workflowId);
|
||||
setSelectedThreadId(messageData.workflowId);
|
||||
workflowCreated = true;
|
||||
} else {
|
||||
// Existing workflow - ensure it's selected
|
||||
setSelectedThreadId(messageData.workflowId);
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@ export function createProjectsTableHook(): () => GenericDataHook {
|
|||
}, []);
|
||||
|
||||
// Update project
|
||||
const handleProjectUpdate = useCallback(async (id: string, updateData: any, originalData?: any): Promise<{ success: boolean }> => {
|
||||
const handleProjectUpdate = useCallback(async (id: string, updateData: any): Promise<{ success: boolean }> => {
|
||||
try {
|
||||
setEditingProjects(prev => new Set(prev).add(id));
|
||||
|
||||
|
|
@ -999,7 +999,7 @@ export function createParzellenTableHook(): () => GenericDataHook {
|
|||
}, []);
|
||||
|
||||
// Update parzelle
|
||||
const handleParzelleUpdate = useCallback(async (id: string, updateData: any, originalData?: any): Promise<{ success: boolean }> => {
|
||||
const handleParzelleUpdate = useCallback(async (id: string, updateData: any): Promise<{ success: boolean }> => {
|
||||
try {
|
||||
setEditingParzellen(prev => new Set(prev).add(id));
|
||||
|
||||
|
|
|
|||
|
|
@ -458,6 +458,10 @@
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
.chatHistoryNewChatButton {
|
||||
border-radius: 25px !important;
|
||||
}
|
||||
|
||||
.chatHistoryEmpty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
Loading…
Reference in a new issue