diff --git a/common/constants/explorer.ts b/common/constants/explorer.ts index 73a763d27..3a6d1e2ab 100644 --- a/common/constants/explorer.ts +++ b/common/constants/explorer.ts @@ -23,7 +23,7 @@ export const SELECTED_FIELDS = 'selectedFields'; export const UNSELECTED_FIELDS = 'unselectedFields'; export const AVAILABLE_FIELDS = 'availableFields'; export const QUERIED_FIELDS = 'queriedFields'; -export const TAB_ID_TXT_PFX = 'query-panel-'; +export const TAB_ID_TXT_PFX = 'explorer-tab-'; export const TAB_TITLE = 'New query'; export const TAB_CHART_TITLE = 'Visualizations'; export const TAB_EVENT_TITLE = 'Events'; @@ -299,4 +299,4 @@ export const UNITS_OF_MEASURE = [ 'farenheit (F)', 'meters (m)', 'kilometers (k)', -] +]; diff --git a/common/constants/shared.ts b/common/constants/shared.ts index f87dbf8c1..5a48e6b96 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -192,3 +192,8 @@ export const PLOT_MARGIN = { }; export const WAITING_TIME_ON_USER_ACTIONS = 300; + +export const VISUALIZATION_ERROR = { + NO_DATA: 'No data found.', + INVALID_DATA: 'Invalid visualization data', +}; diff --git a/public/components/common/search/search.tsx b/public/components/common/search/search.tsx index 7767898f2..2f30ab500 100644 --- a/public/components/common/search/search.tsx +++ b/public/components/common/search/search.tsx @@ -83,7 +83,6 @@ export const Search = (props: any) => { stopLive, setIsLiveTailPopoverOpen, liveTailName, - searchError = null, curVisId, setSubType, } = props; @@ -255,19 +254,6 @@ export const Search = (props: any) => { )} - {searchError && searchError.error && ( - - - -

{JSON.parse(searchError.message).error.details}

-
-
-
- )} {flyout} ); diff --git a/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap b/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap index df40c845c..6ef9af13e 100644 --- a/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap +++ b/public/components/event_analytics/explorer/__tests__/__snapshots__/data_grid.test.tsx.snap @@ -192,35 +192,35 @@ exports[`Datagrid component Renders data grid component 1`] = ` className="osdDocTableHeader" > double_per_ip_bytes host ip_count per_ip_bytes resp_code sum_bytes @@ -259,7 +259,7 @@ exports[`Datagrid component Renders data grid component 1`] = ` "timestamp": "2021-05-24 00:00:00", } } - docId="doc_view12" + docId="doc_view11" explorerFields={ Object { "availableFields": Array [ @@ -374,7 +374,7 @@ exports[`Datagrid component Renders data grid component 1`] = ` "unselectedFields": Array [], } } - key="doc_view12" + key="doc_view11" onFlyoutOpen={[Function]} selectedCols={ Array [ @@ -410,7 +410,7 @@ exports[`Datagrid component Renders data grid component 1`] = ` > , - "id": "doc_viewer_tab_2", + "id": "doc_viewer_tab_1", "name": "Table", } } @@ -60,7 +60,7 @@ exports[`Datagrid Doc viewer component Renders Doc viewer component 1`] = ` , - "id": "doc_viewer_tab_2", + "id": "doc_viewer_tab_1", "name": "Table", }, Object { @@ -82,7 +82,7 @@ exports[`Datagrid Doc viewer component Renders Doc viewer component 1`] = ` , - "id": "doc_viewer_tab_3", + "id": "doc_viewer_tab_2", "name": "JSON", }, Object { @@ -104,7 +104,7 @@ exports[`Datagrid Doc viewer component Renders Doc viewer component 1`] = ` , - "id": "doc_viewer_tab_4", + "id": "doc_viewer_tab_3", "name": Traces @@ -126,9 +126,9 @@ exports[`Datagrid Doc viewer component Renders Doc viewer component 1`] = ` > - - -
{ ); const { data, vis } = visualizations; const { userConfigs } = data; + const isAppAnalyticsConfigPanel = data.appData?.fromApp; const [vizConfigs, setVizConfigs] = useState({ dataConfig: {}, @@ -136,17 +137,26 @@ export const ConfigPanel = ({ visualizations, setCurVisId, callback }: any) => { spec: vizConfigs.layoutConfig, setToast, }, - availabilityConfig: { - visualizations, - curVisId, - onConfigChange: handleConfigChange('availabilityConfig'), - vizState: vizConfigs.availabilityConfig, - }, + ...(isAppAnalyticsConfigPanel && { + availabilityConfig: { + visualizations, + curVisId, + onConfigChange: handleConfigChange('availabilityConfig'), + vizState: vizConfigs.availabilityConfig, + }, + }), }; }, [visualizations, vizConfigs, setToast, curVisId]); const tabs: EuiTabbedContentTab[] = useMemo(() => { - return vis.editorconfig.panelTabs.map((tab: PanelTabType) => { + // hide availability for event analytics, only show availability in App Analytics + let tabsToRender: EuiTabbedContentTab[] = vis.editorconfig.panelTabs; + if (!isAppAnalyticsConfigPanel) { + tabsToRender = vis.editorconfig.panelTabs.filter( + (pnltb: PanelTabType) => pnltb.id !== 'availability-panel' + ); + } + return tabsToRender.map((tab: PanelTabType) => { const Editor = tab.editor; return { id: tab.id, @@ -212,6 +222,7 @@ export const ConfigPanel = ({ visualizations, setCurVisId, callback }: any) => { selectedOptions={[getSelectedVisDById(curVisId)]} singleSelection onChange={(visType) => { + handleDiscardConfig(); // currently reset all viz configurations setCurVisId(visType[0].id); }} fullWidth diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability.tsx b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability.tsx index 49ad1e221..102a4fe01 100644 --- a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability.tsx +++ b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability.tsx @@ -61,7 +61,7 @@ export const ConfigAvailability = ({ visualizations, onConfigChange, vizState = ); - const hasSpanInApp = + const isAvailabilityBtnVisible = visualizations.data.query.finalQuery.search(PPL_SPAN_REGEX) > 0 && visualizations.data.appData.fromApp && ['bar', 'line'].includes(visualizations.vis.id); @@ -124,7 +124,7 @@ export const ConfigAvailability = ({ visualizations, onConfigChange, vizState = data-test-subj="addAvailabilityButton" size="s" onClick={handleAddAvailability} - disabled={!hasSpanInApp} + disabled={!isAvailabilityBtnVisible} > {addButtonText} diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx index 971484b7d..b8ac5343d 100644 --- a/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx +++ b/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_configurations_panel.tsx @@ -284,7 +284,7 @@ export const DataConfigPanelItem = ({ }); handleQueryChange(newQueryString); getVisualizations({ - queryState: nextQueryState[FINAL_QUERY], + query: nextQueryState[FINAL_QUERY], callback: (res) => { updateVisUIState({ visData: res, @@ -386,19 +386,6 @@ export const DataConfigPanelItem = ({ )} {/* Show input fields for dimensions */} {!isAggregations && getCommonDimensionsField(selectedObj, name)} - {isPositionButtonVisible(name) && ( - - updateList(id, 'side')} - /> - - )}
diff --git a/public/components/event_analytics/explorer/visualizations/index.tsx b/public/components/event_analytics/explorer/visualizations/index.tsx index 26710cf02..11efb16f2 100644 --- a/public/components/event_analytics/explorer/visualizations/index.tsx +++ b/public/components/event_analytics/explorer/visualizations/index.tsx @@ -52,6 +52,7 @@ export const ExplorerVisualizations = ({ queryManager, }: IExplorerVisualizationsProps) => { const { vis } = visualizations; + const isMarkDown = vis.id === VIS_CHART_TYPES.Text; const fieldOptionList = explorerFields.availableFields.map((field) => ({ ...field, label: field.name, @@ -96,13 +97,13 @@ export const ExplorerVisualizations = ({ {(EuiResizablePanel, EuiResizableButton) => ( <> -
+
-
- {renderDataConfigContainer()} -
+ {!isMarkDown && ( +
+ {renderDataConfigContainer()} +
+ )}
{ }; const deleteHistoryList = async () => { - const objectIdsToDelete = selectedHistories.map((history) => history.data.objectId); + const objectIdsToDelete = selectedHistories.map((hstry) => hstry.data.objectId); await savedObjects .deleteSavedObjectsList({ objectIdList: objectIdsToDelete }) .then(async (res) => { @@ -143,7 +144,7 @@ export const Home = (props: IHomeProps) => { const addNewTab = async () => { // get a new tabId - const tabId = uniqueId(TAB_ID_TXT_PFX); + const tabId = htmlIdGenerator(TAB_ID_TXT_PFX)(); // create a new tab await batch(() => { @@ -279,7 +280,6 @@ export const Home = (props: IHomeProps) => { setToast(`Sample events added successfully.`); } catch (error: any) { setToast(`Cannot add sample events data, error: ${error}`, 'danger'); - console.error(error.body.message); } finally { setIsTableLoading(false); } diff --git a/public/components/metrics/sidebar/sidebar.scss b/public/components/metrics/sidebar/sidebar.scss index 9cba0fc76..b70951086 100644 --- a/public/components/metrics/sidebar/sidebar.scss +++ b/public/components/metrics/sidebar/sidebar.scss @@ -106,12 +106,7 @@ } #vis__mainContent { - .explorer__insights { - min-height: 0; - display: grid; - grid-template-columns: 50% 50%; - height: 100%; - + .explorer__insights, .explorer__configPanel-markdown { .explorerFieldSelector, .explorer__vizDataConfig { padding: $euiSizeS; @@ -145,6 +140,13 @@ } } +#vis__mainContent .explorer__insights { + min-height: 0; + display: grid; + grid-template-columns: 50% 50%; + height: 100%; +} + .sidebarHeight { line-height: normal; overflow: auto; diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap index bbbb71931..fe08d905c 100644 --- a/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap +++ b/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap @@ -529,75 +529,132 @@ exports[`Veritcal Bar component Renders veritcal bar component 1`] = ` } } > - -
- -
- -
- - - - - - -
- -

- Missing aggregations -

-
- -
- -
- - + id="explorerPlotComponent" + style={ + Object { + "height": "100%", + "width": "100%", + } + } + /> + + `; diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap index c8115cee2..e9a120c07 100644 --- a/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap +++ b/public/components/visualizations/charts/__tests__/__snapshots__/horizontal_bar.test.tsx.snap @@ -529,75 +529,132 @@ exports[`Horizontal bar component Renders horizontal bar component 1`] = ` } } > - -
- -
- -
- - - - - - -
- -

- Missing aggregations -

-
- -
- -
- - + id="explorerPlotComponent" + style={ + Object { + "height": "100%", + "width": "100%", + } + } + /> + + `; diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap index 9694c40d7..c87a070c8 100644 --- a/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap +++ b/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap @@ -529,75 +529,121 @@ exports[`Line component Renders line component 1`] = ` } } > - -
- -
- -
- - - - - - -
- -

- Missing mandatory series -

-
- -
- -
- - + id="explorerPlotComponent" + style={ + Object { + "height": "100%", + "width": "100%", + } + } + /> + + `; diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap index 1a1281d6e..cd618eefc 100644 --- a/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap +++ b/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap @@ -558,15 +558,15 @@ exports[`Logs View component Renders logs view component 1`] = ` className="osdDocTableHeader" > Time _source diff --git a/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap b/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap index 4bcc7e430..6a90d37ef 100644 --- a/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap +++ b/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap @@ -529,82 +529,121 @@ exports[`Pie component Renders pie component 1`] = ` } } > - -
- -
- -
- - - - - - -
- -

- - - No data found - - -

-
- -
- -
- - + id="explorerPlotComponent" + style={ + Object { + "height": "100%", + "width": "100%", + } + } + /> + + `; diff --git a/public/components/visualizations/charts/bar/bar.tsx b/public/components/visualizations/charts/bar/bar.tsx index a0aaaf38b..720063d31 100644 --- a/public/components/visualizations/charts/bar/bar.tsx +++ b/public/components/visualizations/charts/bar/bar.tsx @@ -98,24 +98,27 @@ export const Bar = ({ visualizations, layout, config }: any) => { const plotlyColorway = queriedVizData[fields[lastIndex].name].length < 16 ? PLOTLY_COLOR : [LONG_CHART_COLOR]; - if (isEmpty(series)) - return ; - const addStylesToTraces = (traces, traceStyles) => { - const { barOrientation, fillOpacity, tooltipMode, tooltipText, lineWidth } = traceStyles; + const { + barOrientation: barOrient, + fillOpacity: opac, + tooltipMode: tltpMode, + tooltipText: tltpText, + lineWidth: lwidth, + } = traceStyles; return traces.map((trace, idx: number) => { const selectedColor = getSelectedColorTheme(trace.aggName, idx); return { ...trace, type, - orientation: barOrientation, - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, + orientation: barOrient, + hoverinfo: tltpMode === 'hidden' ? 'none' : tltpText, ...{ marker: { - color: hexToRgb(selectedColor, fillOpacity), + color: hexToRgb(selectedColor, opac), line: { color: selectedColor, - width: lineWidth, + width: lwidth, }, }, }, diff --git a/public/components/visualizations/charts/lines/line.tsx b/public/components/visualizations/charts/lines/line.tsx index 8f34f59d0..bc06b2e98 100644 --- a/public/components/visualizations/charts/lines/line.tsx +++ b/public/components/visualizations/charts/lines/line.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { find, isEmpty, last, forEach } from 'lodash'; +import { isEmpty, last } from 'lodash'; import React, { useMemo } from 'react'; import { AGGREGATIONS, GROUPBY } from '../../../../../common/constants/explorer'; import { @@ -64,7 +64,6 @@ export const Line = ({ visualizations, layout, config }: any) => { const tooltipMode = tooltipOptions.tooltipMode !== undefined ? tooltipOptions.tooltipMode : 'show'; const tooltipText = tooltipOptions.tooltipText !== undefined ? tooltipOptions.tooltipText : 'all'; - const lastIndex = fields.length - 1; const visType: string = name; const mode = chartStyles.style || (visType === VIS_CHART_TYPES.Line ? DefaultModeLine : DefaultModeScatter); @@ -84,20 +83,23 @@ export const Line = ({ visualizations, layout, config }: any) => { (colorTheme.length > 0 && colorTheme.find((colorSelected) => colorSelected.name.name === field)?.color) || PLOTLY_COLOR[index % PLOTLY_COLOR.length]; - const timestampField = find(fields, (field) => field.type === 'timestamp'); - let xaxis = [timestampField]; - - if (isEmpty(series)) return ; const addStylesToTraces = (traces, traceStyles) => { - const { fillOpacity, tooltipMode, tooltipText, lineWidth, lineShape, markerSize } = traceStyles; + const { + fillOpacity: opac, + tooltipMode: tltpMode, + tooltipText: tltpText, + lineWidth: lwidth, + lineShape: lshape, + markerSize: mkrSize, + } = traceStyles; return traces.map((trace, idx: number) => { const selectedColor = getSelectedColorTheme(trace.aggName, idx); - const fillColor = hexToRgb(selectedColor, fillOpacity); + const fillColor = hexToRgb(selectedColor, opac); return { ...trace, - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, + hoverinfo: tltpMode === 'hidden' ? 'none' : tltpText, type: 'line', mode, ...{ @@ -105,17 +107,17 @@ export const Line = ({ visualizations, layout, config }: any) => { fillcolor: fillColor, }, line: { - shape: lineShape, - width: lineWidth, + shape: lshape, + width: lwidth, color: selectedColor, }, marker: { - size: markerSize, + size: mkrSize, ...{ color: fillColor, line: { color: selectedColor, - width: lineWidth, + width: lwidth, }, }, }, @@ -143,16 +145,7 @@ export const Line = ({ visualizations, layout, config }: any) => { transformPreprocessedDataToTraces(preprocessJsonData(jsonData, visConfig), visConfig), traceStyles ); - }, [ - chartStyles, - jsonData, - dimensions, - series, - span, - breakdowns, - panelOptions, - tooltipOptions, - ]); + }, [chartStyles, jsonData, dimensions, series, span, breakdowns, panelOptions, tooltipOptions]); const mergedLayout = useMemo(() => { const axisLabelsStyle = { diff --git a/public/components/visualizations/charts/pie/pie.tsx b/public/components/visualizations/charts/pie/pie.tsx index e6443b158..3b8fb2db1 100644 --- a/public/components/visualizations/charts/pie/pie.tsx +++ b/public/components/visualizations/charts/pie/pie.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { find, isEmpty, forEach } from 'lodash'; +import { find, forEach } from 'lodash'; import React, { useMemo } from 'react'; import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors'; import { @@ -18,7 +18,6 @@ import { ConfigListEntry, IVisualizationContainerProps, } from '../../../../../common/types/explorer'; -import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; import { getPropName, getTooltipHoverInfo } from '../../../event_analytics/utils/utils'; import { Plt } from '../../plotly/plot'; import { removeBacktick } from '../../../../../common/utils'; @@ -72,18 +71,15 @@ export const Pie = ({ visualizations, layout, config }: any) => { xaxes = dimensions; } - if (isEmpty(xaxes) || isEmpty(series)) { - return ; - } - const invertHex = (hex: string) => - (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase(); + (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase(); // eslint-disable-line no-bitwise const labelsOfXAxis = xaxes.reduce((prev, cur) => { if (backtickRemovedVisData[removeBacktick(cur.name)]) { if (prev.length === 0) return backtickRemovedVisData[removeBacktick(cur.name)].flat(); return prev.map( - (item: string | number, index: number) => `${item}, ${backtickRemovedVisData[removeBacktick(cur.name)][index]}` + (item: string | number, index: number) => + `${item}, ${backtickRemovedVisData[removeBacktick(cur.name)][index]}` ); } }, []); @@ -98,7 +94,11 @@ export const Pie = ({ visualizations, layout, config }: any) => { colorTheme.name !== DEFAULT_PALETTE ? { marker: { - colors: [...Array(backtickRemovedVisData[removeBacktick(fieldName)].length).fill(colorTheme.childColor)], + colors: [ + ...Array(backtickRemovedVisData[removeBacktick(fieldName)].length).fill( + colorTheme.childColor + ), + ], line: { color: hexColor, width: 1, diff --git a/public/components/visualizations/visualization.tsx b/public/components/visualizations/visualization.tsx index 63c342244..d737bc7bb 100644 --- a/public/components/visualizations/visualization.tsx +++ b/public/components/visualizations/visualization.tsx @@ -7,16 +7,55 @@ import React from 'react'; import { isEmpty } from 'lodash'; import { VisualizationChart } from './visualization_chart'; import { VisCanvassPlaceholder } from '../event_analytics/explorer/visualizations/shared_components'; +import { IVisualizationContainerProps } from '../../../common/types/explorer'; +import { VIS_CHART_TYPES, VISUALIZATION_ERROR } from '../../../common/constants/shared'; +import { AGGREGATIONS, GROUPBY } from '../../../common/constants/explorer'; -interface IVisualizationProps {} +export const Visualization = ({ + visualizations, +}: { + visualizations: IVisualizationContainerProps; +}) => { + const isVisDataValid = (vs: IVisualizationContainerProps) => { + const { + data: { + rawVizData: { data: queriedVizData }, + userConfigs: { + dataConfig: { span = {}, [GROUPBY]: dimensions = [], [AGGREGATIONS]: series = [] } = {}, + } = {}, + }, + vis = {}, + }: IVisualizationContainerProps = vs; + + // Markdown, it does not depend on if there is data + if (vis.id === VIS_CHART_TYPES.Text) return [true, '']; + + if (isEmpty(queriedVizData)) return [false, VISUALIZATION_ERROR.NO_DATA]; + + if (isEmpty(series)) return [false, VISUALIZATION_ERROR.INVALID_DATA]; // series is required to any visualization type + + // timeseries + if (vis.id === VIS_CHART_TYPES.Line && (isEmpty(span) || dimensions.length > 0)) + return [false, VISUALIZATION_ERROR.INVALID_DATA]; + + // bars, pie + if (dimensions.length < 1 && isEmpty(span)) return [false, VISUALIZATION_ERROR.INVALID_DATA]; + + // heatmap + if (vis.id === VIS_CHART_TYPES.HeatMap && (series.length !== 1 || dimensions.length !== 2)) + return [false, VISUALIZATION_ERROR.INVALID_DATA]; + + return [true, '']; + }; + + const [isValid, erroInfo] = isVisDataValid(visualizations); -export const Visualization = ({ visualizations }: IVisualizationProps) => { return ( <> - {!isEmpty(visualizations?.data?.rawVizData?.data) ? ( + {isValid ? ( ) : ( - + )} ); diff --git a/public/framework/redux/store/shared_state.ts b/public/framework/redux/store/shared_state.ts index 9cfce5351..ee041c9db 100644 --- a/public/framework/redux/store/shared_state.ts +++ b/public/framework/redux/store/shared_state.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { uniqueId } from 'lodash'; +import { htmlIdGenerator } from '@elastic/eui'; import { TAB_ID_TXT_PFX } from '../../../../common/constants/explorer'; -export const initialTabId: string = uniqueId(TAB_ID_TXT_PFX); +export const initialTabId: string = htmlIdGenerator(TAB_ID_TXT_PFX)();