Skip to content

Commit

Permalink
Merge branch 'main' into astandrik.enable-autorefresh-for-paginated-t…
Browse files Browse the repository at this point in the history
…ables-1070-2
  • Loading branch information
astandrik authored Aug 9, 2024
2 parents 0c76d4d + 085e116 commit 21f42c3
Show file tree
Hide file tree
Showing 19 changed files with 267 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@
"all"
],
},
}
}
6 changes: 5 additions & 1 deletion src/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"selector": "TSTypeReference>TSQualifiedName[left.name='React'][right.name='FC']",
"message": "Don't use React.FC",
},
],
{
"selector": "ImportDeclaration[source.value='axios'] :matches(ImportSpecifier[imported.name='isAxiosError'])",
"message": "Please use isAxiosError from utils/response instead of axios",
},
],
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
CircleXmark,
} from '@gravity-ui/icons';
import {Icon, Spin, Tooltip} from '@gravity-ui/uikit';
import {isAxiosError} from 'axios';

import i18n from '../../containers/Tenant/Query/i18n';
import {isQueryCancelledError} from '../../containers/Tenant/Query/utils/isQueryCancelledError';
import {cn} from '../../utils/cn';
import {useChangedQuerySettings} from '../../utils/hooks/useChangedQuerySettings';
import {isAxiosError} from '../../utils/response';
import QuerySettingsDescription from '../QuerySettingsDescription/QuerySettingsDescription';

import './QueryExecutionStatus.scss';
Expand Down
70 changes: 45 additions & 25 deletions src/components/VDiskInfo/VDiskInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React from 'react';

