407 lines
17 KiB
TypeScript
407 lines
17 KiB
TypeScript
import React from 'react';
|
|
import { IconType } from 'react-icons';
|
|
import { DragDropConfig } from '../../components/UiComponents/DragDropOverlay/DragDropOverlay';
|
|
|
|
// Generic privilege checker function type
|
|
export type PrivilegeChecker = () => boolean | Promise<boolean>;
|
|
|
|
// Form field configuration for create/edit buttons
|
|
export interface ButtonFormField {
|
|
key: string;
|
|
label: string | LanguageText;
|
|
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
|
|
export interface DropdownConfig<T = any> {
|
|
type: 'dropdown';
|
|
items: Array<{
|
|
id: string | number;
|
|
label: string | LanguageText;
|
|
value: T;
|
|
metadata?: Record<string, any>;
|
|
}>;
|
|
selectedItemId?: string | number | null;
|
|
onSelect: (item: { id: string | number; label: string | LanguageText; value: T; metadata?: Record<string, any> } | null, hookData?: any) => void | Promise<void>;
|
|
placeholder?: string | LanguageText;
|
|
emptyMessage?: string | LanguageText;
|
|
headerText?: string | LanguageText;
|
|
// Optional: name of property in hookData that provides items, selectedItemId, and onSelect
|
|
dataSource?: {
|
|
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
|
|
};
|
|
}
|
|
|
|
// Button configuration for header actions
|
|
export interface PageButton {
|
|
id: string;
|
|
label: string | LanguageText;
|
|
variant?: 'primary' | 'secondary' | 'danger' | 'success' | 'warning';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
icon?: IconType;
|
|
onClick?: (hookData?: any) => void | Promise<void>;
|
|
disabled?: boolean | ((hookData?: any) => boolean | { disabled: boolean; message?: string });
|
|
// Form configuration for create buttons
|
|
formConfig?: {
|
|
fields: ButtonFormField[];
|
|
popupTitle?: string | LanguageText;
|
|
popupSize?: 'small' | 'medium' | 'large';
|
|
createOperationName?: string; // Name of the create operation in hookData (e.g., 'handlePromptCreate')
|
|
successMessage?: string | LanguageText;
|
|
errorMessage?: string | LanguageText;
|
|
multiStep?: boolean; // Enable multi-step form mode
|
|
};
|
|
// Dropdown configuration for dropdown selection buttons
|
|
dropdownConfig?: DropdownConfig;
|
|
}
|
|
|
|
// Input form configuration
|
|
export interface InputFormConfig {
|
|
hookFactory?: () => () => GenericDataHook; // Optional, can use page-level hook from tableConfig if exists
|
|
placeholder?: string | LanguageText;
|
|
buttonLabel?: string | LanguageText;
|
|
stopButtonLabel?: string | LanguageText; // Label for stop button when workflow is running
|
|
buttonIcon?: IconType;
|
|
stopButtonIcon?: IconType; // Icon for stop button when workflow is running
|
|
buttonVariant?: 'primary' | 'secondary' | 'danger' | 'success' | 'warning';
|
|
stopButtonVariant?: 'primary' | 'secondary' | 'danger' | 'success' | 'warning'; // Variant for stop button
|
|
buttonSize?: 'sm' | 'md' | 'lg';
|
|
textFieldSize?: 'sm' | 'md' | 'lg';
|
|
}
|
|
|
|
// Settings field configuration
|
|
export interface SettingsFieldConfig {
|
|
id: string;
|
|
type: 'text' | 'select' | 'toggle';
|
|
label: string | LanguageText;
|
|
description?: string | LanguageText;
|
|
required?: boolean;
|
|
disabled?: boolean | ((data: any) => boolean);
|
|
inputType?: 'text' | 'email' | 'tel';
|
|
placeholder?: string | LanguageText;
|
|
options?: Array<{id: string | number; label: string | LanguageText; value: any}>;
|
|
dataKey: string; // Dot-notation path (e.g., "mandate_general.company_name")
|
|
onSave?: (value: any, data: any) => Promise<void> | void;
|
|
}
|
|
|
|
// Settings section configuration
|
|
export interface SettingsSectionConfig {
|
|
id: string;
|
|
title: string | LanguageText;
|
|
description?: string | LanguageText;
|
|
icon?: IconType; // Optional icon for section header
|
|
// Fields will be loaded from backend based on sectionId
|
|
sectionId: string; // Identifier sent to backend to fetch fields
|
|
// Save handler per section (uses Button component)
|
|
onSave?: (sectionId: string, data: any) => Promise<void>;
|
|
saveButtonLabel?: string | LanguageText;
|
|
saveButtonVariant?: 'primary' | 'secondary' | 'danger' | 'success' | 'warning';
|
|
saveButtonSize?: 'sm' | 'md' | 'lg';
|
|
// Optional: Static field overrides from config (takes precedence over backend)
|
|
staticFields?: SettingsFieldConfig[];
|
|
// Optional: Conditional rendering - if provided, section only renders if condition returns true
|
|
renderCondition?: (formData: any) => boolean;
|
|
// Optional: Alternative content to render when renderCondition returns false
|
|
renderAlternative?: (formData: any, t: (key: string) => string, resolveLanguageText: (text: string | LanguageText, t: (key: string) => string) => string) => React.ReactNode;
|
|
}
|
|
|
|
// Settings configuration
|
|
export interface SettingsConfig {
|
|
sections: SettingsSectionConfig[];
|
|
hookFactory?: () => () => GenericDataHook; // For data fetching and field loading
|
|
}
|
|
|
|
// Content section for paragraphs
|
|
export interface PageContent {
|
|
id: string;
|
|
type: 'paragraph' | 'heading' | 'list' | 'code' | 'divider' | 'custom' | 'table' | 'inputForm' | 'messages' | 'settings' | 'log' | 'tabs' | 'columns';
|
|
content?: string | LanguageText; // Optional for dividers
|
|
level?: number; // For headings (1-6)
|
|
items?: (string | LanguageText)[]; // For lists
|
|
language?: string; // For code blocks
|
|
customComponent?: React.ComponentType<any>;
|
|
// Table-specific properties
|
|
tableConfig?: TableContentConfig;
|
|
// Input form-specific properties
|
|
inputFormConfig?: InputFormConfig;
|
|
// Messages-specific properties
|
|
messagesConfig?: {
|
|
variant?: 'chat' | 'log'; // Message display variant: 'chat' for bubble UI, 'log' for list/table UI
|
|
dataSource?: 'messages' | 'logs' | 'both'; // Data source to render: 'messages' (default), 'logs', or 'both' (logs use log variant when merged)
|
|
showDocuments?: boolean;
|
|
showMetadata?: boolean;
|
|
showProgress?: boolean;
|
|
emptyMessage?: string | LanguageText;
|
|
};
|
|
// Settings-specific properties
|
|
settingsConfig?: SettingsConfig;
|
|
// Log-specific properties
|
|
logConfig?: {
|
|
emptyMessage?: string | LanguageText;
|
|
};
|
|
// Tabs-specific properties
|
|
tabsConfig?: {
|
|
tabs: Array<{
|
|
id: string;
|
|
label: string | LanguageText;
|
|
content: PageContent[]; // Nested content sections for each tab
|
|
}>;
|
|
defaultTabId?: string;
|
|
};
|
|
// Columns-specific properties
|
|
columnsConfig?: {
|
|
columns: Array<{
|
|
id: string;
|
|
width?: string; // CSS width (e.g., "3fr", "1fr", "75%", "25%")
|
|
content: PageContent[]; // Nested content sections for each column
|
|
}>;
|
|
gap?: string; // CSS gap value
|
|
};
|
|
}
|
|
|
|
// Generic hook interface for data fetching
|
|
export interface GenericDataHook {
|
|
data: any[];
|
|
loading: boolean;
|
|
isRefetching?: boolean; // True when refetching data (keeps existing data visible)
|
|
error: string | null;
|
|
refetch?: (params?: { page?: number; pageSize?: number; sort?: Array<{field: string; direction: 'asc' | 'desc'}>; filters?: any; search?: string }) => Promise<void>;
|
|
pagination?: {
|
|
currentPage: number;
|
|
pageSize: number;
|
|
totalItems: number;
|
|
totalPages: number;
|
|
sort?: Array<{ field: string; direction: 'asc' | 'desc' }>;
|
|
filters?: any;
|
|
} | null;
|
|
removeFileOptimistically?: (fileId: string) => void; // For optimistic updates
|
|
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<boolean>; // For file download 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
|
|
// 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
|
|
onDelete?: (row: any) => Promise<void>; // For single item deletion
|
|
onDeleteMultiple?: (rows: any[]) => Promise<void>; // For multiple item deletion
|
|
// Input form operations
|
|
inputValue?: string;
|
|
onInputChange?: (value: string) => void;
|
|
handleSubmit?: () => Promise<void>; // 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<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
|
|
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
|
|
settingsData?: any; // Unified data object for settings fields
|
|
settingsFields?: Record<string, SettingsFieldConfig[]>; // Field definitions per sectionId
|
|
settingsLoading?: Record<string, boolean>; // Loading state per section
|
|
settingsErrors?: Record<string, string | null>; // Error state per 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
|
|
export interface ActionButtonConfig {
|
|
type: 'view' | 'edit' | 'download' | 'delete' | 'copy' | 'connect' | 'play';
|
|
onAction?: (row: any) => Promise<void> | void; // Optional for delete buttons since they handle their own logic
|
|
title?: string | LanguageText;
|
|
disabled?: (row: any) => boolean | { disabled: boolean; message?: string };
|
|
loading?: (row: any) => boolean;
|
|
// Field mappings for flexible data access
|
|
idField?: string; // Field name for the unique identifier (default: 'id')
|
|
nameField?: string; // Field name for display name (default: 'name' or 'file_name')
|
|
typeField?: string; // Field name for type/mime type (default: 'type' or 'mime_type')
|
|
contentField?: string; // Field name for content (default: 'content')
|
|
statusField?: string; // Field name for status (default: 'status')
|
|
authorityField?: string; // Field name for authority (msft/google) (default: 'authority')
|
|
// Operation and loading state names
|
|
operationName?: string; // Name of the operation function in hookData
|
|
disconnectOperationName?: string; // Name of the disconnect operation function in hookData (for connect button)
|
|
refreshOperationName?: string; // Name of the refresh operation function in hookData (for connect button)
|
|
loadingStateName?: string; // Name of the loading state in hookData
|
|
fetchItemFunctionName?: string; // Name of the function in hookData to fetch a single item by ID (for edit button)
|
|
// Navigation and behavior (for play button)
|
|
navigateTo?: string; // Path to navigate to after action (default: 'start/dashboard')
|
|
mode?: 'workflow' | 'prompt'; // Behavior mode for play button: 'workflow' selects workflow, 'prompt' sets input value (default: 'prompt')
|
|
}
|
|
|
|
// Table content configuration
|
|
export interface TableContentConfig {
|
|
hookFactory: () => () => GenericDataHook; // Hook factory that returns a hook function
|
|
columns?: any[]; // Column configuration (optional - can be generated dynamically from attributes via hookData.columns)
|
|
actionButtons?: ActionButtonConfig[]; // Action buttons configuration
|
|
searchable?: boolean;
|
|
filterable?: boolean;
|
|
sortable?: boolean;
|
|
resizable?: boolean;
|
|
pagination?: boolean;
|
|
pageSize?: number;
|
|
className?: string;
|
|
emptyMessage?: string; // Custom message to display when table is empty
|
|
}
|
|
|
|
// Language-aware text interface
|
|
export interface LanguageText {
|
|
de: string;
|
|
en: string;
|
|
fr: string;
|
|
}
|
|
|
|
// Utility function to resolve language text
|
|
export const resolveLanguageText = (text: string | LanguageText | undefined, t?: (key: string, fallback?: string) => string): string => {
|
|
if (!text) return '';
|
|
if (typeof text === 'string') {
|
|
// Always use the translation function for strings (language keys)
|
|
if (t) {
|
|
return t(text);
|
|
}
|
|
return text;
|
|
}
|
|
// For LanguageText objects, we should convert them to language keys
|
|
// For now, fallback to the first available language
|
|
return text.de || text.en || text.fr || '';
|
|
};
|
|
|
|
// Generic page data interface
|
|
export interface GenericPageData {
|
|
// Core identification
|
|
id: string;
|
|
path: string;
|
|
name: string;
|
|
description?: string | LanguageText;
|
|
|
|
// Navigation
|
|
parentPath?: string; // For subpages/subsubpages
|
|
order?: number;
|
|
showInSidebar?: boolean;
|
|
|
|
// Visual
|
|
icon?: IconType;
|
|
title: string | LanguageText;
|
|
subtitle?: string | LanguageText;
|
|
|
|
// Header configuration
|
|
headerButtons?: PageButton[];
|
|
|
|
// Content sections
|
|
content?: PageContent[];
|
|
|
|
// Page behavior
|
|
persistent?: boolean;
|
|
preserveState?: boolean;
|
|
preload?: boolean;
|
|
moduleEnabled?: boolean;
|
|
hide?: boolean; // If true, page is completely hidden and not rendered
|
|
|
|
// Subpage support
|
|
hasSubpages?: boolean;
|
|
|
|
// Lifecycle hooks
|
|
onActivate?: () => void | Promise<void>;
|
|
onDeactivate?: () => void | Promise<void>;
|
|
onLoad?: () => void | Promise<void>;
|
|
onUnload?: () => void | Promise<void>;
|
|
|
|
// Custom component override (optional)
|
|
customComponent?: React.ComponentType<any>;
|
|
|
|
// Drag and drop configuration
|
|
dragDropConfig?: DragDropConfig;
|
|
}
|
|
|
|
// Page data file structure
|
|
export interface PageDataFile {
|
|
page: GenericPageData;
|
|
subpages?: PageDataFile[];
|
|
}
|
|
|
|
// Sidebar item interface for compatibility
|
|
export interface SidebarItem {
|
|
id: string;
|
|
name: string;
|
|
link: string | undefined; // Allow undefined for parent groups that aren't clickable pages
|
|
icon?: IconType | React.ComponentType<React.SVGProps<SVGSVGElement>>; // Allow both IconType and SVG components
|
|
moduleEnabled: boolean;
|
|
order: number;
|
|
submenu?: SidebarSubmenuItemData[];
|
|
}
|
|
|
|
// Sidebar submenu item data interface
|
|
export interface SidebarSubmenuItemData {
|
|
id: string;
|
|
name: string;
|
|
link: string;
|
|
icon?: IconType;
|
|
}
|
|
|
|
// Page instance for PageManager
|
|
export interface PageInstance {
|
|
path: string;
|
|
component: React.ReactElement;
|
|
isActive: boolean;
|
|
shouldPreserve: boolean;
|
|
pageData: GenericPageData;
|
|
}
|
|
|
|
// Page manager props
|
|
export interface PageManagerProps {
|
|
loadingComponent: React.ComponentType;
|
|
errorComponent: React.ComponentType;
|
|
}
|