/** * View Designer — Criteria grid (SSMS Query Designer'in orta bolumu). * * Sutunlar: Column · Alias · Table · Output · Group By · Sort Type · Sort Order * · Filter · Or... Filtre hucreleri serbest yuklemdir ("> 100", "LIKE '%a%'"); * yaninda kucuk bir yardimci ile de doldurulabilir. */ import { useState } from 'react' import { FaCode, FaGripVertical, FaMagic, FaPlus, FaTrash } from 'react-icons/fa' import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models' import { GROUP_MODES, GROUP_MODE_LABEL, isAggregateMode, orColumnCount, quoteLiteral, type CriteriaRow, type GroupMode, type ViewSource, } from './viewModel' interface CriteriaGridProps { rows: CriteriaRow[] sources: ViewSource[] columnsOf: (sourceId: string) => DatabaseColumnDto[] grouped: boolean onUpdateRow: (id: string, patch: Partial) => void onUpdateFilter: (id: string, index: number, value: string) => void onRemoveRow: (id: string) => void onMoveRow: (id: string, delta: number) => void onAddRow: () => void onAddExpression: () => void labels: { column: string alias: string table: string output: string groupBy: string sortType: string sortOrder: string filter: string or: string addRow: string addExpression: string expressionPlaceholder: string empty: string builder: string builderValue: string builderLiteral: string builderExpression: string apply: string } } const OPERATORS = [ '=', '<>', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'IS NULL', 'IS NOT NULL', 'BETWEEN', ] export function CriteriaGrid({ rows, sources, columnsOf, grouped, onUpdateRow, onUpdateFilter, onRemoveRow, onMoveRow, onAddRow, onAddExpression, labels, }: CriteriaGridProps) { const [builder, setBuilder] = useState<{ rowId: string; index: number } | null>(null) const orCount = orColumnCount(rows) const cellCls = 'w-full rounded border border-transparent bg-transparent px-1 py-0.5 text-xs hover:border-gray-300 focus:border-blue-500 focus:bg-white dark:text-gray-100 dark:hover:border-gray-600 dark:focus:bg-gray-700' return (
{rows.length}
{grouped && ( )} {Array.from({ length: orCount }).map((_, i) => ( ))} {rows.length === 0 && ( )} {rows.map((row) => { const isAgg = grouped && isAggregateMode(row.groupMode) const isExpression = row.expression !== undefined return ( {grouped && ( )} {Array.from({ length: orCount }).map((_, i) => ( ))} ) })}
{labels.column} {labels.alias} {labels.table} {labels.output} {labels.groupBy} {labels.sortType} {labels.sortOrder} {i === 0 ? labels.filter : labels.or}
{labels.empty}
{isExpression ? ( onUpdateRow(row.id, { expression: e.target.value })} /> ) : ( )} onUpdateRow(row.id, { alias: e.target.value })} /> {isExpression ? ( SQL ) : ( )} onUpdateRow(row.id, { output: e.target.checked })} /> onUpdateRow(row.id, { sortOrder: Number(e.target.value) || 0 }) } />
100 · = 'A' · IS NULL" : ''} value={row.filters[i] ?? ''} onChange={(e) => onUpdateFilter(row.id, i, e.target.value)} />
{builder?.rowId === row.id && builder.index === i && ( { onUpdateFilter(row.id, i, text) setBuilder(null) }} onClose={() => setBuilder(null)} /> )} {isAgg && (row.filters[i] ?? '').trim() && ( HAVING )}
) } // ─── Filtre yardimcisi ──────────────────────────────────────────────────────── function FilterBuilder({ labels, onApply, onClose, }: { labels: CriteriaGridProps['labels'] onApply: (text: string) => void onClose: () => void }) { const [operator, setOperator] = useState('=') const [value, setValue] = useState('') const [value2, setValue2] = useState('') const [mode, setMode] = useState<'literal' | 'expression'>('literal') const needsValue = operator !== 'IS NULL' && operator !== 'IS NOT NULL' const build = () => { if (!needsValue) return operator const render = (v: string) => (mode === 'literal' ? quoteLiteral(v.trim()) : v.trim()) if (operator === 'BETWEEN') return `BETWEEN ${render(value)} AND ${render(value2)}` if (operator === 'IN' || operator === 'NOT IN') { if (mode === 'expression') return `${operator} (${value.trim()})` const items = value .split(',') .map((v) => v.trim()) .filter(Boolean) .map(quoteLiteral) return `${operator} (${items.join(', ')})` } return `${operator} ${render(value)}` } return (
e.stopPropagation()} > {needsValue && ( <> setValue(e.target.value)} /> {operator === 'BETWEEN' && ( setValue2(e.target.value)} /> )} )}
) } export default CriteriaGrid