import {getVDiskPagePath} from '../../routes';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {valueIsDefined} from '../../utils';
import {cn} from '../../utils/cn';
import {formatStorageValuesToGb, stringifyVdiskId} from '../../utils/dataFormatters/dataFormatters';
import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
import {getSeverityColor} from '../../utils/disks/helpers';
import type {PreparedVDisk} from '../../utils/disks/types';
import {useTypedSelector} from '../../utils/hooks';
import {bytesToSpeed} from '../../utils/utils';
import {EntityStatus} from '../EntityStatus/EntityStatus';
import {InfoViewer} from '../InfoViewer';
Expand Down Expand Up @@ -33,6 +37,7 @@ export function VDiskInfo<T extends PreparedVDisk>({
...infoViewerProps
}: VDiskInfoProps<T>) {
const diskPagesAvailable = useDiskPagesAvailable();
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

const {
AllocatedSize,
Expand Down Expand Up @@ -142,32 +147,47 @@ export function VDiskInfo<T extends PreparedVDisk>({
value: bytesToSpeed(WriteThroughput),
});
}
if (valueIsDefined(PDiskId) && valueIsDefined(NodeId) && valueIsDefined(VDiskSlotId)) {
const vDiskPagePath = getVDiskPagePath(VDiskSlotId, PDiskId, NodeId);
const vDiskInternalViewerPath = createVDiskDeveloperUILink({
nodeId: NodeId,
pDiskId: PDiskId,
vDiskSlotId: VDiskSlotId,
});

vdiskInfo.push({
label: vDiskInfoKeyset('links'),
value: (
<span className={b('links')}>
{withVDiskPageLink && diskPagesAvailable && (
<LinkWithIcon
title={vDiskInfoKeyset('vdisk-page')}
url={vDiskPagePath}
external={false}
/>
)}
<LinkWithIcon
title={vDiskInfoKeyset('developer-ui')}
url={vDiskInternalViewerPath}
/>
</span>
),
});
const diskParamsDefined =
valueIsDefined(PDiskId) && valueIsDefined(NodeId) && valueIsDefined(VDiskSlotId);

if (diskParamsDefined) {
const links: React.ReactNode[] = [];

if (withVDiskPageLink && diskPagesAvailable) {
const vDiskPagePath = getVDiskPagePath(VDiskSlotId, PDiskId, NodeId);
links.push(
<LinkWithIcon
key={vDiskPagePath}
title={vDiskInfoKeyset('vdisk-page')}
url={vDiskPagePath}
external={false}
/>,
);
}

if (isUserAllowedToMakeChanges) {
const vDiskInternalViewerPath = createVDiskDeveloperUILink({
nodeId: NodeId,
pDiskId: PDiskId,
vDiskSlotId: VDiskSlotId,
});

links.push(
<LinkWithIcon
key={vDiskInternalViewerPath}
title={vDiskInfoKeyset('developer-ui')}
url={vDiskInternalViewerPath}
/>,
);
}

if (links.length) {
vdiskInfo.push({
label: vDiskInfoKeyset('links'),
value: <div className={b('links')}>{links}</div>,
});
}
}

const title = data && withTitle ? <VDiskTitle data={data} /> : null;
Expand Down
15 changes: 14 additions & 1 deletion src/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,23 @@
overflow: auto;

height: 100%;
padding: 0 16px;
padding: 0 20px;

@include flex-container();

.ydb-table-with-controls-layout__controls {
height: 46px;
padding-top: 0;
}

&.ydb-table-with-controls-layout .data-table__sticky_moving {
top: 46px !important;
}

&__search {
@include search();
}

&__table-row {
cursor: pointer;
}
Expand Down
45 changes: 33 additions & 12 deletions src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import type {Column} from '@gravity-ui/react-data-table';

import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../../../components/Search';
import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout';
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
import {selectQueriesHistory} from '../../../../store/reducers/executeQuery';
import {
selectQueriesHistory,
selectQueriesHistoryFilter,
setQueryHistoryFilter,
} from '../../../../store/reducers/executeQuery';
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
import type {QueryInHistory} from '../../../../types/store/executeQuery';
Expand All @@ -25,13 +31,18 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
const dispatch = useTypedDispatch();

const queriesHistory = useTypedSelector(selectQueriesHistory);
const filter = useTypedSelector(selectQueriesHistoryFilter);
const reversedHistory = [...queriesHistory].reverse();

const onQueryClick = (query: QueryInHistory) => {
changeUserInput({input: query.queryText});
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
};

const onChangeFilter = (value: string) => {
dispatch(setQueryHistoryFilter(value));
};

const columns: Column<QueryInHistory>[] = [
{
name: 'queryText',
Expand All @@ -49,17 +60,27 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
];

return (
<div className={b()}>
<ResizeableDataTable
columnsWidthLSKey={QUERIES_HISTORY_COLUMNS_WIDTH_LS_KEY}
columns={columns}
data={reversedHistory}
settings={QUERY_TABLE_SETTINGS}
emptyDataMessage={i18n('history.empty')}
onRowClick={(row) => onQueryClick(row)}
rowClassName={() => b('table-row')}
/>
</div>
<TableWithControlsLayout className={b()}>
<TableWithControlsLayout.Controls>
<Search
value={filter}
onChange={onChangeFilter}
placeholder={i18n('filter.text.placeholder')}
className={b('search')}
/>
</TableWithControlsLayout.Controls>
<TableWithControlsLayout.Table>
<ResizeableDataTable
columnsWidthLSKey={QUERIES_HISTORY_COLUMNS_WIDTH_LS_KEY}
columns={columns}
data={reversedHistory}
settings={QUERY_TABLE_SETTINGS}
emptyDataMessage={i18n(filter ? 'history.empty-search' : 'history.empty')}
onRowClick={(row) => onQueryClick(row)}
rowClassName={() => b('table-row')}
/>
</TableWithControlsLayout.Table>
</TableWithControlsLayout>
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const CONTEXT_MENU_GROUP_ID = 'navigation';
const RESULT_TYPES = {
EXECUTE: 'execute',
EXPLAIN: 'explain',
};
} as const;

const b = cn('query-editor');

Expand Down Expand Up @@ -100,7 +100,7 @@ function QueryEditor(props: QueryEditorProps) {
} = props;
const {tenantPath: savedPath} = executeQuery;

const [resultType, setResultType] = React.useState(RESULT_TYPES.EXECUTE);
const [resultType, setResultType] = React.useState<ValueOf<typeof RESULT_TYPES>>();
const [isResultLoaded, setIsResultLoaded] = React.useState(false);
const [querySettings] = useQueryExecutionSettings();
const [enableTracingLevel] = useSetting<boolean>(ENABLE_TRACING_LEVEL_KEY);
Expand Down
15 changes: 14 additions & 1 deletion src/containers/Tenant/Query/SavedQueries/SavedQueries.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,23 @@
overflow: auto;

height: 100%;
padding: 0 16px;
padding: 0 20px;

@include flex-container();

.ydb-table-with-controls-layout__controls {
height: 46px;
padding-top: 0;
}

&.ydb-table-with-controls-layout .data-table__sticky_moving {
top: 46px !important;
}

&__search {
@include search();
}

&__row {
cursor: pointer;

Expand Down
50 changes: 34 additions & 16 deletions src/containers/Tenant/Query/SavedQueries/SavedQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import DataTable from '@gravity-ui/react-data-table';
import {Button, Dialog, Icon} from '@gravity-ui/uikit';

import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../../../components/Search';
import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout';
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
import {
deleteSavedQuery,
selectSavedQueriesFilter,
setQueryNameToEdit,
setSavedQueriesFilter,
} from '../../../../store/reducers/queryActions/queryActions';
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
import type {SavedQuery} from '../../../../types/store/query';
import {cn} from '../../../../utils/cn';
import {useTypedDispatch} from '../../../../utils/hooks';
import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks';
import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants';
import i18n from '../i18n';
import {useSavedQueries} from '../utils/useSavedQueries';
Expand Down Expand Up @@ -64,6 +68,7 @@ interface SavedQueriesProps {
export const SavedQueries = ({changeUserInput}: SavedQueriesProps) => {
const savedQueries = useSavedQueries();
const dispatch = useTypedDispatch();
const filter = useTypedSelector(selectSavedQueriesFilter);

const [isDeleteDialogVisible, setIsDeleteDialogVisible] = React.useState(false);
const [queryNameToDelete, setQueryNameToDelete] = React.useState<string>('');
Expand Down Expand Up @@ -97,6 +102,10 @@ export const SavedQueries = ({changeUserInput}: SavedQueriesProps) => {
};
};

const onChangeFilter = (value: string) => {
dispatch(setSavedQueriesFilter(value));
};

const columns: Column<SavedQuery>[] = [
{
name: 'name',
Expand Down Expand Up @@ -129,21 +138,30 @@ export const SavedQueries = ({changeUserInput}: SavedQueriesProps) => {

return (
<React.Fragment>
<div className={b()}>
<ResizeableDataTable
columnsWidthLSKey={SAVED_QUERIES_COLUMNS_WIDTH_LS_KEY}
columns={columns}
data={savedQueries}
settings={QUERY_TABLE_SETTINGS}
emptyDataMessage={i18n('saved.empty')}
rowClassName={() => b('row')}
onRowClick={(row) => onQueryClick(row.body, row.name)}
initialSortOrder={{
columnId: 'name',
order: DataTable.ASCENDING,
}}
/>
</div>
<TableWithControlsLayout className={b()}>
<TableWithControlsLayout.Controls>
<Search
onChange={onChangeFilter}
placeholder={i18n('filter.text.placeholder')}
className={b('search')}
/>
</TableWithControlsLayout.Controls>
<TableWithControlsLayout.Table>
<ResizeableDataTable
columnsWidthLSKey={SAVED_QUERIES_COLUMNS_WIDTH_LS_KEY}
columns={columns}
data={savedQueries}
settings={QUERY_TABLE_SETTINGS}
emptyDataMessage={i18n(filter ? 'history.empty-search' : 'saved.empty')}
rowClassName={() => b('row')}
onRowClick={(row) => onQueryClick(row.body, row.name)}
initialSortOrder={{
columnId: 'name',
order: DataTable.ASCENDING,
}}
/>
</TableWithControlsLayout.Table>
</TableWithControlsLayout>
<DeleteDialog
visible={isDeleteDialogVisible}
queryName={queryNameToDelete}
Expand Down
3 changes: 3 additions & 0 deletions src/containers/Tenant/Query/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"tabs.saved": "Saved",

"history.empty": "History is empty",
"history.empty-search": "Search result is empty",
"saved.empty": "There are no saved queries",

"delete-dialog.header": "Delete query",
Expand Down Expand Up @@ -48,6 +49,8 @@
"action.next-query": "Next query in history",
"action.save-query": "Save query",

"filter.text.placeholder": "Search by query text...",

"gear.tooltip": "Query execution settings have been changed for ",
"banner.query-settings.message": "Query results are displayed for "
}
8 changes: 6 additions & 2 deletions src/containers/Tenant/Query/utils/useSavedQueries.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {SAVED_QUERIES_KEY, useSetting} from '../../../../lib';
import {SAVED_QUERIES_KEY, useSetting, useTypedSelector} from '../../../../lib';
import {selectSavedQueriesFilter} from '../../../../store/reducers/queryActions/queryActions';
import type {SavedQuery} from '../../../../types/store/query';

export function useSavedQueries() {
const [savedQueries] = useSetting<SavedQuery[]>(SAVED_QUERIES_KEY, []);
const filter = useTypedSelector(selectSavedQueriesFilter).toLowerCase();

return savedQueries;
return filter
? savedQueries.filter((item) => item.body.toLowerCase().includes(filter))
: savedQueries;
}
2 changes: 1 addition & 1 deletion src/store/reducers/authentication/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const authenticationApi = api.injectEndpoints({
dispatch(setUser(data));
return {data};
} catch (error) {
if (isAxiosResponse(error) && error.status === 401) {
if (isAxiosResponse(error) && error.status === 401 && !error.data?.authUrl) {
dispatch(setIsAuthenticated(false));
}
return {error};
Expand Down
Loading

0 comments on commit 21f42c3

Please sign in to comment.