import React, { useState, useMemo } from 'react'; import { Loader2, X } from 'lucide-react'; import { useAllTables } from '@/features/tables/hooks/useAllTables'; import { useQuery } from '@tanstack/react-query'; import { apiClient } from '@/shared/utils/apiClient'; interface TicketsSourceConfig { tableId: number; tableName: string; tableIcon?: string; displayColumn?: string; descriptionColumn?: string; statusColumn?: string; priorityColumn?: string; statusDictTableId?: number; priorityDictTableId?: number; } // Column name aliases for auto-mapping const TITLE_ALIASES = ['title', 'what', 'name', 'subject']; const DESC_ALIASES = ['description', 'why', 'details', 'body']; const STATUS_ALIASES = ['state', 'status', 'task_status']; const PRIORITY_ALIASES = ['priority', 'urgency']; function findColumn( columns: Array<{ column_name: string; type: string; config?: string }>, aliases: string[] ): string | undefined { for (const alias of aliases) { const col = columns.find(c => c.column_name === alias); if (col) return col.column_name; } return undefined; } function parseDictTableId(config: string | undefined | null): number | undefined { if (!config) return undefined; try { const parsed: unknown = JSON.parse(config); if (parsed && typeof parsed === 'object' && 'relationTableId' in parsed) { return (parsed as { relationTableId: number }).relationTableId; } } catch { /* ignore */ } return undefined; } interface TicketsSourceInlineSelectorProps { defaultSpaceId?: number; onSelect: (config: TicketsSourceConfig) => void; onCancel: () => void; showHeader?: boolean; } /** * Inline selector for ticket/task source table * @deprecated Use TicketsSourceInlineSelector - this export kept for backwards compatibility */ export const TasksSourceInlineSelector = TicketsSourceInlineSelector; export function TicketsSourceInlineSelector({ defaultSpaceId, onSelect, onCancel, showHeader = true }: TicketsSourceInlineSelectorProps) { const [selectedProjectId, setSelectedProjectId] = useState(null); const [selectedTableId, setSelectedTableId] = useState(null); const { data: allTablesData, isLoading } = useAllTables(); // Fetch columns for the selected table (for auto-mapping) const { data: tableColumns = [] } = useQuery>({ queryKey: ['inline-selector-columns', selectedTableId], queryFn: async () => { const response = await apiClient.get<{ success: boolean; data: Array<{ column_name: string; display_name: string; type: string; config?: string }>; }>(`/tables/${selectedTableId}/columns`); return response.success ? (response.data || []) : []; }, enabled: !!selectedTableId, staleTime: 5 * 60_000 }); // Filter spaces const filteredSpaces = useMemo(() => { if (!allTablesData?.spacesWithTables) return []; if (!defaultSpaceId) return allTablesData.spacesWithTables; return allTablesData.spacesWithTables.filter(s => s.id === defaultSpaceId); }, [allTablesData, defaultSpaceId]); // Get tables for selected project const projectTables = useMemo(() => { if (!selectedProjectId || !allTablesData?.spacesWithTables) return []; for (const space of allTablesData.spacesWithTables) { const project = space.projects.find(p => p.id === selectedProjectId); if (project) return project.tables || []; } return []; }, [selectedProjectId, allTablesData]); // Get selected table info const selectedTable = useMemo(() => { if (!selectedTableId) return null; return allTablesData?.flat?.find(t => t.id === String(selectedTableId)); }, [selectedTableId, allTablesData]); // Auto-mapped column info (shown as preview) const autoMapped = useMemo(() => { if (!tableColumns.length) return null; const titleCol = findColumn(tableColumns, TITLE_ALIASES); const descCol = findColumn(tableColumns, DESC_ALIASES); const statusCol = findColumn(tableColumns, STATUS_ALIASES); const priorityCol = findColumn(tableColumns, PRIORITY_ALIASES); if (!titleCol && !descCol && !statusCol && !priorityCol) return null; const statusColInfo = statusCol ? tableColumns.find(c => c.column_name === statusCol) : undefined; const priorityColInfo = priorityCol ? tableColumns.find(c => c.column_name === priorityCol) : undefined; return { displayColumn: titleCol, descriptionColumn: descCol, statusColumn: statusCol, priorityColumn: priorityCol, statusDictTableId: parseDictTableId(statusColInfo?.config), priorityDictTableId: parseDictTableId(priorityColInfo?.config), }; }, [tableColumns]); const handleSave = () => { if (!selectedTable) return; onSelect({ tableId: Number(selectedTable.id), tableName: selectedTable.displayName || selectedTable.name, tableIcon: selectedTable.icon, displayColumn: autoMapped?.displayColumn || selectedTable.displayField, descriptionColumn: autoMapped?.descriptionColumn, statusColumn: autoMapped?.statusColumn, priorityColumn: autoMapped?.priorityColumn, statusDictTableId: autoMapped?.statusDictTableId, priorityDictTableId: autoMapped?.priorityDictTableId, }); }; if (isLoading) { return (
); } return (
{/* Header with close button */} {showHeader && (
)} {/* Project selector with label if no header */} {!showHeader && } {/* Project selector */} {/* Table selector */}
{/* Auto-mapped columns preview */} {autoMapped && (

Автомаппинг:

{autoMapped.displayColumn &&

Название: {autoMapped.displayColumn}

} {autoMapped.descriptionColumn &&

Описание: {autoMapped.descriptionColumn}

} {autoMapped.statusColumn &&

Статус: {autoMapped.statusColumn}

} {autoMapped.priorityColumn &&

Приоритет: {autoMapped.priorityColumn}

}
)} {/* Save button only */}
); }