fix:readded deleted code, fixed build

This commit is contained in:
Ida Dittrich 2026-01-12 09:15:07 +01:00
parent 5808bd4bee
commit 239fd328bc
11 changed files with 243 additions and 102 deletions

View file

@ -43,10 +43,10 @@ export async function fetchAttributes(
request: ApiRequestFunction, request: ApiRequestFunction,
entityType: string entityType: string
): Promise<AttributeDefinition[]> { ): Promise<AttributeDefinition[]> {
const data = await request<any>({ const data = await request({
url: `/api/attributes/${entityType}`, url: `/api/attributes/${entityType}`,
method: 'get' method: 'get'
}); }) as any;
// Extract attributes from response - check if response.data.attributes exists, otherwise check if response.data is an array // Extract attributes from response - check if response.data.attributes exists, otherwise check if response.data is an array
let attrs: AttributeDefinition[] = []; let attrs: AttributeDefinition[] = [];
@ -81,10 +81,10 @@ export async function fetchConnectionAttributes(request: ApiRequestFunction): Pr
* Endpoint: GET /api/attributes/FileItem * Endpoint: GET /api/attributes/FileItem
*/ */
export async function fetchFileAttributes(request: ApiRequestFunction): Promise<AttributeDefinition[]> { export async function fetchFileAttributes(request: ApiRequestFunction): Promise<AttributeDefinition[]> {
const data = await request<AttributeDefinition[] | { attributes: AttributeDefinition[] }>({ const data = await request({
url: '/api/attributes/FileItem', url: '/api/attributes/FileItem',
method: 'get' method: 'get'
}); }) as AttributeDefinition[] | { attributes: AttributeDefinition[] };
// Handle different response formats // Handle different response formats
if (Array.isArray(data)) { if (Array.isArray(data)) {

View file

@ -206,12 +206,12 @@ export async function stopChatbotApi(
request: ApiRequestFunction, request: ApiRequestFunction,
workflowId: string workflowId: string
): Promise<ChatbotWorkflow> { ): Promise<ChatbotWorkflow> {
const data = await request<ChatbotWorkflow>({ const data = await request({
url: `/api/chatbot/${workflowId}/stop`, url: `/api/chatbot/${workflowId}/stop`,
method: 'post' method: 'post'
}); });
return data; return data as ChatbotWorkflow;
} }
/** /**
@ -229,11 +229,11 @@ export async function getChatbotThreadsApi(
console.log(`[getChatbotThreadsApi] Fetching threads with params:`, requestParams); console.log(`[getChatbotThreadsApi] Fetching threads with params:`, requestParams);
const data = await request<any>({ const data = await request({
url: '/api/chatbot/threads', url: '/api/chatbot/threads',
method: 'get', method: 'get',
params: requestParams params: requestParams
}); }) as any;
console.log(`[getChatbotThreadsApi] Full response:`, JSON.stringify(data, null, 2)); console.log(`[getChatbotThreadsApi] Full response:`, JSON.stringify(data, null, 2));
console.log(`[getChatbotThreadsApi] Response structure:`, { console.log(`[getChatbotThreadsApi] Response structure:`, {
@ -265,11 +265,11 @@ export async function getChatbotThreadApi(
): Promise<{ workflow: ChatbotWorkflow; chatData: { items: ChatDataItem[] } }> { ): Promise<{ workflow: ChatbotWorkflow; chatData: { items: ChatDataItem[] } }> {
console.log(`[getChatbotThreadApi] Fetching thread with workflowId: ${workflowId}`); 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', url: '/api/chatbot/threads',
method: 'get', method: 'get',
params: { workflowId } params: { workflowId }
}); }) as { workflow: ChatbotWorkflow; chatData: { items: ChatDataItem[] } };
console.log(`[getChatbotThreadApi] Full response for workflowId ${workflowId}:`, JSON.stringify(data, null, 2)); console.log(`[getChatbotThreadApi] Full response for workflowId ${workflowId}:`, JSON.stringify(data, null, 2));
console.log(`[getChatbotThreadApi] Response structure:`, { console.log(`[getChatbotThreadApi] Response structure:`, {
@ -279,7 +279,7 @@ export async function getChatbotThreadApi(
hasItems: !!data.chatData?.items, hasItems: !!data.chatData?.items,
chatDataKeys: data.chatData ? Object.keys(data.chatData) : [], chatDataKeys: data.chatData ? Object.keys(data.chatData) : [],
itemsLength: Array.isArray(data.chatData?.items) ? data.chatData.items.length : 'not an array', 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 { return {
@ -301,7 +301,7 @@ export async function deleteChatbotWorkflowApi(
workflowId: string workflowId: string
): Promise<boolean> { ): Promise<boolean> {
try { try {
await request<any>({ await request({
url: `/api/chatbot/${workflowId}`, url: `/api/chatbot/${workflowId}`,
method: 'delete' method: 'delete'
}); });

View file

@ -162,7 +162,7 @@ export function FormGeneratorControls({
{/* Delete Controls - Show when items are selected */} {/* Delete Controls - Show when items are selected */}
{selectable && selectedCount > 0 && ( {selectable && selectedCount > 0 && (
<div className={styles.deleteControlsIntegrated}> <div className={styles.deleteControlsIntegrated}>
{selectedCount === 1 && onDeleteSingle && !(selectedCount === _displayData.length && _displayData.length > 0) && ( {selectedCount === 1 && onDeleteSingle && !(selectedCount === displayData.length && displayData.length > 0) && (
<Button <Button
onClick={onDeleteSingle} onClick={onDeleteSingle}
variant="primary" variant="primary"
@ -172,7 +172,7 @@ export function FormGeneratorControls({
{t('formgen.delete.single', 'Delete')} {t('formgen.delete.single', 'Delete')}
</Button> </Button>
)} )}
{(selectedCount > 1 || (selectedCount === _displayData.length && _displayData.length > 0)) && onDeleteMultiple && ( {(selectedCount > 1 || (selectedCount === displayData.length && displayData.length > 0)) && onDeleteMultiple && (
<Button <Button
onClick={onDeleteMultiple} onClick={onDeleteMultiple}
variant="primary" variant="primary"

View file

@ -74,6 +74,7 @@
padding: 0; padding: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
margin-top: 10px;
} }
.selectAllCheckbox { .selectAllCheckbox {
@ -293,13 +294,8 @@
gap: 4px; gap: 4px;
flex-shrink: 0; flex-shrink: 0;
padding-top: 0; padding-top: 0;
opacity: 0;
transition: opacity 0.15s ease;
}
.listItem:hover .itemActions,
.listItem.selected .itemActions {
opacity: 1; opacity: 1;
transition: opacity 0.15s ease;
} }
.itemFields { .itemFields {

View file

@ -701,74 +701,81 @@ export function FormGeneratorList<T extends Record<string, any>>({
) : ( ) : (
<> <>
{/* Select All Header */} {/* Select All Header */}
{selectable && displayData.length > 0 && ( {selectable && (
<div className={styles.listHeader}> <div className={styles.listHeader}>
<input {displayData.length > 0 && (
type="checkbox" <>
checked={(() => { {hasSelectedItems && (onDeleteMultiple || onDelete) ? (
const selectableIndices = displayData // Show delete button when items are selected
.map((row, index) => ({ row, index })) <div className={styles.headerDeleteButtonContainer}>
.filter(({ row }) => !isItemSelectable || isItemSelectable(row)) {isConfirmingDelete ? (
.map(({ index }) => index); <div className={actionButtonStyles.deleteConfirmButtons}>
return selectedItems.size === selectableIndices.length && selectableIndices.length > 0; <button
})()} onClick={handleConfirmDelete}
onChange={handleSelectAll} className={`${actionButtonStyles.actionButton} ${actionButtonStyles.confirmButton}`}
title={t('formgen.select.all', 'Select all items')} title={t('formgen.delete.confirm', 'Confirm delete')}
className={styles.selectAllCheckbox} 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 && ( {title && (
<h3 className={styles.listTitle}>{title}</h3> <h3 className={styles.listTitle}>{title}</h3>
)} )}
{title && data.length > 0 && ( {title && data.length > 0 && (
<span className={styles.listCount}>({data.length})</span> <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 && ( {headerButton && (
<div className={styles.headerButtonWrapper}> <div className={styles.headerButtonWrapper}>
{headerButton} {headerButton}
</div> </div>
)} )}
{sortable && ( {sortable && displayData.length > 0 && (
<div className={styles.sortControls}> <div className={styles.sortControls}>
{detectedFields.map(field => ( {detectedFields.map(field => (
<button <button

View file

@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { GenericPageData, PageButton, PageContent, resolveLanguageText, SettingsFieldConfig, SettingsSectionConfig, GenericDataHook } from './pageInterface'; 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 { FormGeneratorForm, AttributeDefinition } from '../../components/FormGenerator/FormGeneratorForm';
import { Button, UploadButton, CreateButton, TextField, Messages, ChatMessage, LogMessage, DropdownSelect, Log, WorkflowStatus, Tabs } from '../../components/UiComponents'; import { Button, UploadButton, CreateButton, TextField, Messages, ChatMessage, LogMessage, DropdownSelect, Log, WorkflowStatus, Tabs } from '../../components/UiComponents';
import { Popup } from '../../components/UiComponents/Popup'; import { Popup } from '../../components/UiComponents/Popup';
@ -10,6 +10,7 @@ import { DragDropOverlay } from '../../components/UiComponents/DragDropOverlay';
import { useLanguage } from '../../providers/language/LanguageContext'; import { useLanguage } from '../../providers/language/LanguageContext';
import { usePermissions } from '../../hooks/usePermissions'; import { usePermissions } from '../../hooks/usePermissions';
import { FiPaperclip } from 'react-icons/fi'; import { FiPaperclip } from 'react-icons/fi';
import { IoMdAdd } from 'react-icons/io';
import type { WorkflowFile } from '../../hooks/playground/useDashboardInputForm'; import type { WorkflowFile } from '../../hooks/playground/useDashboardInputForm';
import styles from '../../styles/pages.module.css'; import styles from '../../styles/pages.module.css';
@ -172,9 +173,11 @@ const TabsRenderer: React.FC<{
label: resolveLanguageText(tab.label, t), label: resolveLanguageText(tab.label, t),
content: ( content: (
<> <>
{tab.content.map((nestedContent, index) => {tab.content.map((nestedContent, index) => (
renderContent(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' }}> <div style={{ flexShrink: 0, display: 'flex', gap: '8px' }}>
<UploadButton <UploadButton
onUpload={hookData.handleFileUpload ? async (file: File) => { onUpload={hookData.handleFileUpload ? async (file: File) => {
const result = await hookData.handleFileUpload!(file); await hookData.handleFileUpload!(file);
// Error handling is done in the hook // Error handling is done in the hook
} : async () => {}} } : async () => {}}
disabled={hookData.isSubmitting || false} disabled={hookData.isSubmitting || false}
@ -1774,8 +1777,8 @@ const PageRenderer: React.FC<PageRendererProps> = ({
const gap = columnsConfig.gap || '1rem'; const gap = columnsConfig.gap || '1rem';
return ( return (
<div <div
key={content.id} key={content.id}
className={styles.columnsContainer} className={styles.columnsContainer}
style={{ style={{
display: 'grid', display: 'grid',
@ -1785,15 +1788,152 @@ const PageRenderer: React.FC<PageRendererProps> = ({
> >
{columnsConfig.columns.map((column, colIndex) => ( {columnsConfig.columns.map((column, colIndex) => (
<div key={column.id} className={styles.column}> <div key={column.id} className={styles.column}>
{column.content.map((nestedContent, index) => {column.content.map((nestedContent, index) => (
renderContent(nestedContent, `${colIndex}-${index}`) <React.Fragment key={nestedContent.id || `${colIndex}-${index}`}>
)} {renderContent(nestedContent)}
</React.Fragment>
))}
</div> </div>
))} ))}
</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: default:
return null; return null;
} }

View file

@ -20,7 +20,7 @@ const PekMapView: React.FC = () => {
const adjacentSet = new Map<string, any>(); const adjacentSet = new Map<string, any>();
selectedParcels.forEach(parcel => { selectedParcels.forEach(parcel => {
if (parcel.adjacent_parcels) { if (parcel.adjacent_parcels) {
parcel.adjacent_parcels.forEach(adj => { parcel.adjacent_parcels.forEach((adj: { id: string }) => {
if (!adjacentSet.has(adj.id)) { if (!adjacentSet.has(adj.id)) {
adjacentSet.set(adj.id, adj); adjacentSet.set(adj.id, adj);
} }

View file

@ -125,7 +125,7 @@ export interface SettingsConfig {
// Content section for paragraphs // Content section for paragraphs
export interface PageContent { export interface PageContent {
id: string; 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 content?: string | LanguageText; // Optional for dividers
level?: number; // For headings (1-6) level?: number; // For headings (1-6)
items?: (string | LanguageText)[]; // For lists items?: (string | LanguageText)[]; // For lists
@ -150,6 +150,10 @@ export interface PageContent {
logConfig?: { logConfig?: {
emptyMessage?: string | LanguageText; emptyMessage?: string | LanguageText;
}; };
// Chat history-specific properties
chatHistoryConfig?: {
emptyMessage?: string | LanguageText;
};
// Tabs-specific properties // Tabs-specific properties
tabsConfig?: { tabsConfig?: {
tabs: Array<{ tabs: Array<{

View file

@ -69,7 +69,6 @@ export function useChatbot() {
processedLogsRef.current.clear(); // Clear processed logs tracking processedLogsRef.current.clear(); // Clear processed logs tracking
// Reset thinking message refs // Reset thinking message refs
const thinkingId = thinkingMessageIdRef.current;
thinkingMessageIdRef.current = null; thinkingMessageIdRef.current = null;
thinkingLogsRef.current = []; thinkingLogsRef.current = [];
@ -189,13 +188,8 @@ export function useChatbot() {
return; 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) // ALWAYS clear thinking message when ANY message arrives (not just assistant)
// The thinking message should disappear when the real message comes // The thinking message should disappear when the real message comes
const thinkingId = thinkingMessageIdRef.current;
// Check if we've already processed this message // Check if we've already processed this message
const messageId = messageData.id; const messageId = messageData.id;
@ -367,7 +361,7 @@ export function useChatbot() {
}, []); }, []);
// Handle file upload // 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); setUploadError(null);
setUploadingFile(true); setUploadingFile(true);
@ -417,7 +411,7 @@ export function useChatbot() {
console.error('File upload failed:', err); console.error('File upload failed:', err);
const errorMessage = err.message || 'Failed to upload file'; const errorMessage = err.message || 'Failed to upload file';
setUploadError(errorMessage); setUploadError(errorMessage);
return { success: false, error: errorMessage }; return { success: false, data: null };
} finally { } finally {
setUploadingFile(false); setUploadingFile(false);
} }
@ -517,9 +511,6 @@ export function useChatbot() {
console.log('[handleSubmit] Final requestBody:', JSON.stringify(requestBody, null, 2)); 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 // Clear thinking message when starting a new request
clearThinkingMessage(); clearThinkingMessage();
processedLogsRef.current.clear(); // Clear processed logs for new request processedLogsRef.current.clear(); // Clear processed logs for new request
@ -553,7 +544,6 @@ export function useChatbot() {
// New workflow created - select it automatically // New workflow created - select it automatically
setWorkflowId(messageData.workflowId); setWorkflowId(messageData.workflowId);
setSelectedThreadId(messageData.workflowId); setSelectedThreadId(messageData.workflowId);
workflowCreated = true;
} else { } else {
// Existing workflow - ensure it's selected // Existing workflow - ensure it's selected
setSelectedThreadId(messageData.workflowId); setSelectedThreadId(messageData.workflowId);

View file

@ -527,7 +527,7 @@ export function createProjectsTableHook(): () => GenericDataHook {
}, []); }, []);
// Update project // 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 { try {
setEditingProjects(prev => new Set(prev).add(id)); setEditingProjects(prev => new Set(prev).add(id));
@ -999,7 +999,7 @@ export function createParzellenTableHook(): () => GenericDataHook {
}, []); }, []);
// Update parzelle // 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 { try {
setEditingParzellen(prev => new Set(prev).add(id)); setEditingParzellen(prev => new Set(prev).add(id));

View file

@ -458,6 +458,10 @@
height: 16px; height: 16px;
} }
.chatHistoryNewChatButton {
border-radius: 25px !important;
}
.chatHistoryEmpty { .chatHistoryEmpty {
display: flex; display: flex;
align-items: center; align-items: center;