fix:build errors removed

This commit is contained in:
Ida Dittrich 2026-01-05 06:34:31 +01:00
parent ae6a634274
commit 079d398f8a
41 changed files with 321 additions and 205 deletions

View file

@ -43,7 +43,7 @@ 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'
}); });
@ -81,7 +81,7 @@ 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'
}); });

View file

@ -171,10 +171,19 @@ export async function registerApi(registerData: RegisterData): Promise<RegisterR
headers headers
}); });
const userData: any = response.data;
return { return {
success: true, success: true,
message: 'Registration successful', message: 'Registration successful',
user: response.data user: userData && typeof userData === 'object' && 'id' in userData ? {
id: String(userData.id || ''),
username: String(userData.username || ''),
email: String(userData.email || ''),
fullName: String(userData.fullName || ''),
language: String(userData.language || 'en'),
enabled: Boolean(userData.enabled !== false),
privilege: String(userData.privilege || 'user')
} : undefined
}; };
} }
@ -186,7 +195,7 @@ export async function registerWithMsalApi(
request: ApiRequestFunction, request: ApiRequestFunction,
userData: MsalRegisterData userData: MsalRegisterData
): Promise<RegisterResponse> { ): Promise<RegisterResponse> {
const response = await request<RegisterResponse>({ const response = await request({
url: '/api/msft/register', url: '/api/msft/register',
method: 'post', method: 'post',
data: userData, data: userData,
@ -197,10 +206,19 @@ export async function registerWithMsalApi(
} }
}); });
const responseData: any = response;
return { return {
success: true, success: true,
message: 'Registration successful', message: 'Registration successful',
user: response user: responseData && typeof responseData === 'object' && 'id' in responseData ? {
id: String(responseData.id || ''),
username: String(responseData.username || ''),
email: String(responseData.email || ''),
fullName: String(responseData.fullName || ''),
language: String(responseData.language || 'en'),
enabled: Boolean((responseData as any).enabled !== false),
privilege: String((responseData as any).privilege || 'user')
} : undefined
}; };
} }

View file

