import * as React from 'react'; import { AutoComplete, Input, Select } from 'antd'; import { SelectValue } from 'antd/lib/select'; import { OptionData } from 'rc-select/lib/interface/index'; import shallow from 'zustand/shallow'; import { validateQuery } from '../../actions/QueryActions'; import useApplicationConfigStore from '../../stores/ApplicationConfigStore'; import useErrorStore from '../../stores/ErrorStore'; import useQueryStore from '../../stores/QueryStore'; import { IntervalOptions, DefaultIntervals } from '../../types/QueryTypes'; import OspreyButton, { ButtonColors } from '../../uikit/OspreyButton'; import { TextSizes } from '../../uikit/Text'; import QueryIcon from '../../uikit/icons/QueryIcon'; import { filterAutoComplete, sortOptions } from '../../utils/AutoCompleteUtils'; import { getQueryDateRange, isEmptyDateRange, CUSTOM_RANGE_OPTION } from '../../utils/QueryUtils'; import IconHeader from '../common/IconHeader'; import QueryFilter from '../common/QueryFilter'; import OptionLabelWithInfo from '../common/OptionLabelWithInfo'; import QueryErrors from './QueryErrors'; import styles from './QueryInput.module.css'; const SelectOptions = [ ...Object.entries(IntervalOptions).map(([option, { label }]) => ({ value: option, label: label, })), { value: CUSTOM_RANGE_OPTION, label: 'Custom Range' }, ]; export interface QueryInputProps { interval: DefaultIntervals; dateRange: { start: string; end: string }; onIntervalChange: (interval: DefaultIntervals) => void; } const QueryInput = ({ interval: executedQueryInterval, onIntervalChange: onExecutedQueryIntervalChange, dateRange, }: QueryInputProps) => { const [executedQuery, updateExecutedQuery] = useQueryStore( (state) => [state.executedQuery, state.updateExecutedQuery], shallow ); const [knownFeatureNames, knownActionNames, ruleInfoMapping] = useApplicationConfigStore( (state) => [state.knownFeatureNames, state.knownActionNames, state.ruleInfoMapping], shallow ); const [queryFilter, setQueryFilter] = React.useState(executedQuery.queryFilter); const [searchOptions, setSearchOptions] = React.useState([]); const [filteredOptions, setFilteredOptions] = React.useState([]); // store the selected interval state separate from the current query's interval, so that changes // to the interval can be made without losing WIP in the query input field const [selectedInterval, setSelectedInterval] = React.useState(executedQueryInterval); const [isLoading, setIsLoading] = React.useState(false); const handleSelectChange = (value: DefaultIntervals) => { // only update the active query's interval if the user has not made any changes to their query input if (executedQuery.queryFilter === queryFilter) { onExecutedQueryIntervalChange(value); } setSelectedInterval(value); }; React.useEffect(() => { const options = [...knownFeatureNames, ...knownActionNames].map((option: string) => ({ value: option, label: , })); setSearchOptions(options); setFilteredOptions(options); }, [knownFeatureNames, knownActionNames]); React.useEffect(() => { setQueryFilter(executedQuery.queryFilter); // if the user has selected a new query to execute from the saved queries list, make sure that // we update the selected interval to reflect the correct one if (executedQuery.interval !== selectedInterval) { setSelectedInterval(executedQuery.interval); } }, [executedQuery, selectedInterval]); const handleSelectAutoComplete = (value: SelectValue) => { let insertValue = String(value); if (knownActionNames.has(insertValue)) { insertValue = `"${insertValue}"`; } const textAreaValueArr = queryFilter.split(' '); textAreaValueArr[textAreaValueArr.length - 1] = insertValue; const newTextAreaValue = textAreaValueArr.join(' '); setQueryFilter(newTextAreaValue); }; const clearErrors = useErrorStore((state) => state.clearErrors); const handleSubmitQuery = async () => { setIsLoading(true); clearErrors(); const isValid = queryFilter === '' ? true : await validateQuery(queryFilter); if (isValid) { const { start, end } = getQueryDateRange(selectedInterval, dateRange); updateExecutedQuery({ start, end, queryFilter, interval: selectedInterval }); } setIsLoading(false); }; const handleKeyPress = (e: React.KeyboardEvent) => { const ctrlOrMetaKey = e.ctrlKey || e.metaKey; if (ctrlOrMetaKey && e.key === 'Enter') { handleSubmitQuery(); } }; const handleTextAreaChange = (e: React.ChangeEvent) => { setQueryFilter(e.target.value); handleSearch(e.target.value); }; const handleSearch = (inputValue: string) => { const currentWord = String(inputValue.split(' ').slice(-1)); const filtered = searchOptions .filter((option) => filterAutoComplete(inputValue, option)) .sort(sortOptions(currentWord)); setFilteredOptions(filtered); }; return (
} title="Query" />
Submit Query
); }; export default QueryInput;