sozsoft-platform/ui/src/views/developerKit/SqlResultsGrid.tsx

125 lines
4.4 KiB
TypeScript
Raw Normal View History

2026-02-24 20:44:16 +00:00
import { useMemo } from 'react'
import { DataGrid } from 'devextreme-react'
import { Column, Paging, Scrolling, SearchPanel, Export, Selection } from 'devextreme-react/data-grid'
import type { SqlQueryExecutionResultDto } from '@/proxy/sql-query-manager/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { FaTimesCircle } from 'react-icons/fa'
interface SqlResultsGridProps {
result: SqlQueryExecutionResultDto
}
const SqlResultsGrid = ({ result }: SqlResultsGridProps) => {
const { translate } = useLocalization()
const columns = useMemo(() => {
// Get columns from metadata if available
if (result.metadata?.columns && Array.isArray(result.metadata.columns)) {
return result.metadata.columns
}
// Otherwise infer from data
if (result.data && result.data.length > 0) {
const firstRow = result.data[0]
return Object.keys(firstRow).map((key) => ({
name: key,
dataType: typeof firstRow[key],
isNullable: true,
}))
}
return []
}, [result])
const dataSource = useMemo(() => {
return result.data || []
}, [result])
if (!result.success) {
return (
<div className="h-full flex flex-col">
<div className="flex items-center gap-2 mb-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded">
2026-05-19 20:22:25 +00:00
<FaTimesCircle className="text-red-500 dark:text-red-400 text-xl" />
2026-02-24 20:44:16 +00:00
<div>
<div className="font-semibold text-red-700 dark:text-red-400">
{translate('::App.Platform.Error')}
</div>
<div className="text-sm text-red-600 dark:text-red-500">
{result.error || result.message}
</div>
</div>
</div>
</div>
)
}
return (
<div className="h-full flex flex-col">
{dataSource.length > 0 ? (
<div className="flex-1 overflow-hidden relative">
<div className="absolute inset-0">
<DataGrid
dataSource={dataSource}
showBorders={true}
showRowLines={true}
showColumnLines={true}
rowAlternationEnabled={true}
columnAutoWidth={true}
wordWrapEnabled={false}
allowColumnReordering={true}
allowColumnResizing={true}
columnResizingMode="widget"
height="100%"
>
<Scrolling mode="virtual" rowRenderingMode="virtual" />
<Paging enabled={true} pageSize={50} />
<SearchPanel visible={true} width={240} placeholder={translate('::App.Platform.Search')} />
<Export enabled={true} allowExportSelectedData={true} />
<Selection mode="multiple" showCheckBoxesMode="always" />
{columns.map((col, index) => (
<Column
key={col.name || index}
dataField={col.name}
caption={col.name}
dataType={
col.dataType?.toLowerCase().includes('int') ||
col.dataType?.toLowerCase().includes('decimal') ||
col.dataType?.toLowerCase().includes('numeric') ||
col.dataType?.toLowerCase().includes('float') ||
col.dataType?.toLowerCase().includes('money')
? 'number'
: col.dataType?.toLowerCase().includes('date') ||
col.dataType?.toLowerCase().includes('time')
? 'date'
: col.dataType?.toLowerCase().includes('bit') ||
col.dataType?.toLowerCase().includes('boolean')
? 'boolean'
: 'string'
}
allowSorting={true}
allowFiltering={true}
allowHeaderFiltering={true}
/>
))}
</DataGrid>
</div>
</div>
) : (
<div className="flex-1 flex items-center justify-center text-gray-500">
<div className="text-center">
<div className="text-lg mb-2">{translate('::App.Platform.NoResults')}</div>
<div className="text-sm">
{result.rowsAffected > 0
? translate('::App.Platform.RowCount', { count: result.rowsAffected })
: translate('::App.Platform.NoResultsReturned')}
</div>
</div>
</div>
)}
</div>
)
}
export default SqlResultsGrid