diff --git a/src/api/attributesApi.ts b/src/api/attributesApi.ts index 28f9600..945f004 100644 --- a/src/api/attributesApi.ts +++ b/src/api/attributesApi.ts @@ -43,7 +43,7 @@ export async function fetchAttributes( request: ApiRequestFunction, entityType: string ): Promise { - const data = await request({ + const data = await request({ url: `/api/attributes/${entityType}`, method: 'get' }); @@ -81,7 +81,7 @@ export async function fetchConnectionAttributes(request: ApiRequestFunction): Pr * Endpoint: GET /api/attributes/FileItem */ export async function fetchFileAttributes(request: ApiRequestFunction): Promise { - const data = await request({ + const data = await request({ url: '/api/attributes/FileItem', method: 'get' }); diff --git a/src/api/authApi.ts b/src/api/authApi.ts index e4cc292..93ae5af 100644 --- a/src/api/authApi.ts +++ b/src/api/authApi.ts @@ -171,10 +171,19 @@ export async function registerApi(registerData: RegisterData): Promise { - const response = await request({ + const response = await request({ url: '/api/msft/register', method: 'post', data: userData, @@ -197,10 +206,19 @@ export async function registerWithMsalApi( } }); + const responseData: any = response; return { success: true, 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 }; } diff --git a/src/api/connectionApi.ts b/src/api/connectionApi.ts index 606a20b..83e6dfc 100644 --- a/src/api/connectionApi.ts +++ b/src/api/connectionApi.ts @@ -78,7 +78,7 @@ export type ApiRequestFunction = (options: ApiRequestOptions) => Promise { +export async function fetchConnectionAttributes(_request: ApiRequestFunction): Promise { // Note: This uses api.get directly due to response format handling // 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'); @@ -109,7 +109,7 @@ export async function fetchConnections( } } - const data = await request | Connection[]>({ + const data = await request({ url: '/api/connections/', method: 'get', params: requestParams @@ -126,7 +126,7 @@ export async function createConnection( request: ApiRequestFunction, connectionData: CreateConnectionData ): Promise { - return await request({ + return await request({ url: '/api/connections/', method: 'post', data: connectionData @@ -141,7 +141,7 @@ export async function connectService( request: ApiRequestFunction, connectionId: string ): Promise { - return await request({ + return await request({ url: `/api/connections/${connectionId}/connect`, method: 'post' }); @@ -155,7 +155,7 @@ export async function disconnectService( request: ApiRequestFunction, connectionId: string ): Promise<{ message: string }> { - return await request<{ message: string }>({ + return await request({ url: `/api/connections/${connectionId}/disconnect`, method: 'post' }); @@ -169,7 +169,7 @@ export async function deleteConnection( request: ApiRequestFunction, connectionId: string ): Promise<{ message: string }> { - return await request<{ message: string }>({ + return await request({ url: `/api/connections/${connectionId}`, method: 'delete' }); @@ -184,7 +184,7 @@ export async function updateConnection( connectionId: string, updateData: Partial ): Promise { - return await request({ + return await request({ url: `/api/connections/${connectionId}`, method: 'put', data: updateData @@ -199,7 +199,7 @@ export async function refreshMicrosoftToken( request: ApiRequestFunction, connectionId: string ): Promise { - return await request({ + return await request({ url: `/api/connections/${connectionId}/refresh-microsoft-token`, method: 'post' }); @@ -213,7 +213,7 @@ export async function refreshGoogleToken( request: ApiRequestFunction, connectionId: string ): Promise { - return await request({ + return await request({ url: `/api/connections/${connectionId}/refresh-google-token`, method: 'post' }); diff --git a/src/api/fileApi.ts b/src/api/fileApi.ts index 037a0cf..8c94328 100644 --- a/src/api/fileApi.ts +++ b/src/api/fileApi.ts @@ -58,7 +58,7 @@ export type ApiRequestFunction = (options: ApiRequestOptions) => Promise { - const data = await request({ + const data = await request({ url: '/api/attributes/FileItem', method: 'get' }); @@ -109,7 +109,7 @@ export async function fetchFiles( } } - const data = await request | FileInfo[]>({ + const data = await request({ url: '/api/files/list', method: 'get', params: requestParams @@ -127,7 +127,7 @@ export async function fetchFileById( fileId: string ): Promise { try { - const data = await request({ + const data = await request({ url: `/api/files/${fileId}`, method: 'get' }); @@ -147,7 +147,7 @@ export async function updateFile( fileId: string, fileData: Partial ): Promise { - return await request({ + return await request({ url: `/api/files/${fileId}`, method: 'put', data: fileData diff --git a/src/api/permissionApi.ts b/src/api/permissionApi.ts index a367f6f..f78ecb7 100644 --- a/src/api/permissionApi.ts +++ b/src/api/permissionApi.ts @@ -38,7 +38,7 @@ export async function fetchPermissions( params.item = item; } - const data = await request({ + const data = await request({ url: '/api/rbac/permissions', method: 'get', params diff --git a/src/api/promptApi.ts b/src/api/promptApi.ts index 8743153..6204f51 100644 --- a/src/api/promptApi.ts +++ b/src/api/promptApi.ts @@ -84,7 +84,7 @@ export type ApiRequestFunction = (options: ApiRequestOptions) => Promise { +export async function fetchPromptAttributes(_request: ApiRequestFunction): Promise { // Note: This uses api.get directly due to response format handling // 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'); @@ -115,7 +115,7 @@ export async function fetchPrompts( } } - const data = await request | Prompt[]>({ + const data = await request({ url: '/api/prompts', method: 'get', params: requestParams @@ -133,7 +133,7 @@ export async function fetchPromptById( promptId: string ): Promise { try { - const data = await request({ + const data = await request({ url: `/api/prompts/${promptId}`, method: 'get' }); @@ -152,7 +152,7 @@ export async function createPrompt( request: ApiRequestFunction, promptData: CreatePromptData ): Promise { - return await request({ + return await request({ url: '/api/prompts', method: 'post', data: promptData @@ -168,7 +168,7 @@ export async function updatePrompt( promptId: string, promptData: UpdatePromptData ): Promise { - return await request({ + return await request({ url: `/api/prompts/${promptId}`, method: 'put', data: promptData diff --git a/src/api/userApi.ts b/src/api/userApi.ts index 68dc887..126adeb 100644 --- a/src/api/userApi.ts +++ b/src/api/userApi.ts @@ -35,6 +35,8 @@ export interface AttributeDefinition { minWidth?: number; maxWidth?: number; filterOptions?: string[]; + readonly?: boolean; + editable?: boolean; } export interface PaginationParams { @@ -78,7 +80,7 @@ export async function fetchCurrentUser( endpoint = '/api/google/me'; } - return await request({ + return await request({ url: endpoint, method: 'get' }); @@ -108,7 +110,7 @@ export async function logoutUser( * Fetch user attributes from backend * Endpoint: GET /api/attributes/User */ -export async function fetchUserAttributes(request: ApiRequestFunction): Promise { +export async function fetchUserAttributes(_request: ApiRequestFunction): Promise { // 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 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 | User[]>({ + const data = await request({ url: '/api/users/', method: 'get', params: requestParams @@ -157,7 +159,7 @@ export async function fetchUserById( userId: string ): Promise { try { - const data = await request({ + const data = await request({ url: `/api/users/${userId}`, method: 'get' }); @@ -176,7 +178,7 @@ export async function createUser( request: ApiRequestFunction, userData: Partial ): Promise { - return await request({ + return await request({ url: '/api/users', method: 'post', data: userData @@ -192,7 +194,7 @@ export async function updateUser( userId: string, userData: UserUpdateData ): Promise { - return await request({ + return await request({ url: `/api/users/${userId}`, method: 'put', data: userData diff --git a/src/api/workflowApi.ts b/src/api/workflowApi.ts index 9bd3543..2e813f5 100644 --- a/src/api/workflowApi.ts +++ b/src/api/workflowApi.ts @@ -75,7 +75,7 @@ export async function fetchWorkflows(request: ApiRequestFunction): Promise({ + const data = await request({ url: '/api/workflows/', method: 'get' }); @@ -134,7 +134,7 @@ export async function fetchWorkflow( request: ApiRequestFunction, workflowId: string ): Promise { - return await request({ + return await request({ url: `/api/workflows/${workflowId}`, method: 'get' }); @@ -148,7 +148,7 @@ export async function fetchWorkflowStatus( request: ApiRequestFunction, workflowId: string ): Promise { - const data = await request({ + const data = await request({ url: `/api/workflows/${workflowId}/status`, method: 'get' }); @@ -174,7 +174,7 @@ export async function fetchWorkflowMessages( messageId?: string ): Promise { const params = messageId ? { messageId } : undefined; - const data = await request({ + const data = await request({ url: `/api/workflows/${workflowId}/messages`, method: 'get', params @@ -207,7 +207,7 @@ export async function fetchWorkflowLogs( logId?: string ): Promise { const params = logId ? { logId } : undefined; - const data = await request({ + const data = await request({ url: `/api/workflows/${workflowId}/logs`, method: 'get', params @@ -248,7 +248,7 @@ export async function fetchChatData( console.log('📤 fetchChatData request:', requestConfig); - const data = await request(requestConfig); + const data = await request(requestConfig); console.log('📥 fetchChatData response:', data); @@ -359,7 +359,7 @@ export async function startWorkflowApi( console.log(' Request Body:', JSON.stringify(requestBody, null, 2)); console.log(' Full Request Config:', JSON.stringify(requestConfig, null, 2)); - const response = await request(requestConfig); + const response = await request(requestConfig); console.log('📥 startWorkflow response:', response); @@ -389,7 +389,7 @@ export async function updateWorkflowApi( workflowId: string, updateData: Partial<{ name: string; description?: string; tags?: string[] }> ): Promise { - return await request({ + return await request({ url: `/api/workflows/${workflowId}`, method: 'put', data: updateData @@ -494,7 +494,7 @@ export async function fetchAttributes( request: ApiRequestFunction, entityType: string = 'ChatWorkflow' ): Promise { - const data = await request({ + const data = await request({ url: `/api/attributes/${entityType}`, method: 'get' }); diff --git a/src/components/FormGenerator/ActionButtons/EditActionButton/EditActionButton.tsx b/src/components/FormGenerator/ActionButtons/EditActionButton/EditActionButton.tsx index dc288b4..3555125 100644 --- a/src/components/FormGenerator/ActionButtons/EditActionButton/EditActionButton.tsx +++ b/src/components/FormGenerator/ActionButtons/EditActionButton/EditActionButton.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { MdModeEdit } from 'react-icons/md'; import { useLanguage } from '../../../../providers/language/LanguageContext'; import { Popup } from '../../../UiComponents/Popup'; -import { FormGeneratorForm } from '../../FormGeneratorForm'; +import { FormGeneratorForm, AttributeDefinition } from '../../FormGeneratorForm'; import styles from '../ActionButton.module.css'; export interface EditActionButtonProps { @@ -154,16 +154,18 @@ export function EditActionButton({ // Get the item ID from the row const itemId = (editData as any)[idField]; - // Get edit fields configuration - const fields = getEditFields(); + // Get edit fields configuration from attributes + const attributes = getAttributes(); + const fields = attributes || []; // Extract the fields to update from the edit data const updateData: any = {}; - fields.forEach(field => { + fields.forEach((field: AttributeDefinition) => { if (field.editable !== false) { - const value = (updatedData as any)[field.key]; + const fieldName = field.name; + const value = (updatedData as any)[fieldName]; if (value !== undefined) { - updateData[field.key] = value; + updateData[fieldName] = value; } } }); diff --git a/src/components/FormGenerator/ActionButtons/PlayActionButton/PlayActionButton.tsx b/src/components/FormGenerator/ActionButtons/PlayActionButton/PlayActionButton.tsx index 42d0224..b216200 100644 --- a/src/components/FormGenerator/ActionButtons/PlayActionButton/PlayActionButton.tsx +++ b/src/components/FormGenerator/ActionButtons/PlayActionButton/PlayActionButton.tsx @@ -30,9 +30,9 @@ export function PlayActionButton({ loading = false, className = '', title, - hookData, + hookData: _hookData, idField = 'id', - nameField = 'name', + nameField: _nameField = 'name', contentField = 'content', navigateTo = 'start/dashboard', mode = 'prompt' diff --git a/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx b/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx index 340311b..b657fef 100644 --- a/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx +++ b/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx @@ -64,7 +64,7 @@ export function FormGeneratorControls({ filterFocused, onFilterFocus, selectedCount, - displayData, + displayData: _displayData, onDeleteSingle, onDeleteMultiple, onRefresh, diff --git a/src/components/FormGenerator/FormGeneratorList/FormGeneratorList.tsx b/src/components/FormGenerator/FormGeneratorList/FormGeneratorList.tsx index 0c1ba9e..ff767f3 100644 --- a/src/components/FormGenerator/FormGeneratorList/FormGeneratorList.tsx +++ b/src/components/FormGenerator/FormGeneratorList/FormGeneratorList.tsx @@ -453,7 +453,7 @@ export function FormGeneratorList>({ }; // 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) { return (
@@ -491,12 +491,15 @@ export function FormGeneratorList>({ } // 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 ( onFieldChange?.(row, field.key, newValue)} - type={attributeTypeToInputType(field.type || 'string')} + type={textFieldType as 'text' | 'email' | 'url' | 'password' | 'search' | 'tel' | 'number'} required={field.required} readonly={!field.editable} className={styles.fieldInput} diff --git a/src/components/Sidebar/SidebarItem.tsx b/src/components/Sidebar/SidebarItem.tsx index dc0ff14..c60f5fd 100644 --- a/src/components/Sidebar/SidebarItem.tsx +++ b/src/components/Sidebar/SidebarItem.tsx @@ -32,7 +32,6 @@ const SidebarItem: React.FC = React.memo(({ // Get the actual color from parent li element const parentLi = wrapper.closest('li'); - const parentColor = parentLi ? window.getComputedStyle(parentLi).color : '#000000'; // Force color directly - use black for now to ensure visibility const iconColor = '#000000'; // Force black for visibility @@ -218,7 +217,6 @@ const SidebarItem: React.FC = React.memo(({ > = ({ item, isOpen, isMinimiz {SubIcon && ( { + removeOptimistically: (_fileId: string) => { // This will be handled by the parent component's state }, refetch: async () => { @@ -121,7 +121,7 @@ export function ConnectedFilesList({ // View button (always shown) buttons.push({ type: 'view', - onAction: async (file: WorkflowFile) => { + onAction: async (_file: WorkflowFile) => { // View is handled by ViewActionButton's FilePreview component return Promise.resolve(); }, @@ -156,7 +156,7 @@ export function ConnectedFilesList({ return buttons; }, [actionButtons, onDelete, onRemove]); - const handleView = async (file: WorkflowFile) => { + const handleView = async (_file: WorkflowFile) => { // View is handled by ViewActionButton's FilePreview component return Promise.resolve(); }; @@ -187,10 +187,10 @@ export function ConnectedFilesList({
{allFiles .filter(file => file.fileId && file.fileId.trim() !== '') // Ensure fileId exists - .map((file, index) => { - const isDeleting = deletingFiles.has(file.fileId!); - const isPreviewing = previewingFiles.has(file.fileId!); - const isRemoving = removingFiles.has(file.fileId!); + .map((file) => { + // const isDeleting = deletingFiles.has(file.fileId!); + // const isPreviewing = previewingFiles.has(file.fileId!); + // const isRemoving = removingFiles.has(file.fileId!); // Use fileId as key since we've filtered out files without it const uniqueKey = file.fileId!; diff --git a/src/components/UiComponents/Log/Log.tsx b/src/components/UiComponents/Log/Log.tsx index 369d479..a946c40 100644 --- a/src/components/UiComponents/Log/Log.tsx +++ b/src/components/UiComponents/Log/Log.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { LogProps, WorkflowLog } from './LogTypes'; +import { LogProps } from './LogTypes'; import { AutoScroll } from '../AutoScroll'; import { formatUnixTimestamp } from '../../../utils/time'; import styles from './Log.module.css'; diff --git a/src/components/UiComponents/Log/LogTypes.ts b/src/components/UiComponents/Log/LogTypes.ts index 4eb6a51..6032a8e 100644 --- a/src/components/UiComponents/Log/LogTypes.ts +++ b/src/components/UiComponents/Log/LogTypes.ts @@ -1,5 +1,3 @@ -import type React from 'react'; - /** * Log entry from workflow */ diff --git a/src/components/UiComponents/MapView/MapViewLeaflet.tsx b/src/components/UiComponents/MapView/MapViewLeaflet.tsx index c7d75aa..78d7bd6 100644 --- a/src/components/UiComponents/MapView/MapViewLeaflet.tsx +++ b/src/components/UiComponents/MapView/MapViewLeaflet.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import { lv95ToWGS84, wgs84ToLV95 } from './LV95Converter'; -import type { MapPoint, ParcelGeometry, MapViewProps } from './MapView'; +import type { MapViewProps } from './MapView'; import styles from './MapView.module.css'; // Fix for default marker icons in Leaflet @@ -32,7 +32,7 @@ const MapViewLeaflet: React.FC = ({ }) => { const mapRef = useRef(null); const mapContainerRef = useRef(null); - const layersRef = useRef([]); + const layersRef = useRef([]); const centerMarkerRef = useRef(null); // Initialize map diff --git a/src/components/UiComponents/Messages/MessageParts/DocumentItem.tsx b/src/components/UiComponents/Messages/MessageParts/DocumentItem.tsx index 3209f8d..fabe925 100644 --- a/src/components/UiComponents/Messages/MessageParts/DocumentItem.tsx +++ b/src/components/UiComponents/Messages/MessageParts/DocumentItem.tsx @@ -23,7 +23,7 @@ export interface DocumentItemProps { */ export const DocumentItem: React.FC = ({ document, - message, + message: _message, className, onFileDelete, onFileRemove, @@ -31,7 +31,7 @@ export const DocumentItem: React.FC = ({ deletingFiles = new Set(), previewingFiles = new Set(), removingFiles = new Set(), - workflowId + workflowId: _workflowId }) => { // Convert MessageDocument to WorkflowFile format for compatibility with action buttons const workflowFile: WorkflowFile = useMemo(() => ({ @@ -50,7 +50,7 @@ export const DocumentItem: React.FC = ({ // Create hookData object for action buttons const hookData = useMemo(() => ({ - handleDelete: async (fileId: string) => { + handleDelete: async (_fileId: string) => { if (onFileDelete) { await onFileDelete(workflowFile); return true; diff --git a/src/components/UiComponents/TextField/TextField.tsx b/src/components/UiComponents/TextField/TextField.tsx index c33a7b1..b0cfcce 100644 --- a/src/components/UiComponents/TextField/TextField.tsx +++ b/src/components/UiComponents/TextField/TextField.tsx @@ -9,6 +9,7 @@ interface TextFieldProps extends BaseTextFieldProps { step?: string; min?: string | number; max?: string | number; + onKeyDown?: (e: React.KeyboardEvent) => void; } const TextField: React.FC = ({ diff --git a/src/components/UiComponents/WorkflowStatus/WorkflowStatusTypes.ts b/src/components/UiComponents/WorkflowStatus/WorkflowStatusTypes.ts index 50dabb6..d83ca14 100644 --- a/src/components/UiComponents/WorkflowStatus/WorkflowStatusTypes.ts +++ b/src/components/UiComponents/WorkflowStatus/WorkflowStatusTypes.ts @@ -1,5 +1,3 @@ -import type React from 'react'; - /** * Log entry from workflow */ diff --git a/src/components/UiComponents/index.ts b/src/components/UiComponents/index.ts index e00d9d2..51a83fc 100644 --- a/src/components/UiComponents/index.ts +++ b/src/components/UiComponents/index.ts @@ -11,7 +11,9 @@ export * from './MapView'; export * from './ParcelInfoPanel'; export * from './CopyableTruncatedValue'; 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 * from './WorkflowStatus'; +export type { WorkflowStatusProps } from './WorkflowStatus/WorkflowStatusTypes'; export * from './AutoScroll'; diff --git a/src/contexts/FileContext.tsx b/src/contexts/FileContext.tsx index 9fc661d..d178417 100644 --- a/src/contexts/FileContext.tsx +++ b/src/contexts/FileContext.tsx @@ -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'; interface FileContextType { @@ -16,7 +16,7 @@ interface FileContextType { const FileContext = createContext(undefined); 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 { handleFileUpload: hookHandleFileUpload, handleFileDelete: hookHandleFileDelete, @@ -40,25 +40,13 @@ export function FileProvider({ children }: { children: React.ReactNode }) { return result; } - // Add file optimistically to the shared state - 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); - + // File will be added via refetch // Refetch to ensure we have the latest data (this will update all consumers) await refetchFiles(); } return result; - }, [hookHandleFileUpload, addFileOptimistically, refetchFiles]); + }, [hookHandleFileUpload, refetchFiles]); // Centralized file delete that updates the shared state const handleFileDelete = useCallback(async (fileId: string, onOptimisticDelete?: () => void) => { diff --git a/src/contexts/WorkflowSelectionContext.tsx b/src/contexts/WorkflowSelectionContext.tsx index 33a2c37..d9a08d0 100644 --- a/src/contexts/WorkflowSelectionContext.tsx +++ b/src/contexts/WorkflowSelectionContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react'; +import { createContext, useContext, useState, useCallback, ReactNode } from 'react'; interface WorkflowSelectionContextType { selectedWorkflowId: string | null; diff --git a/src/core/PageManager/PageManager.tsx b/src/core/PageManager/PageManager.tsx index 3fbc2ec..6a07c96 100644 --- a/src/core/PageManager/PageManager.tsx +++ b/src/core/PageManager/PageManager.tsx @@ -71,7 +71,7 @@ const PageManager: React.FC = ({ ) : ( { + onButtonClick={(_buttonId, _button) => { }} /> )} @@ -88,7 +88,8 @@ const PageManager: React.FC = ({ } else { if (import.meta.env.DEV) { - const instance = newInstances.get(currentPath); + const _instance = newInstances.get(currentPath); + void _instance; // Intentionally unused, for debugging purposes } } diff --git a/src/core/PageManager/PageRenderer.tsx b/src/core/PageManager/PageRenderer.tsx index 83bb702..aebb03d 100644 --- a/src/core/PageManager/PageRenderer.tsx +++ b/src/core/PageManager/PageRenderer.tsx @@ -10,6 +10,7 @@ import { DragDropOverlay } from '../../components/UiComponents/DragDropOverlay'; import { useLanguage } from '../../providers/language/LanguageContext'; import { usePermissions } from '../../hooks/usePermissions'; import { FiPaperclip } from 'react-icons/fi'; +import type { WorkflowFile } from '../../hooks/playground/useDashboardInputForm'; import styles from '../../styles/pages.module.css'; interface PageRendererProps { @@ -362,7 +363,7 @@ const PageRenderer: React.FC = ({ onSave?: (sectionId: string, data: any) => Promise; getNestedValue: (obj: any, path: string) => 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>({}); const [sectionSaveLoading, setSectionSaveLoading] = useState>({}); const [sectionSaveMessages, setSectionSaveMessages] = useState>({}); @@ -677,6 +678,46 @@ const PageRenderer: React.FC = ({ // Render content based on type 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) | 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) + 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) + const result = handler(file); + if (result instanceof Promise) await result; + } : undefined; + + const wrapFileRemove: ((file: WorkflowFile) => Promise) | 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) + 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) + const result = handler(file); + if (result instanceof Promise) await result; + } : undefined; + switch (content.type) { case 'heading': const HeadingTag = `h${content.level || 2}` as keyof React.JSX.IntrinsicElements; @@ -834,7 +875,14 @@ const PageRenderer: React.FC = ({ } } else { // 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 { disabledFn = () => false; @@ -949,7 +997,7 @@ const PageRenderer: React.FC = ({ {})} placeholder={t('dashboard.prompt.select', 'Select a prompt')} emptyMessage={t('dashboard.prompt.empty', 'No prompts available')} headerText={t('dashboard.prompt.header', 'Select Prompt')} @@ -966,7 +1014,7 @@ const PageRenderer: React.FC = ({ {})} placeholder={t('dashboard.workflow.mode.select', 'Select workflow mode')} emptyMessage={t('dashboard.workflow.mode.empty', 'No modes available')} headerText={t('dashboard.workflow.mode.header', 'Workflow Mode')} @@ -1033,7 +1081,7 @@ const PageRenderer: React.FC = ({ }, { type: 'remove', - onAction: hookData.handleFileRemove, + onAction: wrapFileRemove, showOnlyForPending: true, idField: 'fileId', loadingStateName: 'removingItems' @@ -1045,9 +1093,12 @@ const PageRenderer: React.FC = ({ idField: 'fileId' } ]} - onDelete={hookData.handleFileDelete} - onRemove={hookData.handleFileRemove} - onAttach={hookData.handleFileAttach} // Allow attaching files for next message + onDelete={wrapFileDelete} + onRemove={wrapFileRemove} + onAttach={hookData.handleFileAttach ? async (fileId: string) => { + const result = hookData.handleFileAttach!(fileId); + if (result instanceof Promise) await result; + } : undefined} deletingFiles={hookData.deletingFiles || new Set()} previewingFiles={hookData.previewingFiles || new Set()} removingFiles={new Set()} // Can be tracked if needed @@ -1080,7 +1131,13 @@ const PageRenderer: React.FC = ({ justifyContent: 'flex-end' }}> { + const handler = hookData.handleFileUploadAndAttach || hookData.handleFileUpload; + if (handler) { + // Handler returns Promise<{ success, data }>, but UploadButton expects Promise + await handler(file); + } + } : async () => {}} disabled={hookData.isSubmitting || false} loading={hookData.uploadingFile || false} variant="primary" @@ -1207,7 +1264,7 @@ const PageRenderer: React.FC = ({ {})} placeholder={t('dashboard.prompt.select', 'Select a prompt')} emptyMessage={t('dashboard.prompt.empty', 'No prompts available')} headerText={t('dashboard.prompt.header', 'Select Prompt')} @@ -1222,7 +1279,7 @@ const PageRenderer: React.FC = ({ {})} placeholder={t('dashboard.workflow.mode.select', 'Select workflow mode')} emptyMessage={t('dashboard.workflow.mode.empty', 'No modes available')} headerText={t('dashboard.workflow.mode.header', 'Workflow Mode')} @@ -1320,8 +1377,8 @@ const PageRenderer: React.FC = ({ showDocuments={config.showDocuments !== false} showMetadata={config.showMetadata !== false} showProgress={config.showProgress !== false} - onFileDelete={hookData?.handleFileDelete} - onFileRemove={hookData?.handleFileRemove} + onFileDelete={wrapFileDelete} + onFileRemove={wrapFileRemove} deletingFiles={hookData?.deletingFiles} previewingFiles={hookData?.previewingFiles} removingFiles={hookData?.removingFiles} @@ -1334,8 +1391,8 @@ const PageRenderer: React.FC = ({ key={message.id || index} message={cleanMessage} showDocuments={config.showDocuments !== false} - onFileDelete={hookData?.handleFileDelete} - onFileRemove={hookData?.handleFileRemove} + onFileDelete={wrapFileDelete} + onFileRemove={wrapFileRemove} deletingFiles={hookData?.deletingFiles} previewingFiles={hookData?.previewingFiles} removingFiles={hookData?.removingFiles} @@ -1356,8 +1413,8 @@ const PageRenderer: React.FC = ({ showMetadata={config.showMetadata !== false} showProgress={config.showProgress !== false} emptyMessage={config.emptyMessage ? resolveLanguageText(config.emptyMessage, t) : undefined} - onFileDelete={hookData?.handleFileDelete} - onFileRemove={hookData?.handleFileRemove} + onFileDelete={wrapFileDelete} + onFileRemove={wrapFileRemove} deletingFiles={hookData?.deletingFiles} previewingFiles={hookData?.previewingFiles} removingFiles={hookData?.removingFiles} @@ -1662,6 +1719,14 @@ const PageRenderer: React.FC = ({ 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 ( = ({ variant={button.variant || 'primary'} size={button.size || 'md'} icon={button.icon} - disabled={button.disabled} + disabled={disabledValue} onSuccess={() => { // Refetch data after successful creation if (hookData.refetch) { diff --git a/src/core/PageManager/SidebarProvider.tsx b/src/core/PageManager/SidebarProvider.tsx index fdb2de4..fa175dd 100644 --- a/src/core/PageManager/SidebarProvider.tsx +++ b/src/core/PageManager/SidebarProvider.tsx @@ -121,7 +121,7 @@ export const SidebarProvider: React.FC = ({ children }) => } // Process parent groups - for (const [parentPath, parentGroup] of parentGroups.entries()) { + for (const [_parentPath, parentGroup] of parentGroups.entries()) { // Filter subpages by RBAC access const accessibleSubpages = []; for (const subpage of parentGroup.subpages) { diff --git a/src/core/PageManager/data/pages/dashboard.ts b/src/core/PageManager/data/pages/dashboard.ts index cefbab2..348cf28 100644 --- a/src/core/PageManager/data/pages/dashboard.ts +++ b/src/core/PageManager/data/pages/dashboard.ts @@ -34,6 +34,7 @@ export const dashboardPageData: GenericPageData = { placeholder: 'dashboard.workflow.select', emptyMessage: 'dashboard.workflow.empty', headerText: 'dashboard.workflow.header', + onSelect: () => {}, // Placeholder - actual handler comes from dataSource.onSelectMethod dataSource: { itemsProperty: 'workflowItems', selectedIdProperty: 'selectedWorkflowId', diff --git a/src/core/PageManager/data/pages/pek-tables.ts b/src/core/PageManager/data/pages/pek-tables.ts index af79347..f7ece04 100644 --- a/src/core/PageManager/data/pages/pek-tables.ts +++ b/src/core/PageManager/data/pages/pek-tables.ts @@ -1,5 +1,5 @@ import { GenericPageData } from '../../pageInterface'; -import { FaTable, FaBuilding } from 'react-icons/fa'; +import { FaTable } from 'react-icons/fa'; import { IoMdSend } from 'react-icons/io'; import { usePekTablesContext } from '../../../../contexts/PekTablesContext'; import PekTablesDropdown from './pek-tables/PekTablesDropdown'; diff --git a/src/core/PageManager/data/pages/pek/PekLocationInput.tsx b/src/core/PageManager/data/pages/pek/PekLocationInput.tsx index 6f73875..ffbf062 100644 --- a/src/core/PageManager/data/pages/pek/PekLocationInput.tsx +++ b/src/core/PageManager/data/pages/pek/PekLocationInput.tsx @@ -7,16 +7,16 @@ import styles from './PekLocationInput.module.css'; const PekLocationInput: React.FC = () => { const { - kanton, - setKanton, - gemeinde, - setGemeinde, + kanton: _kanton, + setKanton: _setKanton, + gemeinde: _gemeinde, + setGemeinde: _setGemeinde, adresse, setAdresse, buildLocationString, useCurrentLocation, isGettingLocation, - locationError, + locationError: _locationError, searchParcel, isSearchingParcel } = usePekContext(); diff --git a/src/core/PageManager/pageInterface.ts b/src/core/PageManager/pageInterface.ts index 22fe91a..5c8d9c9 100644 --- a/src/core/PageManager/pageInterface.ts +++ b/src/core/PageManager/pageInterface.ts @@ -9,13 +9,14 @@ export type PrivilegeChecker = () => boolean | Promise; export interface ButtonFormField { key: string; label: string | LanguageText; - type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly'; + type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly' | 'multiselect'; required?: boolean; placeholder?: string | LanguageText; minRows?: number; maxRows?: number; validator?: (value: any) => string | null; defaultValue?: any; + options?: string[] | Array<{ value: string | number; label: string }>; // For enum/multiselect fields } // Dropdown configuration for header dropdown buttons @@ -37,6 +38,7 @@ export interface DropdownConfig { itemsProperty?: string; // Property name in hookData that contains items array selectedIdProperty?: string; // Property name in hookData that contains selectedItemId 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 // File operations 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; // For file download functionality handleDelete?: (fileId: string, onOptimisticDelete?: () => void) => Promise; // For file delete functionality + handleFileDelete?: ((fileId: string, onOptimisticDelete?: () => void) => Promise) | ((file: any) => Promise); // Can accept fileId or WorkflowFile handlePreview?: (fileId: string, fileName: string, mimeType?: string) => Promise; // 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) | ((file: any) => Promise | void); // Can accept fileId or WorkflowFile + handleFileAttach?: (fileId: string) => Promise; // 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; // Set of file IDs being deleted + previewingFiles?: Set; // Set of file IDs being previewed + removingFiles?: Set; // 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 onDelete?: (row: any) => Promise; // For single item deletion onDeleteMultiple?: (rows: any[]) => Promise; // For multiple item deletion @@ -170,14 +187,37 @@ export interface GenericDataHook { onInputChange?: (value: string) => void; handleSubmit?: () => Promise; // No parameters, uses internal inputValue 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; + 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; // Workflow lifecycle state workflowId?: string; workflowStatus?: string; + workflowData?: { + currentRound?: number; + [key: string]: any; + }; isRunning?: boolean; + currentRound?: number; // Current workflow round + latestStats?: any; // Latest workflow statistics // Messages from workflow messages?: any[]; // Logs from workflow logs?: any[]; + // Dashboard log tree + dashboardTree?: any; // Dashboard log tree structure + onToggleOperationExpanded?: (operationId: string) => void; + getChildOperations?: (parentId: string | null) => string[]; // Message overlay component MessageOverlayComponent?: () => React.ReactElement; // Settings-specific properties @@ -186,6 +226,8 @@ export interface GenericDataHook { settingsLoading?: Record; // Loading state per section settingsErrors?: Record; // Error state per section saveSection?: (sectionId: string, data: any) => Promise; // Save handler for a section + // Dropdown data source loading property + [key: string]: any; // Allow additional properties for dynamic data sources } // Action button configuration @@ -306,8 +348,8 @@ export interface PageDataFile { export interface SidebarItem { id: string; name: string; - link: string; - icon?: IconType; + link: string | undefined; // Allow undefined for parent groups that aren't clickable pages + icon?: IconType | React.ComponentType>; // Allow both IconType and SVG components moduleEnabled: boolean; order: number; submenu?: SidebarSubmenuItemData[]; diff --git a/src/hooks/playground/useDashboardInputForm.ts b/src/hooks/playground/useDashboardInputForm.ts index 62ec806..809e1f2 100644 --- a/src/hooks/playground/useDashboardInputForm.ts +++ b/src/hooks/playground/useDashboardInputForm.ts @@ -11,6 +11,7 @@ import { useWorkflowLifecycle } from './useWorkflowLifecycle'; import { useWorkflows } from './useWorkflows'; import { useDashboardLogTree } from './useDashboardLogTree'; import { extractFileIdsFromMessage, convertFilesToDocuments, sortMessages } from './playgroundUtils'; +import type { WorkflowLog as LogTypesWorkflowLog } from '../../components/UiComponents/Log/LogTypes'; export interface WorkflowFile { id: string; @@ -45,7 +46,6 @@ export function useDashboardInputForm() { isStopping, startingWorkflow, messages, - logs, dashboardLogs, unifiedContentLogs, latestStats, @@ -190,7 +190,20 @@ export function useDashboardInputForm() { // Only process if there are new logs 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; @@ -352,7 +365,7 @@ export function useDashboardInputForm() { return allMessages.sort(sortMessages); }, [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); if (result.success && result.fileData) { @@ -360,27 +373,32 @@ export function useDashboardInputForm() { const fileData = responseData.file || responseData; const fileId = fileData?.id; - if (!fileId) return; - - const newFile: WorkflowFile = { - id: fileId, - fileId: fileId, - fileName: fileData.fileName || file.name, - fileSize: fileData.fileSize || file.size, - mimeType: fileData.mimeType || file.type || 'application/octet-stream', - source: 'user_uploaded' - }; - - setPendingFiles(prev => { - if (prev.some(f => f.fileId === fileId)) { - return prev; - } - return [...prev, newFile]; - }); + if (fileId) { + const newFile: WorkflowFile = { + id: fileId, + fileId: fileId, + fileName: fileData.fileName || file.name, + fileSize: fileData.fileSize || file.size, + mimeType: fileData.mimeType || file.type || 'application/octet-stream', + source: 'user_uploaded' + }; + + setPendingFiles(prev => { + if (prev.some(f => f.fileId === fileId)) { + return prev; + } + return [...prev, newFile]; + }); + } } + + return { + success: result.success || false, + data: result.fileData || null + }; }, [workflowId, fileContext]); - const handleFileAttach = useCallback(async (fileId: string) => { + const handleFileAttach = useCallback(async (fileId: string): Promise => { const isInPending = pendingFiles.some(f => f.fileId === fileId); if (isInPending) { @@ -424,8 +442,8 @@ export function useDashboardInputForm() { } }, [pendingFiles, fileContext.files, workflowFiles]); - const handleFileUploadAndAttach = useCallback(async (file: File) => { - await handleFileUpload(file); + const handleFileUploadAndAttach = useCallback(async (file: File): Promise<{ success: boolean; data: any }> => { + return await handleFileUpload(file); }, [handleFileUpload]); const handleFileRemove = useCallback(async (file: WorkflowFile) => { diff --git a/src/hooks/playground/useDashboardLogTree.ts b/src/hooks/playground/useDashboardLogTree.ts index 0160b46..b698229 100644 --- a/src/hooks/playground/useDashboardLogTree.ts +++ b/src/hooks/playground/useDashboardLogTree.ts @@ -56,7 +56,6 @@ export function useDashboardLogTree() { // Get or create operation const existingOperation = newTree.operations.get(operationId); - const isNewOperation = !existingOperation; // Create new logs Map (copy existing logs if updating) const logsMap = existingOperation diff --git a/src/hooks/playground/useWorkflowLifecycle.ts b/src/hooks/playground/useWorkflowLifecycle.ts index 5ed9076..9e2b997 100644 --- a/src/hooks/playground/useWorkflowLifecycle.ts +++ b/src/hooks/playground/useWorkflowLifecycle.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; +import { useState, useEffect, useCallback, useRef } from 'react'; import { useApiRequest } from '../useApi'; import { type Workflow, @@ -334,7 +334,6 @@ export function useWorkflowLifecycle() { // Determine if polling should continue const currentStatus = statusRef.current; - const changedAt = statusChangedFromRunningAtRef.current; // Stop polling immediately for failed or stopped workflows // For completed workflows, allow grace period (handled by useEffect) @@ -357,7 +356,7 @@ export function useWorkflowLifecycle() { }, [request, updateWorkflowStatus, processUnifiedChatData]); // Load initial workflow data (non-polling) - const loadWorkflowData = useCallback(async (id: string) => { + const _loadWorkflowData = useCallback(async (id: string) => { try { const workflowData = await fetchWorkflowApi(request, id).catch(() => null); @@ -416,6 +415,7 @@ export function useWorkflowLifecycle() { console.error('Error loading workflow data:', error); } }, [request, updateWorkflowStatus, convertLogToFrontendFormat, processUnifiedChatData]); + void _loadWorkflowData; // Intentionally unused, reserved for future use // Set up polling when workflow is running useEffect(() => { diff --git a/src/hooks/useAuthentication.ts b/src/hooks/useAuthentication.ts index a723db3..d095502 100644 --- a/src/hooks/useAuthentication.ts +++ b/src/hooks/useAuthentication.ts @@ -4,7 +4,7 @@ import { useMsal } from '@azure/msal-react'; import api from '../api'; import { useApiRequest } from './useApi'; import { getApiBaseUrl } from '../../config/config'; -import { setUserDataCache, clearUserDataCache } from '../utils/userCache'; +import { setUserDataCache, clearUserDataCache, type CachedUserData } from '../utils/userCache'; import { loginApi, fetchCurrentUserApi, @@ -44,7 +44,7 @@ export function useAuth() { if (userData) { // Cache user data in sessionStorage (cleared on tab close - more secure than localStorage) - setUserDataCache(userData); + setUserDataCache(userData as CachedUserData); } } catch (userError) { console.error('Failed to fetch user data after login:', userError); @@ -171,7 +171,7 @@ export function useMsalAuth() { try { const userData = await fetchCurrentUserApi('msft'); if (userData) { - setUserDataCache(userData); + setUserDataCache(userData as CachedUserData); } } catch (userError) { console.error('Failed to fetch user data after Microsoft login:', userError); @@ -349,7 +349,7 @@ export function useGoogleAuth() { try { const userData = await fetchCurrentUserApi('google'); if (userData) { - setUserDataCache(userData); + setUserDataCache(userData as CachedUserData); } } catch (userError) { console.error('Failed to fetch user data after Google login:', userError); @@ -652,7 +652,7 @@ export function useCurrentUser() { setUser(userData); // Cache user data in sessionStorage (cleared on tab close - more secure than localStorage) - setUserDataCache(userData); + setUserDataCache(userData as CachedUserData); return userData; } catch (error: any) { diff --git a/src/hooks/useFiles.ts b/src/hooks/useFiles.ts index 4184fca..6ae5dda 100644 --- a/src/hooks/useFiles.ts +++ b/src/hooks/useFiles.ts @@ -7,14 +7,12 @@ import { getUserDataCache } from '../utils/userCache'; import { useApiRequest } from './useApi'; import { usePermissions, type UserPermissions } from './usePermissions'; import { - fetchFileAttributes, + fetchFileAttributes as _fetchFileAttributes, fetchFiles as fetchFilesApi, fetchFileById as fetchFileByIdApi, updateFile as updateFileApi, deleteFile as deleteFileApi, - deleteFiles as deleteFilesApi, - type AttributeDefinition, - type PaginationParams + deleteFiles as deleteFilesApi } from '../api/fileApi'; // File interfaces - exactly matching backend FileItem model @@ -32,7 +30,7 @@ export interface FileInfo { // Field names come directly from backend attributes export type UserFile = any; -// Attribute definition interface +// Attribute definition interface (local definition, not imported to avoid conflicts) export interface AttributeDefinition { name: string; label: string; @@ -46,7 +44,7 @@ export interface AttributeDefinition { filterOptions?: string[]; // For enum types } -// Pagination parameters +// Pagination parameters (local definition, not imported to avoid conflicts) export interface PaginationParams { page?: number; pageSize?: number; @@ -129,8 +127,7 @@ export function useUserFiles() { if (!cachedUser) { // User is not authenticated, skip fetching files setFiles([]); - setLoading(false); - setError(null); + // Note: loading and error are managed by useApiRequest hook return; } diff --git a/src/hooks/usePek.ts b/src/hooks/usePek.ts index b40c6a9..e863411 100644 --- a/src/hooks/usePek.ts +++ b/src/hooks/usePek.ts @@ -456,10 +456,10 @@ export function usePek() { const firstCoord = clickedParcel.coordinates[0]; // Calculate centroid as fallback, but prefer a point we know is inside - const sumX = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.x, 0); - const sumY = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.y, 0); - const centroidX = sumX / clickedParcel.coordinates.length; - const centroidY = sumY / clickedParcel.coordinates.length; + // const sumX = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.x, 0); + // const sumY = clickedParcel.coordinates.reduce((sum, coord) => sum + coord.y, 0); + // const _centroidX = sumX / clickedParcel.coordinates.length; + // const _centroidY = sumY / clickedParcel.coordinates.length; // Use first coordinate (guaranteed to be on/in the parcel) for search const locationString = `${firstCoord.x},${firstCoord.y}`; @@ -658,7 +658,7 @@ export function usePek() { // Try to update the parcel via PUT request try { - const updateResponse = await api.put( + await api.put( `/api/realestate/parzelle/${parzelleResult.id}`, updateParcelRequestBody ); diff --git a/src/hooks/usePrompts.ts b/src/hooks/usePrompts.ts index 1a6c1a3..ec24da6 100644 --- a/src/hooks/usePrompts.ts +++ b/src/hooks/usePrompts.ts @@ -223,13 +223,8 @@ export function usePrompts() { fieldType = 'string'; } } - // Legacy support for old format - else if (attr.type === 'boolean') { - fieldType = 'boolean'; - } else if (attr.type === 'enum' && attr.filterOptions) { - fieldType = 'enum'; - options = attr.filterOptions.map(opt => ({ value: opt, label: opt })); - } + // Note: Legacy 'boolean' and 'enum' types are not in the AttributeDefinition type union + // If needed, they should be handled via type casting: (attr as any).type === 'boolean' // Define validators and required fields 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); try { diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 0d0ce30..8037ebf 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -57,7 +57,7 @@ export function createSettingsHook(): () => GenericDataHook { const currentUserIdRef = useRef(currentUser?.id); // Load phone name from localStorage - const loadPhoneName = useCallback((): string => { + const _loadPhoneName = useCallback((): string => { try { return localStorage.getItem('userPhoneName') || ''; } catch (error) { @@ -65,9 +65,10 @@ export function createSettingsHook(): () => GenericDataHook { return ''; } }, []); + void _loadPhoneName; // Intentionally unused, reserved for future use // Load theme from localStorage - const loadTheme = useCallback((): string => { + const _loadTheme = useCallback((): string => { try { const savedTheme = localStorage.getItem('theme'); if (savedTheme) { @@ -80,9 +81,10 @@ export function createSettingsHook(): () => GenericDataHook { return 'light'; } }, []); + void _loadTheme; // Intentionally unused, reserved for future use // Load speech data from localStorage - const loadSpeechData = useCallback((): any | null => { + const _loadSpeechData = useCallback((): any | null => { try { const savedData = localStorage.getItem('speechSignUpData'); const timestamp = localStorage.getItem('speechSignUpTimestamp'); @@ -109,9 +111,10 @@ export function createSettingsHook(): () => GenericDataHook { return null; } }, []); + void _loadSpeechData; // Intentionally unused, reserved for future use // Fetch user data from API - const fetchUserData = useCallback(async () => { + const _fetchUserData = useCallback(async () => { if (!currentUser?.id) return null; try { @@ -122,9 +125,10 @@ export function createSettingsHook(): () => GenericDataHook { throw error; } }, [currentUser?.id, getUser]); + void _fetchUserData; // Intentionally unused, reserved for future use // Fetch field definitions from backend - const fetchFieldsForSection = useCallback(async (sectionId: string): Promise => { + const _fetchFieldsForSection = useCallback(async (sectionId: string): Promise => { try { setSettingsLoading(prev => ({ ...prev, [sectionId]: true })); setSettingsErrors(prev => ({ ...prev, [sectionId]: null })); @@ -148,6 +152,7 @@ export function createSettingsHook(): () => GenericDataHook { setSettingsLoading(prev => ({ ...prev, [sectionId]: false })); } }, [request]); + void _fetchFieldsForSection; // Intentionally unused, reserved for future use // Load all settings data const loadSettingsData = useCallback(async () => { diff --git a/src/hooks/useUsers.ts b/src/hooks/useUsers.ts index 9fec24e..96a78fb 100644 --- a/src/hooks/useUsers.ts +++ b/src/hooks/useUsers.ts @@ -125,14 +125,8 @@ export function useCurrentUser() { } try { - let logoutEndpoint = '/api/local/logout'; - // Determine the correct logout endpoint based on authentication authority - if (user.authenticationAuthority === 'msft') { - logoutEndpoint = '/api/msft/logout'; - } else if (user.authenticationAuthority === 'local') { - logoutEndpoint = '/api/local/logout'; - } + // Note: logoutEndpoint is determined by logoutUserApi based on authenticationAuthority await logoutUserApi(request, user.authenticationAuthority); @@ -498,13 +492,8 @@ export function useOrgUsers() { fieldType = 'string'; } } - // Legacy support for old format - else if (attr.type === 'boolean') { - fieldType = 'boolean'; - } else if (attr.type === 'enum' && attr.filterOptions) { - fieldType = 'enum'; - options = attr.filterOptions.map(opt => ({ value: opt, label: opt })); - } + // Note: Legacy 'boolean' and 'enum' types are not in the AttributeDefinition type union + // If needed, they should be handled via type casting: (attr as any).type === 'boolean' // Define validators and required fields let required = attr.required === true; @@ -547,7 +536,7 @@ export function useOrgUsers() { key: attr.name, label: attr.label || attr.name, type: fieldType, - editable: attr.editable !== false && attr.readonly !== true, + editable: (attr as any).editable !== false && (attr as any).readonly !== true, required, validator, 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); setEditingUsers(prev => new Set(prev).add(userId)); diff --git a/src/hooks/useWorkflows.ts b/src/hooks/useWorkflows.ts index fae8b12..2983701 100644 --- a/src/hooks/useWorkflows.ts +++ b/src/hooks/useWorkflows.ts @@ -19,7 +19,7 @@ import { MessageOverlay } from '../components/UiComponents'; import type { MessageMode } from '../components/UiComponents'; import { useLanguage } from '../providers/language/LanguageContext'; import { useWorkflowSelection } from '../contexts/WorkflowSelectionContext'; -import { getUserDataCache } from '../utils/userCache'; +// import { getUserDataCache } from '../utils/userCache'; // Unused import import { usePermissions, type UserPermissions } from './usePermissions'; // Workflow interface matching backend @@ -279,13 +279,8 @@ export function useUserWorkflows() { fieldType = 'string'; } } - // Legacy support for old format - else if (attr.type === 'boolean') { - fieldType = 'boolean'; - } else if (attr.type === 'enum' && attr.filterOptions) { - fieldType = 'enum'; - options = attr.filterOptions.map(opt => ({ value: opt, label: opt })); - } + // Note: Legacy 'boolean' and 'enum' types are not in the AttributeDefinition type union + // If needed, they should be handled via type casting: (attr as any).type === 'boolean' // Define validators and required fields let required = attr.required === true; @@ -360,7 +355,7 @@ export function useUserWorkflows() { // Listen for workflow creation events to refetch workflows list useEffect(() => { - const handleWorkflowCreated = (event: CustomEvent<{ workflow: UserWorkflow }>) => { + const handleWorkflowCreated = (_event: CustomEvent<{ workflow: UserWorkflow }>) => { // Refetch to ensure we have the latest data fetchWorkflowsData(); }; @@ -409,7 +404,7 @@ export function useWorkflowOperations() { const [warningData, setWarningData] = useState<{ header: string; message: string; mode: MessageMode } | null>(null); // Language context - const { t } = useLanguage(); + const { t: _t } = useLanguage(); // Workflow selection context - to clear selection if deleted workflow is selected 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); setEditingWorkflows(prev => new Set(prev).add(workflowId));