@ -78,7 +78,7 @@ export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<an
* Fetch connection attributes from backend * Fetch connection attributes from backend
* Endpoint: GET /api/attributes/UserConnection * Endpoint: GET /api/attributes/UserConnection
*/ */
export async function fetchConnectionAttributes(request: ApiRequestFunction): Promise<AttributeDefinition[]> { export async function fetchConnectionAttributes(_request: ApiRequestFunction): Promise<AttributeDefinition[]> {
// Note: This uses api.get directly due to response format handling // Note: This uses api.get directly due to response format handling
// For now, we'll use api.get directly in the hook as well // For now, we'll use api.get directly in the hook as well
throw new Error('fetchConnectionAttributes should use api instance directly for response format handling'); throw new Error('fetchConnectionAttributes should use api instance directly for response format handling');
@ -109,7 +109,7 @@ export async function fetchConnections(
} }
} }
const data = await request<PaginatedResponse<Connection> | Connection[]>({ const data = await request({
url: '/api/connections/', url: '/api/connections/',
method: 'get', method: 'get',
params: requestParams params: requestParams
@ -126,7 +126,7 @@ export async function createConnection(
request: ApiRequestFunction, request: ApiRequestFunction,
connectionData: CreateConnectionData connectionData: CreateConnectionData
): Promise<Connection> { ): Promise<Connection> {
return await request<Connection>({ return await request({
url: '/api/connections/', url: '/api/connections/',
method: 'post', method: 'post',
data: connectionData data: connectionData
@ -141,7 +141,7 @@ export async function connectService(
request: ApiRequestFunction, request: ApiRequestFunction,
connectionId: string connectionId: string
): Promise<ConnectResponse> { ): Promise<ConnectResponse> {
return await request<ConnectResponse>({ return await request({
url: `/api/connections/${connectionId}/connect`, url: `/api/connections/${connectionId}/connect`,
method: 'post' method: 'post'
}); });
@ -155,7 +155,7 @@ export async function disconnectService(
request: ApiRequestFunction, request: ApiRequestFunction,
connectionId: string connectionId: string
): Promise<{ message: string }> { ): Promise<{ message: string }> {
return await request<{ message: string }>({ return await request({
url: `/api/connections/${connectionId}/disconnect`, url: `/api/connections/${connectionId}/disconnect`,
method: 'post' method: 'post'
}); });
@ -169,7 +169,7 @@ export async function deleteConnection(
request: ApiRequestFunction, request: ApiRequestFunction,
connectionId: string connectionId: string
): Promise<{ message: string }> { ): Promise<{ message: string }> {
return await request<{ message: string }>({ return await request({
url: `/api/connections/${connectionId}`, url: `/api/connections/${connectionId}`,
method: 'delete' method: 'delete'
}); });
@ -184,7 +184,7 @@ export async function updateConnection(
connectionId: string, connectionId: string,
updateData: Partial<Connection> updateData: Partial<Connection>
): Promise<Connection> { ): Promise<Connection> {
return await request<Connection>({ return await request({
url: `/api/connections/${connectionId}`, url: `/api/connections/${connectionId}`,
method: 'put', method: 'put',
data: updateData data: updateData
@ -199,7 +199,7 @@ export async function refreshMicrosoftToken(
request: ApiRequestFunction, request: ApiRequestFunction,
connectionId: string connectionId: string
): Promise<Connection> { ): Promise<Connection> {
return await request<Connection>({ return await request({
url: `/api/connections/${connectionId}/refresh-microsoft-token`, url: `/api/connections/${connectionId}/refresh-microsoft-token`,
method: 'post' method: 'post'
}); });
@ -213,7 +213,7 @@ export async function refreshGoogleToken(
request: ApiRequestFunction, request: ApiRequestFunction,
connectionId: string connectionId: string
): Promise<Connection> { ): Promise<Connection> {
return await request<Connection>({ return await request({
url: `/api/connections/${connectionId}/refresh-google-token`, url: `/api/connections/${connectionId}/refresh-google-token`,
method: 'post' method: 'post'
}); });

View file

@ -58,7 +58,7 @@ export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<an
* 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'
}); });
@ -109,7 +109,7 @@ export async function fetchFiles(
} }
} }
const data = await request<PaginatedResponse<FileInfo> | FileInfo[]>({ const data = await request({
url: '/api/files/list', url: '/api/files/list',
method: 'get', method: 'get',
params: requestParams params: requestParams
@ -127,7 +127,7 @@ export async function fetchFileById(
fileId: string fileId: string
): Promise<FileInfo | null> { ): Promise<FileInfo | null> {
try { try {
const data = await request<FileInfo>({ const data = await request({
url: `/api/files/${fileId}`, url: `/api/files/${fileId}`,
method: 'get' method: 'get'
}); });
@ -147,7 +147,7 @@ export async function updateFile(
fileId: string, fileId: string,
fileData: Partial<FileInfo> fileData: Partial<FileInfo>
): Promise<FileInfo> { ): Promise<FileInfo> {
return await request<FileInfo>({ return await request({
url: `/api/files/${fileId}`, url: `/api/files/${fileId}`,
method: 'put', method: 'put',
data: fileData data: fileData

View file

@ -38,7 +38,7 @@ export async function fetchPermissions(
params.item = item; params.item = item;
} }
const data = await request<UserPermissions>({ const data = await request({
url: '/api/rbac/permissions', url: '/api/rbac/permissions',
method: 'get', method: 'get',
params params

View file

@ -84,7 +84,7 @@ export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<an
* Fetch prompt attributes from backend * Fetch prompt attributes from backend
* Endpoint: GET /api/attributes/Prompt * Endpoint: GET /api/attributes/Prompt
*/ */
export async function fetchPromptAttributes(request: ApiRequestFunction): Promise<AttributeDefinition[]> { export async function fetchPromptAttributes(_request: ApiRequestFunction): Promise<AttributeDefinition[]> {
// Note: This uses api.get directly due to response format handling // Note: This uses api.get directly due to response format handling
// For now, we'll use api.get directly in the hook as well // For now, we'll use api.get directly in the hook as well
throw new Error('fetchPromptAttributes should use api instance directly for response format handling'); throw new Error('fetchPromptAttributes should use api instance directly for response format handling');
@ -115,7 +115,7 @@ export async function fetchPrompts(
} }
} }
const data = await request<PaginatedResponse<Prompt> | Prompt[]>({ const data = await request({
url: '/api/prompts', url: '/api/prompts',
method: 'get', method: 'get',
params: requestParams params: requestParams
@ -133,7 +133,7 @@ export async function fetchPromptById(
promptId: string promptId: string
): Promise<Prompt | null> { ): Promise<Prompt | null> {
try { try {
const data = await request<Prompt>({ const data = await request({
url: `/api/prompts/${promptId}`, url: `/api/prompts/${promptId}`,
method: 'get' method: 'get'
}); });
@ -152,7 +152,7 @@ export async function createPrompt(
request: ApiRequestFunction, request: ApiRequestFunction,
promptData: CreatePromptData promptData: CreatePromptData
): Promise<Prompt> { ): Promise<Prompt> {
return await request<Prompt>({ return await request({
url: '/api/prompts', url: '/api/prompts',
method: 'post', method: 'post',
data: promptData data: promptData
@ -168,7 +168,7 @@ export async function updatePrompt(
promptId: string, promptId: string,
promptData: UpdatePromptData promptData: UpdatePromptData
): Promise<Prompt> { ): Promise<Prompt> {
return await request<Prompt>({ return await request({
url: `/api/prompts/${promptId}`, url: `/api/prompts/${promptId}`,
method: 'put', method: 'put',
data: promptData data: promptData

View file

@ -35,6 +35,8 @@ export interface AttributeDefinition {
minWidth?: number; minWidth?: number;
maxWidth?: number; maxWidth?: number;
filterOptions?: string[]; filterOptions?: string[];
readonly?: boolean;
editable?: boolean;
} }
export interface PaginationParams { export interface PaginationParams {
@ -78,7 +80,7 @@ export async function fetchCurrentUser(
endpoint = '/api/google/me'; endpoint = '/api/google/me';
} }
return await request<User>({ return await request({
url: endpoint, url: endpoint,
method: 'get' method: 'get'
}); });
@ -108,7 +110,7 @@ export async function logoutUser(
* Fetch user attributes from backend * Fetch user attributes from backend
* Endpoint: GET /api/attributes/User * Endpoint: GET /api/attributes/User
*/ */
export async function fetchUserAttributes(request: ApiRequestFunction): Promise<AttributeDefinition[]> { export async function fetchUserAttributes(_request: ApiRequestFunction): Promise<AttributeDefinition[]> {
// Note: This uses api.get directly in the hook due to response format handling // Note: This uses api.get directly in the hook due to response format handling
// Keeping the function signature here for consistency, but implementation may need api instance // Keeping the function signature here for consistency, but implementation may need api instance
throw new Error('fetchUserAttributes should use api instance directly for response format handling'); throw new Error('fetchUserAttributes should use api instance directly for response format handling');
@ -139,7 +141,7 @@ export async function fetchUsers(
} }
} }
const data = await request<PaginatedResponse<User> | User[]>({ const data = await request({
url: '/api/users/', url: '/api/users/',
method: 'get', method: 'get',
params: requestParams params: requestParams
@ -157,7 +159,7 @@ export async function fetchUserById(
userId: string userId: string
): Promise<User | null> { ): Promise<User | null> {
try { try {
const data = await request<User>({ const data = await request({
url: `/api/users/${userId}`, url: `/api/users/${userId}`,
method: 'get' method: 'get'
}); });
@ -176,7 +178,7 @@ export async function createUser(
request: ApiRequestFunction, request: ApiRequestFunction,
userData: Partial<User> userData: Partial<User>
): Promise<User> { ): Promise<User> {
return await request<User>({ return await request({
url: '/api/users', url: '/api/users',
method: 'post', method: 'post',
data: userData data: userData
@ -192,7 +194,7 @@ export async function updateUser(
userId: string, userId: string,
userData: UserUpdateData userData: UserUpdateData
): Promise<User> { ): Promise<User> {
return await request<User>({ return await request({
url: `/api/users/${userId}`, url: `/api/users/${userId}`,
method: 'put', method: 'put',
data: userData data: userData

View file

@ -75,7 +75,7 @@ export async function fetchWorkflows(request: ApiRequestFunction): Promise<Workf
console.log('📤 fetchWorkflows: Making API request to /api/workflows/'); console.log('📤 fetchWorkflows: Making API request to /api/workflows/');
try { try {
const data = await request<any>({ const data = await request({
url: '/api/workflows/', url: '/api/workflows/',
method: 'get' method: 'get'
}); });
@ -134,7 +134,7 @@ export async function fetchWorkflow(
request: ApiRequestFunction, request: ApiRequestFunction,
workflowId: string workflowId: string
): Promise<Workflow & { messages?: WorkflowMessage[]; logs?: WorkflowLog[] }> { ): Promise<Workflow & { messages?: WorkflowMessage[]; logs?: WorkflowLog[] }> {
return await request<any>({ return await request({
url: `/api/workflows/${workflowId}`, url: `/api/workflows/${workflowId}`,
method: 'get' method: 'get'
}); });
@ -148,7 +148,7 @@ export async function fetchWorkflowStatus(
request: ApiRequestFunction, request: ApiRequestFunction,
workflowId: string workflowId: string
): Promise<Workflow | { status: string } | null> { ): Promise<Workflow | { status: string } | null> {
const data = await request<any>({ const data = await request({
url: `/api/workflows/${workflowId}/status`, url: `/api/workflows/${workflowId}/status`,
method: 'get' method: 'get'
}); });
@ -174,7 +174,7 @@ export async function fetchWorkflowMessages(
messageId?: string messageId?: string
): Promise<WorkflowMessage[]> { ): Promise<WorkflowMessage[]> {
const params = messageId ? { messageId } : undefined; const params = messageId ? { messageId } : undefined;
const data = await request<any>({ const data = await request({
url: `/api/workflows/${workflowId}/messages`, url: `/api/workflows/${workflowId}/messages`,
method: 'get', method: 'get',
params params
@ -207,7 +207,7 @@ export async function fetchWorkflowLogs(
logId?: string logId?: string
): Promise<WorkflowLog[]> { ): Promise<WorkflowLog[]> {
const params = logId ? { logId } : undefined; const params = logId ? { logId } : undefined;
const data = await request<any>({ const data = await request({
url: `/api/workflows/${workflowId}/logs`, url: `/api/workflows/${workflowId}/logs`,
method: 'get', method: 'get',
params params
@ -248,7 +248,7 @@ export async function fetchChatData(
console.log('📤 fetchChatData request:', requestConfig); console.log('📤 fetchChatData request:', requestConfig);
const data = await request<any>(requestConfig); const data = await request(requestConfig);
console.log('📥 fetchChatData response:', data); console.log('📥 fetchChatData response:', data);
@ -359,7 +359,7 @@ export async function startWorkflowApi(
console.log(' Request Body:', JSON.stringify(requestBody, null, 2)); console.log(' Request Body:', JSON.stringify(requestBody, null, 2));
console.log(' Full Request Config:', JSON.stringify(requestConfig, null, 2)); console.log(' Full Request Config:', JSON.stringify(requestConfig, null, 2));
const response = await request<StartWorkflowResponse>(requestConfig); const response = await request(requestConfig);
console.log('📥 startWorkflow response:', response); console.log('📥 startWorkflow response:', response);
@ -389,7 +389,7 @@ export async function updateWorkflowApi(
workflowId: string, workflowId: string,
updateData: Partial<{ name: string; description?: string; tags?: string[] }> updateData: Partial<{ name: string; description?: string; tags?: string[] }>
): Promise<Workflow> { ): Promise<Workflow> {
return await request<Workflow>({ return await request({
url: `/api/workflows/${workflowId}`, url: `/api/workflows/${workflowId}`,
method: 'put', method: 'put',
data: updateData data: updateData
@ -494,7 +494,7 @@ export async function fetchAttributes(
request: ApiRequestFunction, request: ApiRequestFunction,
entityType: string = 'ChatWorkflow' entityType: string = 'ChatWorkflow'
): 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'
}); });

View file

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { MdModeEdit } from 'react-icons/md'; import { MdModeEdit } from 'react-icons/md';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import { Popup } from '../../../UiComponents/Popup'; import { Popup } from '../../../UiComponents/Popup';
import { FormGeneratorForm } from '../../FormGeneratorForm'; import { FormGeneratorForm, AttributeDefinition } from '../../FormGeneratorForm';
import styles from '../ActionButton.module.css'; import styles from '../ActionButton.module.css';
export interface EditActionButtonProps<T = any> { export interface EditActionButtonProps<T = any> {
@ -154,16 +154,18 @@ export function EditActionButton<T = any>({
// Get the item ID from the row // Get the item ID from the row
const itemId = (editData as any)[idField]; const itemId = (editData as any)[idField];
// Get edit fields configuration // Get edit fields configuration from attributes
const fields = getEditFields(); const attributes = getAttributes();
const fields = attributes || [];
// Extract the fields to update from the edit data // Extract the fields to update from the edit data
const updateData: any = {}; const updateData: any = {};
fields.forEach(field => { fields.forEach((field: AttributeDefinition) => {
if (field.editable !== false) { if (field.editable !== false) {
const value = (updatedData as any)[field.key]; const fieldName = field.name;
const value = (updatedData as any)[fieldName];
if (value !== undefined) { if (value !== undefined) {
updateData[field.key] = value; updateData[fieldName] = value;
} }
} }
}); });

View file

@ -30,9 +30,9 @@ export function PlayActionButton<T = any>({
loading = false, loading = false,
className = '', className = '',
title, title,
hookData, hookData: _hookData,
idField = 'id', idField = 'id',
nameField = 'name', nameField: _nameField = 'name',
contentField = 'content', contentField = 'content',
navigateTo = 'start/dashboard', navigateTo = 'start/dashboard',
mode = 'prompt' mode = 'prompt'

View file

@ -64,7 +64,7 @@ export function FormGeneratorControls({
filterFocused, filterFocused,
onFilterFocus, onFilterFocus,
selectedCount, selectedCount,
displayData, displayData: _displayData,
onDeleteSingle, onDeleteSingle,
onDeleteMultiple, onDeleteMultiple,
onRefresh, onRefresh,

View file

@ -453,7 +453,7 @@ export function FormGeneratorList<T extends Record<string, any>>({
}; };
// Render field input // Render field input
const renderFieldInput = (field: FieldConfig, value: any, row: T, index: number) => { const renderFieldInput = (field: FieldConfig, value: any, row: T, _index: number) => {
if (field.type === 'readonly' || !field.editable) { if (field.type === 'readonly' || !field.editable) {
return ( return (
<div className={styles.fieldValue} key={field.key}> <div className={styles.fieldValue} key={field.key}>
@ -491,12 +491,15 @@ export function FormGeneratorList<T extends Record<string, any>>({
} }
// Default to text input // Default to text input
const inputType = attributeTypeToInputType(field.type || 'string');
// TextField doesn't support 'textarea' type, use 'text' instead
const textFieldType = inputType === 'textarea' ? 'text' : inputType;
return ( return (
<TextField <TextField
key={field.key} key={field.key}
value={value || ''} value={value || ''}
onChange={(newValue) => onFieldChange?.(row, field.key, newValue)} onChange={(newValue) => onFieldChange?.(row, field.key, newValue)}
type={attributeTypeToInputType(field.type || 'string')} type={textFieldType as 'text' | 'email' | 'url' | 'password' | 'search' | 'tel' | 'number'}
required={field.required} required={field.required}
readonly={!field.editable} readonly={!field.editable}
className={styles.fieldInput} className={styles.fieldInput}

View file

@ -32,7 +32,6 @@ const SidebarItem: React.FC<SidebarItemProps> = React.memo(({
// Get the actual color from parent li element // Get the actual color from parent li element
const parentLi = wrapper.closest('li'); const parentLi = wrapper.closest('li');
const parentColor = parentLi ? window.getComputedStyle(parentLi).color : '#000000';
// Force color directly - use black for now to ensure visibility // Force color directly - use black for now to ensure visibility
const iconColor = '#000000'; // Force black for visibility const iconColor = '#000000'; // Force black for visibility
@ -218,7 +217,6 @@ const SidebarItem: React.FC<SidebarItemProps> = React.memo(({
> >
<Icon <Icon
className={`${styles.icon} ${styles.iconMinimized} ${isDisabled ? styles.disabledIcon : ''}`} className={`${styles.icon} ${styles.iconMinimized} ${isDisabled ? styles.disabledIcon : ''}`}
size={25}
style={{ style={{
width: '25px', width: '25px',
height: '25px', height: '25px',

View file

@ -53,7 +53,6 @@ const SidebarSubmenu: React.FC<SidebarSubmenuProps> = ({ item, isOpen, isMinimiz
{SubIcon && ( {SubIcon && (
<SubIcon <SubIcon
className={styles.submenuHorizontalIcon} className={styles.submenuHorizontalIcon}
size={16}
style={{ style={{
width: '16px', width: '16px',
height: '16px', height: '16px',

View file

@ -64,7 +64,7 @@ export function ConnectedFilesList({
deletingFiles = new Set(), deletingFiles = new Set(),
previewingFiles = new Set(), previewingFiles = new Set(),
removingFiles = new Set(), removingFiles = new Set(),
workflowId, workflowId: _workflowId,
emptyMessage = 'No files connected to this workflow' emptyMessage = 'No files connected to this workflow'
}: ConnectedFilesListProps) { }: ConnectedFilesListProps) {
// Combine workflow files and pending files, deduplicating by fileId // Combine workflow files and pending files, deduplicating by fileId
@ -98,7 +98,7 @@ export function ConnectedFilesList({
} }
return false; return false;
}, },
removeOptimistically: (fileId: string) => { removeOptimistically: (_fileId: string) => {
// This will be handled by the parent component's state // This will be handled by the parent component's state
}, },
refetch: async () => { refetch: async () => {
@ -121,7 +121,7 @@ export function ConnectedFilesList({
// View button (always shown) // View button (always shown)
buttons.push({ buttons.push({
type: 'view', type: 'view',
onAction: async (file: WorkflowFile) => { onAction: async (_file: WorkflowFile) => {
// View is handled by ViewActionButton's FilePreview component // View is handled by ViewActionButton's FilePreview component
return Promise.resolve(); return Promise.resolve();
}, },
@ -156,7 +156,7 @@ export function ConnectedFilesList({
return buttons; return buttons;
}, [actionButtons, onDelete, onRemove]); }, [actionButtons, onDelete, onRemove]);
const handleView = async (file: WorkflowFile) => { const handleView = async (_file: WorkflowFile) => {
// View is handled by ViewActionButton's FilePreview component // View is handled by ViewActionButton's FilePreview component
return Promise.resolve(); return Promise.resolve();
}; };
@ -187,10 +187,10 @@ export function ConnectedFilesList({
<div className={styles.fileList}> <div className={styles.fileList}>
{allFiles {allFiles
.filter(file => file.fileId && file.fileId.trim() !== '') // Ensure fileId exists .filter(file => file.fileId && file.fileId.trim() !== '') // Ensure fileId exists
.map((file, index) => { .map((file) => {
const isDeleting = deletingFiles.has(file.fileId!); // const isDeleting = deletingFiles.has(file.fileId!);
const isPreviewing = previewingFiles.has(file.fileId!); // const isPreviewing = previewingFiles.has(file.fileId!);
const isRemoving = removingFiles.has(file.fileId!); // const isRemoving = removingFiles.has(file.fileId!);
// Use fileId as key since we've filtered out files without it // Use fileId as key since we've filtered out files without it
const uniqueKey = file.fileId!; const uniqueKey = file.fileId!;

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { LogProps, WorkflowLog } from './LogTypes'; import { LogProps } from './LogTypes';
import { AutoScroll } from '../AutoScroll'; import { AutoScroll } from '../AutoScroll';
import { formatUnixTimestamp } from '../../../utils/time'; import { formatUnixTimestamp } from '../../../utils/time';
import styles from './Log.module.css'; import styles from './Log.module.css';

View file

@ -1,5 +1,3 @@
import type React from 'react';
/** /**
* Log entry from workflow * Log entry from workflow
*/ */

View file

@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
import L from 'leaflet'; import L from 'leaflet';
import 'leaflet/dist/leaflet.css'; import 'leaflet/dist/leaflet.css';
import { lv95ToWGS84, wgs84ToLV95 } from './LV95Converter'; import { lv95ToWGS84, wgs84ToLV95 } from './LV95Converter';
import type { MapPoint, ParcelGeometry, MapViewProps } from './MapView'; import type { MapViewProps } from './MapView';
import styles from './MapView.module.css'; import styles from './MapView.module.css';
// Fix for default marker icons in Leaflet // Fix for default marker icons in Leaflet
@ -32,7 +32,7 @@ const MapViewLeaflet: React.FC<MapViewProps> = ({
}) => { }) => {
const mapRef = useRef<L.Map | null>(null); const mapRef = useRef<L.Map | null>(null);
const mapContainerRef = useRef<HTMLDivElement>(null); const mapContainerRef = useRef<HTMLDivElement>(null);
const layersRef = useRef<L.LayerGroup[]>([]); const layersRef = useRef<L.Layer[]>([]);
const centerMarkerRef = useRef<L.Marker | null>(null); const centerMarkerRef = useRef<L.Marker | null>(null);
// Initialize map // Initialize map

View file

@ -23,7 +23,7 @@ export interface DocumentItemProps {
*/ */
export const DocumentItem: React.FC<DocumentItemProps> = ({ export const DocumentItem: React.FC<DocumentItemProps> = ({
document, document,
message, message: _message,
className, className,
onFileDelete, onFileDelete,
onFileRemove, onFileRemove,
@ -31,7 +31,7 @@ export const DocumentItem: React.FC<DocumentItemProps> = ({
deletingFiles = new Set(), deletingFiles = new Set(),
previewingFiles = new Set(), previewingFiles = new Set(),
removingFiles = new Set(), removingFiles = new Set(),
workflowId workflowId: _workflowId
}) => { }) => {
// Convert MessageDocument to WorkflowFile format for compatibility with action buttons // Convert MessageDocument to WorkflowFile format for compatibility with action buttons
const workflowFile: WorkflowFile = useMemo(() => ({ const workflowFile: WorkflowFile = useMemo(() => ({
@ -50,7 +50,7 @@ export const DocumentItem: React.FC<DocumentItemProps> = ({
// Create hookData object for action buttons // Create hookData object for action buttons
const hookData = useMemo(() => ({ const hookData = useMemo(() => ({
handleDelete: async (fileId: string) => { handleDelete: async (_fileId: string) => {
if (onFileDelete) { if (onFileDelete) {
await onFileDelete(workflowFile); await onFileDelete(workflowFile);
return true; return true;

View file

@ -9,6 +9,7 @@ interface TextFieldProps extends BaseTextFieldProps {
step?: string; step?: string;
min?: string | number; min?: string | number;
max?: string | number; max?: string | number;
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
} }
const TextField: React.FC<TextFieldProps> = ({ const TextField: React.FC<TextFieldProps> = ({

View file

@ -1,5 +1,3 @@
import type React from 'react';
/** /**
* Log entry from workflow * Log entry from workflow
*/ */

View file

@ -11,7 +11,9 @@ export * from './MapView';
export * from './ParcelInfoPanel'; export * from './ParcelInfoPanel';
export * from './CopyableTruncatedValue'; export * from './CopyableTruncatedValue';
export { Log } from './Log'; export { Log } from './Log';
export * from './Log'; export type { LogProps } from './Log/LogTypes';
export { LogMessage } from './Log/LogMessage';
export type { LogMessageProps } from './Log/LogMessage';
export { WorkflowStatus } from './WorkflowStatus'; export { WorkflowStatus } from './WorkflowStatus';
export * from './WorkflowStatus'; export type { WorkflowStatusProps } from './WorkflowStatus/WorkflowStatusTypes';
export * from './AutoScroll'; export * from './AutoScroll';

View file

@ -1,4 +1,4 @@
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; import React, { createContext, useContext, useCallback } from 'react';
import { useUserFiles, useFileOperations, UserFile } from '../hooks/useFiles'; import { useUserFiles, useFileOperations, UserFile } from '../hooks/useFiles';
interface FileContextType { interface FileContextType {
@ -16,7 +16,7 @@ interface FileContextType {
const FileContext = createContext<FileContextType | undefined>(undefined); const FileContext = createContext<FileContextType | undefined>(undefined);
export function FileProvider({ children }: { children: React.ReactNode }) { export function FileProvider({ children }: { children: React.ReactNode }) {
const { data: files, loading, error, refetch: refetchFiles, removeFileOptimistically, addFileOptimistically } = useUserFiles(); const { data: files, loading, error, refetch: refetchFiles, removeFileOptimistically } = useUserFiles();
const { const {
handleFileUpload: hookHandleFileUpload, handleFileUpload: hookHandleFileUpload,
handleFileDelete: hookHandleFileDelete, handleFileDelete: hookHandleFileDelete,
@ -40,25 +40,13 @@ export function FileProvider({ children }: { children: React.ReactNode }) {
return result; return result;
} }
// Add file optimistically to the shared state // File will be added via refetch
const newFile: UserFile = {
id: fileData.id,
file_name: fileData.fileName || file.name,
mime_type: fileData.mimeType || file.type || 'application/octet-stream',
action: 'Document', // Will be determined by mime type in useUserFiles
created_at: fileData.creationDate ? new Date(fileData.creationDate * 1000).toISOString() : new Date().toISOString(),
size: fileData.fileSize || file.size,
source: 'user_uploaded'
};
addFileOptimistically(newFile);
// Refetch to ensure we have the latest data (this will update all consumers) // Refetch to ensure we have the latest data (this will update all consumers)
await refetchFiles(); await refetchFiles();
} }
return result; return result;
}, [hookHandleFileUpload, addFileOptimistically, refetchFiles]); }, [hookHandleFileUpload, refetchFiles]);
// Centralized file delete that updates the shared state // Centralized file delete that updates the shared state
const handleFileDelete = useCallback(async (fileId: string, onOptimisticDelete?: () => void) => { const handleFileDelete = useCallback(async (fileId: string, onOptimisticDelete?: () => void) => {

View file

@ -1,4 +1,4 @@
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react'; import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
interface WorkflowSelectionContextType { interface WorkflowSelectionContextType {
selectedWorkflowId: string | null; selectedWorkflowId: string | null;

View file

@ -71,7 +71,7 @@ const PageManager: React.FC<PageManagerProps> = ({
) : ( ) : (
<PageRenderer <PageRenderer
pageData={pageData} pageData={pageData}
onButtonClick={(buttonId, button) => { onButtonClick={(_buttonId, _button) => {
}} }}
/> />
)} )}
@ -88,7 +88,8 @@ const PageManager: React.FC<PageManagerProps> = ({
} else { } else {
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
const instance = newInstances.get(currentPath); const _instance = newInstances.get(currentPath);
void _instance; // Intentionally unused, for debugging purposes
} }
} }

View file

@ -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 type { WorkflowFile } from '../../hooks/playground/useDashboardInputForm';
import styles from '../../styles/pages.module.css'; import styles from '../../styles/pages.module.css';
interface PageRendererProps { interface PageRendererProps {
@ -362,7 +363,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
onSave?: (sectionId: string, data: any) => Promise<void>; onSave?: (sectionId: string, data: any) => Promise<void>;
getNestedValue: (obj: any, path: string) => any; getNestedValue: (obj: any, path: string) => any;
setNestedValue: (obj: any, path: string, value: any) => any; setNestedValue: (obj: any, path: string, value: any) => any;
}> = ({ sections, formData, fieldsBySection, loadingBySection, errorsBySection, onSave, getNestedValue, setNestedValue }) => { }> = ({ sections, formData, fieldsBySection, loadingBySection, errorsBySection, onSave, getNestedValue, setNestedValue: _setNestedValue }) => {
const [sectionFormData, setSectionFormData] = useState<Record<string, any>>({}); const [sectionFormData, setSectionFormData] = useState<Record<string, any>>({});
const [sectionSaveLoading, setSectionSaveLoading] = useState<Record<string, boolean>>({}); const [sectionSaveLoading, setSectionSaveLoading] = useState<Record<string, boolean>>({});
const [sectionSaveMessages, setSectionSaveMessages] = useState<Record<string, { type: 'success' | 'error', text: string } | null>>({}); const [sectionSaveMessages, setSectionSaveMessages] = useState<Record<string, { type: 'success' | 'error', text: string } | null>>({});
@ -677,6 +678,46 @@ const PageRenderer: React.FC<PageRendererProps> = ({
// Render content based on type // Render content based on type
const renderContent = (content: PageContent) => { const renderContent = (content: PageContent) => {
// Wrapper functions to convert fileId-based handlers to WorkflowFile-based handlers
// These are defined at the top level of renderContent so they're accessible in all content cases
const wrapFileDelete: ((file: WorkflowFile) => Promise<void>) | undefined = hookData?.handleFileDelete ? async (file: WorkflowFile) => {
if (!hookData?.handleFileDelete || !file) return;
const handler = hookData.handleFileDelete as any;
// Check if handler expects fileId (string) or file (WorkflowFile)
if (file?.fileId && typeof file.fileId === 'string') {
// Try fileId signature first (handler might be (fileId: string, ...) => Promise<boolean>)
try {
const result = handler(file.fileId);
if (result instanceof Promise) await result;
return;
} catch {
// Fall through to file signature
}
}
// Try file signature (handler might be (file: WorkflowFile) => Promise<void>)
const result = handler(file);
if (result instanceof Promise) await result;
} : undefined;
const wrapFileRemove: ((file: WorkflowFile) => Promise<void>) | undefined = hookData?.handleFileRemove ? async (file: WorkflowFile) => {
if (!hookData?.handleFileRemove || !file) return;
const handler = hookData.handleFileRemove as any;
// Check if handler expects fileId (string) or file (WorkflowFile)
if (file?.fileId && typeof file.fileId === 'string') {
// Try fileId signature first (handler might be (fileId: string) => void | Promise<void>)
try {
const result = handler(file.fileId);
if (result instanceof Promise) await result;
return;
} catch {
// Fall through to file signature
}
}
// Try file signature (handler might be (file: WorkflowFile) => Promise<void>)
const result = handler(file);
if (result instanceof Promise) await result;
} : undefined;
switch (content.type) { switch (content.type) {
case 'heading': case 'heading':
const HeadingTag = `h${content.level || 2}` as keyof React.JSX.IntrinsicElements; const HeadingTag = `h${content.level || 2}` as keyof React.JSX.IntrinsicElements;
@ -834,7 +875,14 @@ const PageRenderer: React.FC<PageRendererProps> = ({
} }
} else { } else {
// Non-function disabled value // Non-function disabled value
disabledFn = () => action.disabled as boolean | { disabled: boolean; message?: string }; const disabledValue = action.disabled;
if (typeof disabledValue === 'boolean') {
disabledFn = () => disabledValue;
} else if (disabledValue && typeof disabledValue === 'object' && 'disabled' in disabledValue) {
disabledFn = () => disabledValue as { disabled: boolean; message?: string };
} else {
disabledFn = () => false;
}
} }
} else { } else {
disabledFn = () => false; disabledFn = () => false;
@ -949,7 +997,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
<DropdownSelect <DropdownSelect
items={hookData.promptItems || []} items={hookData.promptItems || []}
selectedItemId={hookData.selectedPromptId || null} selectedItemId={hookData.selectedPromptId || null}
onSelect={hookData.onPromptSelect} onSelect={hookData.onPromptSelect || (() => {})}
placeholder={t('dashboard.prompt.select', 'Select a prompt')} placeholder={t('dashboard.prompt.select', 'Select a prompt')}
emptyMessage={t('dashboard.prompt.empty', 'No prompts available')} emptyMessage={t('dashboard.prompt.empty', 'No prompts available')}
headerText={t('dashboard.prompt.header', 'Select Prompt')} headerText={t('dashboard.prompt.header', 'Select Prompt')}
@ -966,7 +1014,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
<DropdownSelect <DropdownSelect
items={hookData.workflowModeItems || []} items={hookData.workflowModeItems || []}
selectedItemId={hookData.selectedWorkflowMode || null} selectedItemId={hookData.selectedWorkflowMode || null}
onSelect={hookData.onWorkflowModeSelect} onSelect={hookData.onWorkflowModeSelect || (() => {})}
placeholder={t('dashboard.workflow.mode.select', 'Select workflow mode')} placeholder={t('dashboard.workflow.mode.select', 'Select workflow mode')}
emptyMessage={t('dashboard.workflow.mode.empty', 'No modes available')} emptyMessage={t('dashboard.workflow.mode.empty', 'No modes available')}
headerText={t('dashboard.workflow.mode.header', 'Workflow Mode')} headerText={t('dashboard.workflow.mode.header', 'Workflow Mode')}
@ -1033,7 +1081,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
}, },
{ {
type: 'remove', type: 'remove',
onAction: hookData.handleFileRemove, onAction: wrapFileRemove,
showOnlyForPending: true, showOnlyForPending: true,
idField: 'fileId', idField: 'fileId',
loadingStateName: 'removingItems' loadingStateName: 'removingItems'
@ -1045,9 +1093,12 @@ const PageRenderer: React.FC<PageRendererProps> = ({
idField: 'fileId' idField: 'fileId'
} }
]} ]}
onDelete={hookData.handleFileDelete} onDelete={wrapFileDelete}
onRemove={hookData.handleFileRemove} onRemove={wrapFileRemove}
onAttach={hookData.handleFileAttach} // Allow attaching files for next message onAttach={hookData.handleFileAttach ? async (fileId: string) => {
const result = hookData.handleFileAttach!(fileId);
if (result instanceof Promise) await result;
} : undefined}
deletingFiles={hookData.deletingFiles || new Set()} deletingFiles={hookData.deletingFiles || new Set()}
previewingFiles={hookData.previewingFiles || new Set()} previewingFiles={hookData.previewingFiles || new Set()}
removingFiles={new Set()} // Can be tracked if needed removingFiles={new Set()} // Can be tracked if needed
@ -1080,7 +1131,13 @@ const PageRenderer: React.FC<PageRendererProps> = ({
justifyContent: 'flex-end' justifyContent: 'flex-end'
}}> }}>
<UploadButton <UploadButton
onUpload={hookData.handleFileUploadAndAttach || hookData.handleFileUpload} onUpload={hookData.handleFileUploadAndAttach || hookData.handleFileUpload ? async (file: File) => {
const handler = hookData.handleFileUploadAndAttach || hookData.handleFileUpload;
if (handler) {
// Handler returns Promise<{ success, data }>, but UploadButton expects Promise<void>
await handler(file);
}
} : async () => {}}
disabled={hookData.isSubmitting || false} disabled={hookData.isSubmitting || false}
loading={hookData.uploadingFile || false} loading={hookData.uploadingFile || false}
variant="primary" variant="primary"
@ -1207,7 +1264,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
<DropdownSelect <DropdownSelect
items={hookData.promptItems || []} items={hookData.promptItems || []}
selectedItemId={hookData.selectedPromptId || null} selectedItemId={hookData.selectedPromptId || null}
onSelect={hookData.onPromptSelect} onSelect={hookData.onPromptSelect || (() => {})}
placeholder={t('dashboard.prompt.select', 'Select a prompt')} placeholder={t('dashboard.prompt.select', 'Select a prompt')}
emptyMessage={t('dashboard.prompt.empty', 'No prompts available')} emptyMessage={t('dashboard.prompt.empty', 'No prompts available')}
headerText={t('dashboard.prompt.header', 'Select Prompt')} headerText={t('dashboard.prompt.header', 'Select Prompt')}
@ -1222,7 +1279,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
<DropdownSelect <DropdownSelect
items={hookData.workflowModeItems || []} items={hookData.workflowModeItems || []}
selectedItemId={hookData.selectedWorkflowMode || null} selectedItemId={hookData.selectedWorkflowMode || null}
onSelect={hookData.onWorkflowModeSelect} onSelect={hookData.onWorkflowModeSelect || (() => {})}
placeholder={t('dashboard.workflow.mode.select', 'Select workflow mode')} placeholder={t('dashboard.workflow.mode.select', 'Select workflow mode')}
emptyMessage={t('dashboard.workflow.mode.empty', 'No modes available')} emptyMessage={t('dashboard.workflow.mode.empty', 'No modes available')}
headerText={t('dashboard.workflow.mode.header', 'Workflow Mode')} headerText={t('dashboard.workflow.mode.header', 'Workflow Mode')}
@ -1320,8 +1377,8 @@ const PageRenderer: React.FC<PageRendererProps> = ({
showDocuments={config.showDocuments !== false} showDocuments={config.showDocuments !== false}
showMetadata={config.showMetadata !== false} showMetadata={config.showMetadata !== false}
showProgress={config.showProgress !== false} showProgress={config.showProgress !== false}
onFileDelete={hookData?.handleFileDelete} onFileDelete={wrapFileDelete}
onFileRemove={hookData?.handleFileRemove} onFileRemove={wrapFileRemove}
deletingFiles={hookData?.deletingFiles} deletingFiles={hookData?.deletingFiles}
previewingFiles={hookData?.previewingFiles} previewingFiles={hookData?.previewingFiles}
removingFiles={hookData?.removingFiles} removingFiles={hookData?.removingFiles}
@ -1334,8 +1391,8 @@ const PageRenderer: React.FC<PageRendererProps> = ({
key={message.id || index} key={message.id || index}
message={cleanMessage} message={cleanMessage}
showDocuments={config.showDocuments !== false} showDocuments={config.showDocuments !== false}
onFileDelete={hookData?.handleFileDelete} onFileDelete={wrapFileDelete}
onFileRemove={hookData?.handleFileRemove} onFileRemove={wrapFileRemove}
deletingFiles={hookData?.deletingFiles} deletingFiles={hookData?.deletingFiles}
previewingFiles={hookData?.previewingFiles} previewingFiles={hookData?.previewingFiles}
removingFiles={hookData?.removingFiles} removingFiles={hookData?.removingFiles}
@ -1356,8 +1413,8 @@ const PageRenderer: React.FC<PageRendererProps> = ({
showMetadata={config.showMetadata !== false} showMetadata={config.showMetadata !== false}
showProgress={config.showProgress !== false} showProgress={config.showProgress !== false}
emptyMessage={config.emptyMessage ? resolveLanguageText(config.emptyMessage, t) : undefined} emptyMessage={config.emptyMessage ? resolveLanguageText(config.emptyMessage, t) : undefined}
onFileDelete={hookData?.handleFileDelete} onFileDelete={wrapFileDelete}
onFileRemove={hookData?.handleFileRemove} onFileRemove={wrapFileRemove}
deletingFiles={hookData?.deletingFiles} deletingFiles={hookData?.deletingFiles}
previewingFiles={hookData?.previewingFiles} previewingFiles={hookData?.previewingFiles}
removingFiles={hookData?.removingFiles} removingFiles={hookData?.removingFiles}
@ -1662,6 +1719,14 @@ const PageRenderer: React.FC<PageRendererProps> = ({
return await createOperation(formData); return await createOperation(formData);
}; };
// Evaluate disabled property if it's a function
const isDisabled = typeof button.disabled === 'function'
? button.disabled(hookData)
: button.disabled ?? false;
const disabledValue = typeof isDisabled === 'object' && isDisabled !== null && 'disabled' in isDisabled
? isDisabled.disabled
: Boolean(isDisabled);
return ( return (
<CreateButton <CreateButton
key={button.id} key={button.id}
@ -1672,7 +1737,7 @@ const PageRenderer: React.FC<PageRendererProps> = ({
variant={button.variant || 'primary'} variant={button.variant || 'primary'}
size={button.size || 'md'} size={button.size || 'md'}
icon={button.icon} icon={button.icon}
disabled={button.disabled} disabled={disabledValue}
onSuccess={() => { onSuccess={() => {
// Refetch data after successful creation // Refetch data after successful creation
if (hookData.refetch) { if (hookData.refetch) {

View file

@ -121,7 +121,7 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
} }
// Process parent groups // Process parent groups
for (const [parentPath, parentGroup] of parentGroups.entries()) { for (const [_parentPath, parentGroup] of parentGroups.entries()) {
// Filter subpages by RBAC access // Filter subpages by RBAC access
const accessibleSubpages = []; const accessibleSubpages = [];
for (const subpage of parentGroup.subpages) { for (const subpage of parentGroup.subpages) {

View file

@ -34,6 +34,7 @@ export const dashboardPageData: GenericPageData = {
placeholder: 'dashboard.workflow.select', placeholder: 'dashboard.workflow.select',
emptyMessage: 'dashboard.workflow.empty', emptyMessage: 'dashboard.workflow.empty',
headerText: 'dashboard.workflow.header', headerText: 'dashboard.workflow.header',
onSelect: () => {}, // Placeholder - actual handler comes from dataSource.onSelectMethod
dataSource: { dataSource: {
itemsProperty: 'workflowItems', itemsProperty: 'workflowItems',
selectedIdProperty: 'selectedWorkflowId', selectedIdProperty: 'selectedWorkflowId',

View file

@ -1,5 +1,5 @@
import { GenericPageData } from '../../pageInterface'; import { GenericPageData } from '../../pageInterface';
import { FaTable, FaBuilding } from 'react-icons/fa'; import { FaTable } from 'react-icons/fa';
import { IoMdSend } from 'react-icons/io'; import { IoMdSend } from 'react-icons/io';
import { usePekTablesContext } from '../../../../contexts/PekTablesContext'; import { usePekTablesContext } from '../../../../contexts/PekTablesContext';
import PekTablesDropdown from './pek-tables/PekTablesDropdown'; import PekTablesDropdown from './pek-tables/PekTablesDropdown';

View file

@ -7,16 +7,16 @@ import styles from './PekLocationInput.module.css';
const PekLocationInput: React.FC = () => { const PekLocationInput: React.FC = () => {
const { const {
kanton, kanton: _kanton,
setKanton, setKanton: _setKanton,
gemeinde, gemeinde: _gemeinde,
setGemeinde, setGemeinde: _setGemeinde,
adresse, adresse,
setAdresse, setAdresse,
buildLocationString, buildLocationString,
useCurrentLocation, useCurrentLocation,
isGettingLocation, isGettingLocation,
locationError, locationError: _locationError,
searchParcel, searchParcel,
isSearchingParcel isSearchingParcel
} = usePekContext(); } = usePekContext();

View file

@ -9,13 +9,14 @@ export type PrivilegeChecker = () => boolean | Promise<boolean>;
export interface ButtonFormField { export interface ButtonFormField {
key: string; key: string;
label: string | LanguageText; label: string | LanguageText;
type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly'; type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly' | 'multiselect';
required?: boolean; required?: boolean;
placeholder?: string | LanguageText; placeholder?: string | LanguageText;
minRows?: number; minRows?: number;
maxRows?: number; maxRows?: number;
validator?: (value: any) => string | null; validator?: (value: any) => string | null;
defaultValue?: any; defaultValue?: any;
options?: string[] | Array<{ value: string | number; label: string }>; // For enum/multiselect fields
} }
// Dropdown configuration for header dropdown buttons // Dropdown configuration for header dropdown buttons
@ -37,6 +38,7 @@ export interface DropdownConfig<T = any> {
itemsProperty?: string; // Property name in hookData that contains items array itemsProperty?: string; // Property name in hookData that contains items array
selectedIdProperty?: string; // Property name in hookData that contains selectedItemId selectedIdProperty?: string; // Property name in hookData that contains selectedItemId
onSelectMethod?: string; // Method name in hookData for onSelect callback onSelectMethod?: string; // Method name in hookData for onSelect callback
loadingProperty?: string; // Property name in hookData that contains loading state
}; };
} }
@ -159,9 +161,24 @@ export interface GenericDataHook {
columns?: any[]; // Optional columns configuration columns?: any[]; // Optional columns configuration
// File operations // File operations
handleUpload?: (file: File) => Promise<{ success: boolean; data: any }>; // For file upload functionality handleUpload?: (file: File) => Promise<{ success: boolean; data: any }>; // For file upload functionality
handleFileUpload?: (file: File) => Promise<{ success: boolean; data: any }>; // Alias for handleUpload
handleDownload?: (fileId: string, fileName: string) => Promise<boolean>; // For file download functionality handleDownload?: (fileId: string, fileName: string) => Promise<boolean>; // For file download functionality
handleDelete?: (fileId: string, onOptimisticDelete?: () => void) => Promise<boolean>; // For file delete functionality handleDelete?: (fileId: string, onOptimisticDelete?: () => void) => Promise<boolean>; // For file delete functionality
handleFileDelete?: ((fileId: string, onOptimisticDelete?: () => void) => Promise<boolean>) | ((file: any) => Promise<void>); // Can accept fileId or WorkflowFile
handlePreview?: (fileId: string, fileName: string, mimeType?: string) => Promise<any>; // For file preview functionality handlePreview?: (fileId: string, fileName: string, mimeType?: string) => Promise<any>; // For file preview functionality
// File management properties
workflowFiles?: any[]; // Files connected to workflow
pendingFiles?: any[]; // Files pending attachment
allUserFiles?: any[]; // All user files
handleFileRemove?: ((fileId: string) => Promise<void> | void) | ((file: any) => Promise<void> | void); // Can accept fileId or WorkflowFile
handleFileAttach?: (fileId: string) => Promise<void>; // Attach file to workflow (always returns Promise)
handleFileUploadAndAttach?: (file: File) => Promise<{ success: boolean; data: any }>; // Upload and attach file
uploadingFile?: boolean; // Loading state for file upload
deletingFiles?: Set<string>; // Set of file IDs being deleted
previewingFiles?: Set<string>; // Set of file IDs being previewed
removingFiles?: Set<string>; // Set of file IDs being removed
isFileAttachmentPopupOpen?: boolean; // Whether file attachment popup is open
setIsFileAttachmentPopupOpen?: (open: boolean) => void; // Set file attachment popup state
// FormGenerator specific handlers // FormGenerator specific handlers
onDelete?: (row: any) => Promise<void>; // For single item deletion onDelete?: (row: any) => Promise<void>; // For single item deletion
onDeleteMultiple?: (rows: any[]) => Promise<void>; // For multiple item deletion onDeleteMultiple?: (rows: any[]) => Promise<void>; // For multiple item deletion
@ -170,14 +187,37 @@ export interface GenericDataHook {
onInputChange?: (value: string) => void; onInputChange?: (value: string) => void;
handleSubmit?: () => Promise<void>; // No parameters, uses internal inputValue handleSubmit?: () => Promise<void>; // No parameters, uses internal inputValue
isSubmitting?: boolean; isSubmitting?: boolean;
// Prompt selector properties
promptPermission?: {
view?: boolean;
read?: string;
};
promptItems?: Array<{ id: string | number; label: string; value: any }>;
selectedPromptId?: string | number | null;
onPromptSelect?: (item: { id: string | number; label: string; value: any } | null) => void | Promise<void>;
promptsLoading?: boolean;
// Workflow mode selector properties
workflowModeItems?: Array<{ id: string | number; label: string; value: any }>;
selectedWorkflowMode?: string | number | null;
onWorkflowModeSelect?: (item: { id: string | number; label: string; value: any } | null) => void | Promise<void>;
// Workflow lifecycle state // Workflow lifecycle state
workflowId?: string; workflowId?: string;
workflowStatus?: string; workflowStatus?: string;
workflowData?: {
currentRound?: number;
[key: string]: any;
};
isRunning?: boolean; isRunning?: boolean;
currentRound?: number; // Current workflow round
latestStats?: any; // Latest workflow statistics
// Messages from workflow // Messages from workflow
messages?: any[]; messages?: any[];
// Logs from workflow // Logs from workflow
logs?: any[]; logs?: any[];
// Dashboard log tree
dashboardTree?: any; // Dashboard log tree structure
onToggleOperationExpanded?: (operationId: string) => void;
getChildOperations?: (parentId: string | null) => string[];
// Message overlay component // Message overlay component
MessageOverlayComponent?: () => React.ReactElement; MessageOverlayComponent?: () => React.ReactElement;
// Settings-specific properties // Settings-specific properties
@ -186,6 +226,8 @@ export interface GenericDataHook {
settingsLoading?: Record<string, boolean>; // Loading state per section settingsLoading?: Record<string, boolean>; // Loading state per section
settingsErrors?: Record<string, string | null>; // Error state per section settingsErrors?: Record<string, string | null>; // Error state per section
saveSection?: (sectionId: string, data: any) => Promise<void>; // Save handler for a section saveSection?: (sectionId: string, data: any) => Promise<void>; // Save handler for a section
// Dropdown data source loading property
[key: string]: any; // Allow additional properties for dynamic data sources
} }
// Action button configuration // Action button configuration
@ -306,8 +348,8 @@ export interface PageDataFile {
export interface SidebarItem { export interface SidebarItem {
id: string; id: string;
name: string; name: string;
link: string; link: string | undefined; // Allow undefined for parent groups that aren't clickable pages
icon?: IconType; icon?: IconType | React.ComponentType<React.SVGProps<SVGSVGElement>>; // Allow both IconType and SVG components
moduleEnabled: boolean; moduleEnabled: boolean;
order: number; order: number;
submenu?: SidebarSubmenuItemData[]; submenu?: SidebarSubmenuItemData[];

View file

@ -11,6 +11,7 @@ import { useWorkflowLifecycle } from './useWorkflowLifecycle';
import { useWorkflows } from './useWorkflows'; import { useWorkflows } from './useWorkflows';
import { useDashboardLogTree } from './useDashboardLogTree'; import { useDashboardLogTree } from './useDashboardLogTree';
import { extractFileIdsFromMessage, convertFilesToDocuments, sortMessages } from './playgroundUtils'; import { extractFileIdsFromMessage, convertFilesToDocuments, sortMessages } from './playgroundUtils';
import type { WorkflowLog as LogTypesWorkflowLog } from '../../components/UiComponents/Log/LogTypes';
export interface WorkflowFile { export interface WorkflowFile {
id: string; id: string;
@ -45,7 +46,6 @@ export function useDashboardInputForm() {
isStopping, isStopping,
startingWorkflow, startingWorkflow,
messages, messages,
logs,
dashboardLogs, dashboardLogs,
unifiedContentLogs, unifiedContentLogs,
latestStats, latestStats,
@ -190,7 +190,20 @@ export function useDashboardInputForm() {
// Only process if there are new logs // Only process if there are new logs
if (newLogs.length > 0) { if (newLogs.length > 0) {
processDashboardLogs(newLogs); // Convert API WorkflowLog format to LogTypes WorkflowLog format
const convertedLogs: LogTypesWorkflowLog[] = newLogs.map(log => ({
id: log.id || `${log.operationId || 'unknown'}-${log.timestamp || Date.now()}`,
workflowId: log.workflowId || '',
message: log.message || '',
type: log.type,
timestamp: log.timestamp || Date.now(),
status: log.status,
progress: log.progress,
performance: log.performance,
parentId: log.parentId,
operationId: log.operationId
}));
processDashboardLogs(convertedLogs);
} }
lastDashboardLogsLengthRef.current = dashboardLogs.length; lastDashboardLogsLengthRef.current = dashboardLogs.length;
@ -352,7 +365,7 @@ export function useDashboardInputForm() {
return allMessages.sort(sortMessages); return allMessages.sort(sortMessages);
}, [messages, optimisticMessage, workflowId]); }, [messages, optimisticMessage, workflowId]);
const handleFileUpload = useCallback(async (file: File) => { const handleFileUpload = useCallback(async (file: File): Promise<{ success: boolean; data: any }> => {
const result = await fileContext.handleFileUpload(file, workflowId || undefined); const result = await fileContext.handleFileUpload(file, workflowId || undefined);
if (result.success && result.fileData) { if (result.success && result.fileData) {
@ -360,8 +373,7 @@ export function useDashboardInputForm() {
const fileData = responseData.file || responseData; const fileData = responseData.file || responseData;
const fileId = fileData?.id; const fileId = fileData?.id;
if (!fileId) return; if (fileId) {
const newFile: WorkflowFile = { const newFile: WorkflowFile = {
id: fileId, id: fileId,
fileId: fileId, fileId: fileId,
@ -378,9 +390,15 @@ export function useDashboardInputForm() {
return [...prev, newFile]; return [...prev, newFile];
}); });
} }
}
return {
success: result.success || false,
data: result.fileData || null
};
}, [workflowId, fileContext]); }, [workflowId, fileContext]);
const handleFileAttach = useCallback(async (fileId: string) => { const handleFileAttach = useCallback(async (fileId: string): Promise<void> => {
const isInPending = pendingFiles.some(f => f.fileId === fileId); const isInPending = pendingFiles.some(f => f.fileId === fileId);
if (isInPending) { if (isInPending) {
@ -424,8 +442,8 @@ export function useDashboardInputForm() {
} }
}, [pendingFiles, fileContext.files, workflowFiles]); }, [pendingFiles, fileContext.files, workflowFiles]);
const handleFileUploadAndAttach = useCallback(async (file: File) => { const handleFileUploadAndAttach = useCallback(async (file: File): Promise<{ success: boolean; data: any }> => {
await handleFileUpload(file); return await handleFileUpload(file);
}, [handleFileUpload]); }, [handleFileUpload]);
const handleFileRemove = useCallback(async (file: WorkflowFile) => { const handleFileRemove = useCallback(async (file: WorkflowFile) => {

View file

@ -56,7 +56,6 @@ export function useDashboardLogTree() {
// Get or create operation // Get or create operation
const existingOperation = newTree.operations.get(operationId); const existingOperation = newTree.operations.get(operationId);
const isNewOperation = !existingOperation;
// Create new logs Map (copy existing logs if updating) // Create new logs Map (copy existing logs if updating)
const logsMap = existingOperation const logsMap = existingOperation

View file

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useState, useEffect, useCallback, useRef } from 'react';
import { useApiRequest } from '../useApi'; import { useApiRequest } from '../useApi';
import { import {
type Workflow, type Workflow,
@ -334,7 +334,6 @@ export function useWorkflowLifecycle() {
// Determine if polling should continue // Determine if polling should continue
const currentStatus = statusRef.current; const currentStatus = statusRef.current;
const changedAt = statusChangedFromRunningAtRef.current;
// Stop polling immediately for failed or stopped workflows // Stop polling immediately for failed or stopped workflows
// For completed workflows, allow grace period (handled by useEffect) // For completed workflows, allow grace period (handled by useEffect)
@ -357,7 +356,7 @@ export function useWorkflowLifecycle() {
}, [request, updateWorkflowStatus, processUnifiedChatData]); }, [request, updateWorkflowStatus, processUnifiedChatData]);
// Load initial workflow data (non-polling) // Load initial workflow data (non-polling)
const loadWorkflowData = useCallback(async (id: string) => { const _loadWorkflowData = useCallback(async (id: string) => {
try { try {
const workflowData = await fetchWorkflowApi(request, id).catch(() => null); const workflowData = await fetchWorkflowApi(request, id).catch(() => null);
@ -416,6 +415,7 @@ export function useWorkflowLifecycle() {
console.error('Error loading workflow data:', error); console.error('Error loading workflow data:', error);
} }
}, [request, updateWorkflowStatus, convertLogToFrontendFormat, processUnifiedChatData]); }, [request, updateWorkflowStatus, convertLogToFrontendFormat, processUnifiedChatData]);
void _loadWorkflowData; // Intentionally unused, reserved for future use
// Set up polling when workflow is running // Set up polling when workflow is running
useEffect(() => { useEffect(() => {

View file

@ -4,7 +4,7 @@ import { useMsal } from '@azure/msal-react';
import api from '../api'; import api from '../api';
import { useApiRequest } from './useApi'; import { useApiRequest } from './useApi';
import { getApiBaseUrl } from '../../config/config'; import { getApiBaseUrl } from '../../config/config';
import { setUserDataCache, clearUserDataCache } from '../utils/userCache'; import { setUserDataCache, clearUserDataCache, type CachedUserData } from '../utils/userCache';
import { import {
loginApi, loginApi,
fetchCurrentUserApi, fetchCurrentUserApi,
@ -44,7 +44,7 @@ export function useAuth() {
if (userData) { if (userData) {
// Cache user data in sessionStorage (cleared on tab close - more secure than localStorage) // Cache user data in sessionStorage (cleared on tab close - more secure than localStorage)
setUserDataCache(userData); setUserDataCache(userData as CachedUserData);
} }
} catch (userError) { } catch (userError) {
console.error('Failed to fetch user data after login:', userError); console.error('Failed to fetch user data after login:', userError);
@ -171,7 +171,7 @@ export function useMsalAuth() {
try { try {
const userData = await fetchCurrentUserApi('msft'); const userData = await fetchCurrentUserApi('msft');
if (userData) { if (userData) {
setUserDataCache(userData); setUserDataCache(userData as CachedUserData);
} }
} catch (userError) { } catch (userError) {
console.error('Failed to fetch user data after Microsoft login:', userError); console.error('Failed to fetch user data after Microsoft login:', userError);
@ -349,7 +349,7 @@ export function useGoogleAuth() {
try { try {
const userData = await fetchCurrentUserApi('google'); const userData = await fetchCurrentUserApi('google');
if (userData) { if (userData) {
setUserDataCache(userData); setUserDataCache(userData as CachedUserData);
} }
} catch (userError) { } catch (userError) {
console.error('Failed to fetch user data after Google login:', userError); console.error('Failed to fetch user data after Google login:', userError);
@ -652,7 +652,7 @@ export function useCurrentUser() {
setUser(userData); setUser(userData);
// Cache user data in sessionStorage (cleared on tab close - more secure than localStorage) // Cache user data in sessionStorage (cleared on tab close - more secure than localStorage)
setUserDataCache(userData); setUserDataCache(userData as CachedUserData);
return userData; return userData;
} catch (error: any) { } catch (error: any) {

View file

@ -7,14 +7,12 @@ import { getUserDataCache } from '../utils/userCache';
import { useApiRequest } from './useApi'; import { useApiRequest } from './useApi';
import { usePermissions, type UserPermissions } from './usePermissions'; import { usePermissions, type UserPermissions } from './usePermissions';
import { import {
fetchFileAttributes, fetchFileAttributes as _fetchFileAttributes,
fetchFiles as fetchFilesApi, fetchFiles as fetchFilesApi,
fetchFileById as fetchFileByIdApi, fetchFileById as fetchFileByIdApi,
updateFile as updateFileApi, updateFile as updateFileApi,
deleteFile as deleteFileApi, deleteFile as deleteFileApi,
deleteFiles as deleteFilesApi, deleteFiles as deleteFilesApi
type AttributeDefinition,
type PaginationParams
} from '../api/fileApi'; } from '../api/fileApi';
// File interfaces - exactly matching backend FileItem model // File interfaces - exactly matching backend FileItem model
@ -32,7 +30,7 @@ export interface FileInfo {
// Field names come directly from backend attributes // Field names come directly from backend attributes
export type UserFile = any; export type UserFile = any;
// Attribute definition interface // Attribute definition interface (local definition, not imported to avoid conflicts)
export interface AttributeDefinition { export interface AttributeDefinition {
name: string; name: string;
label: string; label: string;
@ -46,7 +44,7 @@ export interface AttributeDefinition {
filterOptions?: string[]; // For enum types filterOptions?: string[]; // For enum types
} }
// Pagination parameters // Pagination parameters (local definition, not imported to avoid conflicts)
export interface PaginationParams { export interface PaginationParams {
page?: number; page?: number;
pageSize?: number; pageSize?: number;
@ -129,8 +127,7 @@ export function useUserFiles() {
if (!cachedUser) { if (!cachedUser) {
// User is not authenticated, skip fetching files // User is not authenticated, skip fetching files
setFiles([]); setFiles([]);
setLoading(false); // Note: loading and error are managed by useApiRequest hook
setError(null);
return; return;
} }

View file

@ -456,10 +456,10 @@ export function usePek() {
const firstCoord = clickedParcel.coordinates[0]; const firstCoord = clickedParcel.coordinates[0];
// Calculate centroid as fallback, but prefer a point we know is inside // Calculate centroid as fallback, but prefer a point we know is inside
const sumX = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.x, 0); // const sumX = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.x, 0);
const sumY = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.y, 0); // const sumY = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.y, 0);
const centroidX = sumX / clickedParcel.coordinates.length; // const _centroidX = sumX / clickedParcel.coordinates.length;
const centroidY = sumY / clickedParcel.coordinates.length; // const _centroidY = sumY / clickedParcel.coordinates.length;
// Use first coordinate (guaranteed to be on/in the parcel) for search // Use first coordinate (guaranteed to be on/in the parcel) for search
const locationString = `${firstCoord.x},${firstCoord.y}`; const locationString = `${firstCoord.x},${firstCoord.y}`;
@ -658,7 +658,7 @@ export function usePek() {
// Try to update the parcel via PUT request // Try to update the parcel via PUT request
try { try {
const updateResponse = await api.put( await api.put(
`/api/realestate/parzelle/${parzelleResult.id}`, `/api/realestate/parzelle/${parzelleResult.id}`,
updateParcelRequestBody updateParcelRequestBody
); );

View file

@ -223,13 +223,8 @@ export function usePrompts() {
fieldType = 'string'; fieldType = 'string';
} }
} }
// Legacy support for old format // Note: Legacy 'boolean' and 'enum' types are not in the AttributeDefinition type union
else if (attr.type === 'boolean') { // If needed, they should be handled via type casting: (attr as any).type === 'boolean'
fieldType = 'boolean';
} else if (attr.type === 'enum' && attr.filterOptions) {
fieldType = 'enum';
options = attr.filterOptions.map(opt => ({ value: opt, label: opt }));
}
// Define validators and required fields // Define validators and required fields
let required = attr.required === true; let required = attr.required === true;
@ -444,7 +439,7 @@ export function usePromptOperations() {
} }
}; };
const handlePromptUpdate = async (promptId: string, updateData: { name: string; content: string }, originalData?: any) => { const handlePromptUpdate = async (promptId: string, updateData: { name: string; content: string }, _originalData?: any) => {
setUpdateError(null); setUpdateError(null);
try { try {

View file

@ -57,7 +57,7 @@ export function createSettingsHook(): () => GenericDataHook {
const currentUserIdRef = useRef<string | undefined>(currentUser?.id); const currentUserIdRef = useRef<string | undefined>(currentUser?.id);
// Load phone name from localStorage // Load phone name from localStorage
const loadPhoneName = useCallback((): string => { const _loadPhoneName = useCallback((): string => {
try { try {
return localStorage.getItem('userPhoneName') || ''; return localStorage.getItem('userPhoneName') || '';
} catch (error) { } catch (error) {
@ -65,9 +65,10 @@ export function createSettingsHook(): () => GenericDataHook {
return ''; return '';
} }
}, []); }, []);
void _loadPhoneName; // Intentionally unused, reserved for future use
// Load theme from localStorage // Load theme from localStorage
const loadTheme = useCallback((): string => { const _loadTheme = useCallback((): string => {
try { try {
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
if (savedTheme) { if (savedTheme) {
@ -80,9 +81,10 @@ export function createSettingsHook(): () => GenericDataHook {
return 'light'; return 'light';
} }
}, []); }, []);
void _loadTheme; // Intentionally unused, reserved for future use
// Load speech data from localStorage // Load speech data from localStorage
const loadSpeechData = useCallback((): any | null => { const _loadSpeechData = useCallback((): any | null => {
try { try {
const savedData = localStorage.getItem('speechSignUpData'); const savedData = localStorage.getItem('speechSignUpData');
const timestamp = localStorage.getItem('speechSignUpTimestamp'); const timestamp = localStorage.getItem('speechSignUpTimestamp');
@ -109,9 +111,10 @@ export function createSettingsHook(): () => GenericDataHook {
return null; return null;
} }
}, []); }, []);
void _loadSpeechData; // Intentionally unused, reserved for future use
// Fetch user data from API // Fetch user data from API
const fetchUserData = useCallback(async () => { const _fetchUserData = useCallback(async () => {
if (!currentUser?.id) return null; if (!currentUser?.id) return null;
try { try {
@ -122,9 +125,10 @@ export function createSettingsHook(): () => GenericDataHook {
throw error; throw error;
} }
}, [currentUser?.id, getUser]); }, [currentUser?.id, getUser]);
void _fetchUserData; // Intentionally unused, reserved for future use
// Fetch field definitions from backend // Fetch field definitions from backend
const fetchFieldsForSection = useCallback(async (sectionId: string): Promise<SettingsFieldConfig[]> => { const _fetchFieldsForSection = useCallback(async (sectionId: string): Promise<SettingsFieldConfig[]> => {
try { try {
setSettingsLoading(prev => ({ ...prev, [sectionId]: true })); setSettingsLoading(prev => ({ ...prev, [sectionId]: true }));
setSettingsErrors(prev => ({ ...prev, [sectionId]: null })); setSettingsErrors(prev => ({ ...prev, [sectionId]: null }));
@ -148,6 +152,7 @@ export function createSettingsHook(): () => GenericDataHook {
setSettingsLoading(prev => ({ ...prev, [sectionId]: false })); setSettingsLoading(prev => ({ ...prev, [sectionId]: false }));
} }
}, [request]); }, [request]);
void _fetchFieldsForSection; // Intentionally unused, reserved for future use
// Load all settings data // Load all settings data
const loadSettingsData = useCallback(async () => { const loadSettingsData = useCallback(async () => {

View file

@ -125,14 +125,8 @@ export function useCurrentUser() {
} }
try { try {
let logoutEndpoint = '/api/local/logout';
// Determine the correct logout endpoint based on authentication authority // Determine the correct logout endpoint based on authentication authority
if (user.authenticationAuthority === 'msft') { // Note: logoutEndpoint is determined by logoutUserApi based on authenticationAuthority
logoutEndpoint = '/api/msft/logout';
} else if (user.authenticationAuthority === 'local') {
logoutEndpoint = '/api/local/logout';
}
await logoutUserApi(request, user.authenticationAuthority); await logoutUserApi(request, user.authenticationAuthority);
@ -498,13 +492,8 @@ export function useOrgUsers() {
fieldType = 'string'; fieldType = 'string';
} }
} }
// Legacy support for old format // Note: Legacy 'boolean' and 'enum' types are not in the AttributeDefinition type union
else if (attr.type === 'boolean') { // If needed, they should be handled via type casting: (attr as any).type === 'boolean'
fieldType = 'boolean';
} else if (attr.type === 'enum' && attr.filterOptions) {
fieldType = 'enum';
options = attr.filterOptions.map(opt => ({ value: opt, label: opt }));
}
// Define validators and required fields // Define validators and required fields
let required = attr.required === true; let required = attr.required === true;
@ -547,7 +536,7 @@ export function useOrgUsers() {
key: attr.name, key: attr.name,
label: attr.label || attr.name, label: attr.label || attr.name,
type: fieldType, type: fieldType,
editable: attr.editable !== false && attr.readonly !== true, editable: (attr as any).editable !== false && (attr as any).readonly !== true,
required, required,
validator, validator,
minRows, minRows,
@ -652,7 +641,7 @@ export function useUserOperations() {
} }
}; };
const handleUserUpdate = async (userId: string, updateData: UserUpdateData, originalData?: any) => { const handleUserUpdate = async (userId: string, updateData: UserUpdateData, _originalData?: any) => {
setUpdateError(null); setUpdateError(null);
setEditingUsers(prev => new Set(prev).add(userId)); setEditingUsers(prev => new Set(prev).add(userId));

View file

@ -19,7 +19,7 @@ import { MessageOverlay } from '../components/UiComponents';
import type { MessageMode } from '../components/UiComponents'; import type { MessageMode } from '../components/UiComponents';
import { useLanguage } from '../providers/language/LanguageContext'; import { useLanguage } from '../providers/language/LanguageContext';
import { useWorkflowSelection } from '../contexts/WorkflowSelectionContext'; import { useWorkflowSelection } from '../contexts/WorkflowSelectionContext';
import { getUserDataCache } from '../utils/userCache'; // import { getUserDataCache } from '../utils/userCache'; // Unused import
import { usePermissions, type UserPermissions } from './usePermissions'; import { usePermissions, type UserPermissions } from './usePermissions';
// Workflow interface matching backend // Workflow interface matching backend
@ -279,13 +279,8 @@ export function useUserWorkflows() {
fieldType = 'string'; fieldType = 'string';
} }
} }
// Legacy support for old format // Note: Legacy 'boolean' and 'enum' types are not in the AttributeDefinition type union
else if (attr.type === 'boolean') { // If needed, they should be handled via type casting: (attr as any).type === 'boolean'
fieldType = 'boolean';
} else if (attr.type === 'enum' && attr.filterOptions) {
fieldType = 'enum';
options = attr.filterOptions.map(opt => ({ value: opt, label: opt }));
}
// Define validators and required fields // Define validators and required fields
let required = attr.required === true; let required = attr.required === true;
@ -360,7 +355,7 @@ export function useUserWorkflows() {
// Listen for workflow creation events to refetch workflows list // Listen for workflow creation events to refetch workflows list
useEffect(() => { useEffect(() => {
const handleWorkflowCreated = (event: CustomEvent<{ workflow: UserWorkflow }>) => { const handleWorkflowCreated = (_event: CustomEvent<{ workflow: UserWorkflow }>) => {
// Refetch to ensure we have the latest data // Refetch to ensure we have the latest data
fetchWorkflowsData(); fetchWorkflowsData();
}; };
@ -409,7 +404,7 @@ export function useWorkflowOperations() {
const [warningData, setWarningData] = useState<{ header: string; message: string; mode: MessageMode } | null>(null); const [warningData, setWarningData] = useState<{ header: string; message: string; mode: MessageMode } | null>(null);
// Language context // Language context
const { t } = useLanguage(); const { t: _t } = useLanguage();
// Workflow selection context - to clear selection if deleted workflow is selected // Workflow selection context - to clear selection if deleted workflow is selected
const { selectedWorkflowId, clearWorkflow } = useWorkflowSelection(); const { selectedWorkflowId, clearWorkflow } = useWorkflowSelection();
@ -594,7 +589,7 @@ export function useWorkflowOperations() {
); );
}; };
const handleWorkflowUpdate = async (workflowId: string, updateData: Partial<{ name: string; description?: string; tags?: string[] }>, originalWorkflowData?: any) => { const handleWorkflowUpdate = async (workflowId: string, updateData: Partial<{ name: string; description?: string; tags?: string[] }>, _originalWorkflowData?: any) => {
setUpdateError(null); setUpdateError(null);
setEditingWorkflows(prev => new Set(prev).add(workflowId)); setEditingWorkflows(prev => new Set(prev).add(workflowId));