feat: make summary table sortable, filtrable, with sticky header

This commit is contained in:
Pavel
2025-08-19 17:08:38 +02:00
parent 78215552bf
commit 114ce1ea3a
7 changed files with 140 additions and 19 deletions

View File

@@ -131,6 +131,8 @@ export type SummaryDatabaseColumn = {
header: string; header: string;
fieldName: string; fieldName: string;
type: 'data' | 'fileSize'; type: 'data' | 'fileSize';
filterable?: boolean;
sortable?: boolean;
}; };
export interface ServerSummary { export interface ServerSummary {

View File

@@ -1,11 +1,16 @@
<script lang="ts"> <script lang="ts">
import { writable } from 'svelte/store';
import TableControl from '../elements/TableControl.svelte'; import TableControl from '../elements/TableControl.svelte';
import formatFileSize from '../utility/formatFileSize'; import formatFileSize from '../utility/formatFileSize';
export let rows: any[] = []; export let rows: any[] = [];
export let columns: any[] = []; export let columns: any[] = [];
const filters = writable({});
const tableColumns = columns.map(col => ({ const tableColumns = columns.map(col => ({
filterable: col.filterable,
sortable: col.sortable,
header: col.header, header: col.header,
fieldName: col.fieldName, fieldName: col.fieldName,
type: col.type || 'data', type: col.type || 'data',
@@ -19,7 +24,7 @@
</script> </script>
<div> <div>
<TableControl {rows} columns={tableColumns} /> <TableControl {filters} stickyHeader {rows} columns={tableColumns} />
</div> </div>
<style> <style>

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { writable } from 'svelte/store';
import { DatabaseProcess } from 'dbgate-types'; import { DatabaseProcess } from 'dbgate-types';
import TableControl from '../elements/TableControl.svelte'; import TableControl from '../elements/TableControl.svelte';
import { _t } from '../translations'; import { _t } from '../translations';
@@ -15,6 +16,8 @@
export let refreshInterval: number = 1000; export let refreshInterval: number = 1000;
export let tabVisible: boolean = false; export let tabVisible: boolean = false;
const filters = writable({});
let internalProcesses = [...processes]; let internalProcesses = [...processes];
async function refreshProcesses() { async function refreshProcesses() {
@@ -79,20 +82,54 @@
<div> <div>
<TableControl <TableControl
{filters}
stickyHeader
rows={internalProcesses} rows={internalProcesses}
columns={[ columns={[
{ header: _t('summaryProcesses.processId', { defaultMessage: 'Process ID' }), fieldName: 'processId', slot: 1 },
{ header: _t('summaryProcesses.connectionId', { defaultMessage: 'Connection ID' }), fieldName: 'connectionId' },
{ header: _t('summaryProcesses.client', { defaultMessage: 'Client' }), fieldName: 'client' },
{ header: _t('summaryProcesses.operation', { defaultMessage: 'Operation' }), fieldName: 'operation' },
{ header: _t('summaryProcesses.namespace', { defaultMessage: 'Namespace' }), fieldName: 'namespace' },
{ {
sortable: true,
filterable: true,
header: _t('summaryProcesses.processId', { defaultMessage: 'Process ID' }),
fieldName: 'processId',
slot: 1,
},
{
sortable: true,
filterable: true,
header: _t('summaryProcesses.connectionId', { defaultMessage: 'Connection ID' }),
fieldName: 'connectionId',
},
{
sortable: true,
filterable: true,
header: _t('summaryProcesses.client', { defaultMessage: 'Client' }),
fieldName: 'client',
},
{
filterable: true,
header: _t('summaryProcesses.operation', { defaultMessage: 'Operation' }),
fieldName: 'operation',
},
{
sortable: true,
filterable: true,
header: _t('summaryProcesses.namespace', { defaultMessage: 'Namespace' }),
fieldName: 'namespace',
},
{
sortable: true,
header: _t('summaryProcesses.runningTime', { defaultMessage: 'Running Time' }), header: _t('summaryProcesses.runningTime', { defaultMessage: 'Running Time' }),
fieldName: 'runningTime', fieldName: 'runningTime',
slot: 2, slot: 2,
}, },
{ header: _t('summaryProcesses.state', { defaultMessage: 'State' }), fieldName: 'state' },
{ {
sortable: true,
filterable: true,
header: _t('summaryProcesses.state', { defaultMessage: 'State' }),
fieldName: 'state',
},
{
sortable: true,
header: _t('summaryProcesses.waitingFor', { defaultMessage: 'Waiting For' }), header: _t('summaryProcesses.waitingFor', { defaultMessage: 'Waiting For' }),
fieldName: 'waitingFor', fieldName: 'waitingFor',
slot: 3, slot: 3,

View File

@@ -1,16 +1,28 @@
<script lang="ts"> <script lang="ts">
import { writable } from 'svelte/store';
import TableControl from '../elements/TableControl.svelte'; import TableControl from '../elements/TableControl.svelte';
import JSONTree from '../jsontree/JSONTree.svelte'; import JSONTree from '../jsontree/JSONTree.svelte';
import { _t } from '../translations'; import { _t } from '../translations';
export let variables: { variable: string; value: any }[] = []; export let variables: { variable: string; value: any }[] = [];
const filters = writable({});
</script> </script>
<div> <div>
<TableControl <TableControl
stickyHeader
rows={variables} rows={variables}
{filters}
columns={[ columns={[
{ header: _t('summaryVariables.variable', { defaultMessage: 'Variable' }), fieldName: 'variable' },
{ {
sortable: true,
filterable: true,
header: _t('summaryVariables.variable', { defaultMessage: 'Variable' }),
fieldName: 'variable',
},
{
sortable: true,
filterable: true,
header: _t('summaryVariables.value', { defaultMessage: 'Value' }), header: _t('summaryVariables.value', { defaultMessage: 'Value' }),
fieldName: 'value', fieldName: 'value',
slot: 0, slot: 0,

View File

@@ -647,9 +647,26 @@ const driver = {
databases: { databases: {
rows: databases, rows: databases,
columns: [ columns: [
{ header: 'Name', fieldName: 'name', type: 'data' }, {
{ header: 'Size on disk', fieldName: 'sizeOnDisk', type: 'fileSize' }, filterable: true,
{ header: 'Empty', fieldName: 'empty', type: 'data' }, sortable: true,
header: 'Name',
fieldName: 'name',
type: 'data',
},
{
sortable: true,
header: 'Size on disk',
fieldName: 'sizeOnDisk',
type: 'fileSize',
},
{
filterable: true,
sortable: true,
header: 'Empty',
fieldName: 'empty',
type: 'data',
},
], ],
}, },
}; };

View File

@@ -180,13 +180,53 @@ const driver = {
databases: { databases: {
rows: databases, rows: databases,
columns: [ columns: [
{ header: 'Database', fieldName: 'name', type: 'data' }, {
{ header: 'Status', fieldName: 'status', type: 'data' }, filterable: true,
{ header: 'Recovery Model', fieldName: 'recoveryModel', type: 'data' }, sortable: true,
{ header: 'Compatibility Level', fieldName: 'compatibilityLevel', type: 'data' }, header: 'Database',
{ header: 'Read Only', fieldName: 'isReadOnly', type: 'data' }, fieldName: 'name',
{ header: 'Data Size', fieldName: 'sizeOnDisk', type: 'fileSize' }, type: 'data',
{ header: 'Log Size', fieldName: 'logSizeOnDisk', type: 'fileSize' }, },
{
filterable: true,
sortable: true,
header: 'Status',
fieldName: 'status',
type: 'data',
},
{
filterable: true,
sortable: true,
header: 'Recovery Model',
fieldName: 'recoveryModel',
type: 'data',
},
{
filterable: true,
sortable: true,
header: 'Compatibility Level',
fieldName: 'compatibilityLevel',
type: 'data',
},
{
filterable: true,
sortable: true,
header: 'Read Only',
fieldName: 'isReadOnly',
type: 'data',
},
{
sortable: true,
header: 'Data Size',
fieldName: 'sizeOnDisk',
type: 'fileSize',
},
{
sortable: true,
header: 'Log Size',
fieldName: 'logSizeOnDisk',
type: 'fileSize',
},
], ],
}, },
}; };

View File

@@ -250,7 +250,15 @@ const drivers = driverBases.map(driverBase => ({
rows: databases.map(db => ({ rows: databases.map(db => ({
name: db.name, name: db.name,
})), })),
columns: [{ header: 'Database', fieldName: 'name', type: 'data' }], columns: [
{
filterable: true,
sortable: true,
header: 'Database',
fieldName: 'name',
type: 'data',
},
],
}, },
}; };
}, },