import React from 'react'; import { CircleExclamationFill, CircleInfoFill, CircleXmarkFill, TriangleExclamationFill, } from '@gravity-ui/icons'; import type {IconData} from '@gravity-ui/uikit'; import {ArrowToggle, Button, Flex, Icon, Link} from '@gravity-ui/uikit'; import ShortyString from '../../../../components/ShortyString/ShortyString'; import type {ErrorResponse, IssueMessage} from '../../../../types/api/query'; import {cn} from '../../../../utils/cn'; import {isNumeric} from '../../../../utils/utils'; import {IssuesDialog} from './IssuesDialog'; import { QUERY_SETTINGS_PRAGMAS_SOURCE, getIssuePosition, getMostSevere, hasRootIssues, normalizeRoots, } from './helpers'; import i18n from './i18n'; import type {SEVERITY} from './models'; import {getSeverity} from './models'; import './Issues.scss'; const blockWrapper = cn('kv-result-issues'); const blockIssues = cn('kv-issues'); const blockIssue = cn('kv-issue'); interface ResultIssuesProps { data: ErrorResponse | string; hideSeverity?: boolean; } function ErrorStringMessage({message}: {message: string}) { return
{message}
; } export function ResultIssues({data, hideSeverity}: ResultIssuesProps) { const roots = normalizeRoots(data); const [expanded, setExpanded] = React.useState>({}); const onToggleInline = (idx: number) => setExpanded((p) => ({...p, [idx]: !p[idx]})); if (typeof data === 'string') { return ; } return (
{roots.map((root, idx) => { const hasIssues = hasRootIssues(root.issues); return ( onToggleInline(idx)} /> {expanded[idx] && hasIssues && ( )} ); })}
); } export function ResultIssuesModal({data, hideSeverity}: ResultIssuesProps) { const roots = normalizeRoots(data); const [open, setOpen] = React.useState(false); const [currentIssues, setCurrentIssues] = React.useState(null); const openDialog = (issues?: IssueMessage[]) => { setCurrentIssues(issues ?? []); setOpen(true); }; const closeDialog = () => setOpen(false); if (typeof data === 'string') { return ; } return (
{roots.map((root, idx) => { const hasIssues = hasRootIssues(root.issues); return ( openDialog(root.issues)} /> ); })}
); } interface ErrorPreviewItemProps { severity: SEVERITY; message?: string; hideSeverity?: boolean; hasIssues?: boolean; expanded?: boolean; onClick: () => void; } export function ErrorPreviewItem({ severity, message, hideSeverity, hasIssues, expanded, onClick, }: ErrorPreviewItemProps) { const buttonLabel = expanded ? i18n('action_hide-details') : i18n('action_show-details'); return (
{hideSeverity ? null : } {message} {hasIssues && ( )}
); } interface IssuesProps { issues: IssueMessage[] | null | undefined; hideSeverity?: boolean; } export function Issues({issues, hideSeverity}: IssuesProps) { const mostSevereIssue = getMostSevere(issues); return (
{issues?.map((issue, index) => ( ))}
); } function Issue({ issue, hideSeverity, level = 0, }: { issue: IssueMessage; expanded?: boolean; hideSeverity?: boolean; level?: number; }) { const [isExpand, setIsExpand] = React.useState(true); const severity = getSeverity(issue.severity); const issues = issue.issues; const hasIssues = hasRootIssues(issues); const arrowDirection = isExpand ? 'bottom' : 'right'; return (
{hasIssues && ( )} {hideSeverity ? null : } {issue.issue_code ? ( {i18n('field_code')}: {issue.issue_code} ) : null}
{hasIssues && isExpand && (
)}
); } interface IssueTextProps { issue: IssueMessage; } function IssueText({issue}: IssueTextProps) { const position = getIssuePosition(issue); const ydbEditor = window.ydbEditor; const getIssue = () => { return ( {position && ( {position} )}
); }; const {row, column} = issue.position ?? {}; const isIssueClickable = isNumeric(row) && issue.position?.file !== QUERY_SETTINGS_PRAGMAS_SOURCE && ydbEditor; if (!isIssueClickable) { return getIssue(); } const onIssueClickHandler = () => { const monacoPosition = {lineNumber: row, column: column ?? 0}; ydbEditor.setPosition(monacoPosition); ydbEditor.revealPositionInCenterIfOutsideViewport(monacoPosition); ydbEditor.focus(); }; return ( {getIssue()} ); } function IssueList(props: {issues: IssueMessage[]; expanded: boolean; level: number}) { const {issues, level, expanded} = props; return (
{issues.map((issue, index) => ( ))}
); } const severityIcons: Record = { S_INFO: CircleInfoFill, S_WARNING: CircleExclamationFill, S_ERROR: TriangleExclamationFill, S_FATAL: CircleXmarkFill, }; const blockIssueSeverity = cn('yql-issue-severity'); function IssueSeverity({severity}: {severity: SEVERITY}) { const shortenSeverity = severity.slice(2).toLowerCase(); return ( {shortenSeverity} ); }