mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 00:23:57 +00:00
feat: new server summary tab
This commit is contained in:
46
packages/web/src/buttons/CtaButton.svelte
Normal file
46
packages/web/src/buttons/CtaButton.svelte
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let disabled = false;
|
||||||
|
export let title = null;
|
||||||
|
|
||||||
|
let domButton;
|
||||||
|
|
||||||
|
export function getBoundingClientRect() {
|
||||||
|
return domButton.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="cta-button"
|
||||||
|
{title}
|
||||||
|
{disabled}
|
||||||
|
on:click
|
||||||
|
bind:this={domButton}
|
||||||
|
data-testid={$$props['data-testid']}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.cta-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--theme-font-link);
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-button:hover:not(:disabled) {
|
||||||
|
color: var(--theme-font-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-button:disabled {
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
cursor: not-allowed;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
export let key, value, isParentExpanded, isParentArray;
|
export let key, value, isParentExpanded, isParentArray;
|
||||||
export let expanded = false;
|
export let expanded = false;
|
||||||
|
export let labelOverride = null;
|
||||||
|
export let hideKey = false;
|
||||||
const filteredKey = new Set(['length']);
|
const filteredKey = new Set(['length']);
|
||||||
|
|
||||||
$: keys = Object.getOwnPropertyNames(value);
|
$: keys = Object.getOwnPropertyNames(value);
|
||||||
@@ -22,8 +24,10 @@
|
|||||||
{keys}
|
{keys}
|
||||||
{previewKeys}
|
{previewKeys}
|
||||||
{getValue}
|
{getValue}
|
||||||
label="Array({value.length})"
|
label={labelOverride || `Array(${value.length})`}
|
||||||
bracketOpen="["
|
bracketOpen="["
|
||||||
bracketClose="]"
|
bracketClose="]"
|
||||||
elementValue={value}
|
elementValue={value}
|
||||||
/>
|
{labelOverride}
|
||||||
|
{hideKey}
|
||||||
|
/>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import JSONNested from './JSONNested.svelte';
|
import JSONNested from './JSONNested.svelte';
|
||||||
|
|
||||||
export let key, value, isParentExpanded, isParentArray, nodeType;
|
export let key, value, isParentExpanded, isParentArray, nodeType;
|
||||||
|
export let labelOverride = null;
|
||||||
|
export let hideKey = false;
|
||||||
|
|
||||||
let keys = [];
|
let keys = [];
|
||||||
|
|
||||||
@@ -29,7 +31,9 @@
|
|||||||
{getKey}
|
{getKey}
|
||||||
{getValue}
|
{getValue}
|
||||||
isArray={true}
|
isArray={true}
|
||||||
label="{nodeType}({keys.length})"
|
label={labelOverride || `${nodeType}(${keys.length})`}
|
||||||
bracketOpen={'{'}
|
bracketOpen={'{'}
|
||||||
bracketClose={'}'}
|
bracketClose={'}'}
|
||||||
/>
|
{labelOverride}
|
||||||
|
{hideKey}
|
||||||
|
/>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import MapEntry from './utils/MapEntry'
|
import MapEntry from './utils/MapEntry'
|
||||||
|
|
||||||
export let key, value, isParentExpanded, isParentArray, nodeType;
|
export let key, value, isParentExpanded, isParentArray, nodeType;
|
||||||
|
export let labelOverride = null;
|
||||||
|
export let hideKey = false;
|
||||||
|
|
||||||
let keys = [];
|
let keys = [];
|
||||||
|
|
||||||
@@ -28,8 +30,10 @@
|
|||||||
{keys}
|
{keys}
|
||||||
{getKey}
|
{getKey}
|
||||||
{getValue}
|
{getValue}
|
||||||
label="{nodeType}({keys.length})"
|
label={labelOverride || `${nodeType}(${keys.length})`}
|
||||||
colon=""
|
colon=""
|
||||||
bracketOpen={'{'}
|
bracketOpen={'{'}
|
||||||
bracketClose={'}'}
|
bracketClose={'}'}
|
||||||
|
{labelOverride}
|
||||||
|
{hideKey}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
export let key, value, isParentExpanded, isParentArray;
|
export let key, value, isParentExpanded, isParentArray;
|
||||||
export let expanded = false;
|
export let expanded = false;
|
||||||
|
export let hideKey = false;
|
||||||
|
export let labelOverride = null;
|
||||||
|
|
||||||
const keys = ['key', 'value'];
|
const keys = ['key', 'value'];
|
||||||
|
|
||||||
@@ -17,7 +19,9 @@
|
|||||||
key={isParentExpanded ? String(key) : value.key}
|
key={isParentExpanded ? String(key) : value.key}
|
||||||
{keys}
|
{keys}
|
||||||
{getValue}
|
{getValue}
|
||||||
label={isParentExpanded ? 'Entry ' : '=> '}
|
label={labelOverride || (isParentExpanded ? 'Entry ' : '=> ')}
|
||||||
bracketOpen={'{'}
|
bracketOpen={'{'}
|
||||||
bracketClose={'}'}
|
bracketClose={'}'}
|
||||||
/>
|
{labelOverride}
|
||||||
|
{hideKey}
|
||||||
|
/>
|
||||||
|
|||||||
@@ -21,11 +21,14 @@
|
|||||||
expandable = true;
|
expandable = true;
|
||||||
export let elementValue = null;
|
export let elementValue = null;
|
||||||
export let onRootExpandedChanged = null;
|
export let onRootExpandedChanged = null;
|
||||||
|
export let labelOverride = null;
|
||||||
|
export let hideKey = false;
|
||||||
|
|
||||||
const context = getContext('json-tree-context-key');
|
const context = getContext('json-tree-context-key');
|
||||||
setContext('json-tree-context-key', { ...context, colon });
|
setContext('json-tree-context-key', { ...context, colon });
|
||||||
const elementData = getContext('json-tree-element-data');
|
const elementData = getContext('json-tree-element-data');
|
||||||
const slicedKeyCount = getContext('json-tree-sliced-key-count');
|
const slicedKeyCount = getContext('json-tree-sliced-key-count');
|
||||||
|
const keyLabel = labelOverride ?? key;
|
||||||
|
|
||||||
$: slicedKeys = expanded ? keys : previewKeys.slice(0, slicedKeyCount || 5);
|
$: slicedKeys = expanded ? keys : previewKeys.slice(0, slicedKeyCount || 5);
|
||||||
|
|
||||||
@@ -56,7 +59,16 @@
|
|||||||
{#if expandable && isParentExpanded}
|
{#if expandable && isParentExpanded}
|
||||||
<JSONArrow on:click={toggleExpand} {expanded} />
|
<JSONArrow on:click={toggleExpand} {expanded} />
|
||||||
{/if}
|
{/if}
|
||||||
<JSONKey {key} colon={context.colon} {isParentExpanded} {isParentArray} on:click={toggleExpand} />
|
{#if !hideKey}
|
||||||
|
<JSONKey
|
||||||
|
key={keyLabel}
|
||||||
|
colon={context.colon}
|
||||||
|
{isParentExpanded}
|
||||||
|
{isParentArray}
|
||||||
|
{hideKey}
|
||||||
|
on:click={toggleExpand}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
<span on:click={toggleExpand}><span>{label}</span>{bracketOpen}</span>
|
<span on:click={toggleExpand}><span>{label}</span>{bracketOpen}</span>
|
||||||
</label>
|
</label>
|
||||||
{#if isParentExpanded}
|
{#if isParentExpanded}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
export let expanded = !!getContext('json-tree-default-expanded');
|
export let expanded = !!getContext('json-tree-default-expanded');
|
||||||
export let labelOverride = null;
|
export let labelOverride = null;
|
||||||
export let onRootExpandedChanged = null;
|
export let onRootExpandedChanged = null;
|
||||||
|
export let hideKey = false;
|
||||||
|
|
||||||
$: nodeType = objType(value);
|
$: nodeType = objType(value);
|
||||||
$: componentType = getComponent(nodeType);
|
$: componentType = getComponent(nodeType);
|
||||||
@@ -85,4 +86,5 @@
|
|||||||
{expanded}
|
{expanded}
|
||||||
{labelOverride}
|
{labelOverride}
|
||||||
{onRootExpandedChanged}
|
{onRootExpandedChanged}
|
||||||
|
{hideKey}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
export let expanded = false;
|
export let expanded = false;
|
||||||
export let labelOverride = null;
|
export let labelOverride = null;
|
||||||
export let onRootExpandedChanged = null;
|
export let onRootExpandedChanged = null;
|
||||||
|
export let hideKey = false;
|
||||||
|
|
||||||
$: keys = Object.getOwnPropertyNames(value);
|
$: keys = Object.getOwnPropertyNames(value);
|
||||||
|
|
||||||
@@ -26,4 +27,5 @@
|
|||||||
bracketClose={'}'}
|
bracketClose={'}'}
|
||||||
elementValue={value}
|
elementValue={value}
|
||||||
{onRootExpandedChanged}
|
{onRootExpandedChanged}
|
||||||
|
{hideKey}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import JSONNode from './JSONNode.svelte';
|
import JSONNode from './JSONNode.svelte';
|
||||||
import { setContext } from 'svelte';
|
import { setContext } from 'svelte';
|
||||||
import contextMenu, { getContextMenu } from '../utility/contextMenu';
|
import contextMenu, { getContextMenu } from '../utility/contextMenu';
|
||||||
import openNewTab from '../utility/openNewTab';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { copyTextToClipboard } from '../utility/clipboard';
|
import { copyTextToClipboard } from '../utility/clipboard';
|
||||||
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
||||||
@@ -23,6 +22,7 @@
|
|||||||
export let isDeleted = false;
|
export let isDeleted = false;
|
||||||
export let isInserted = false;
|
export let isInserted = false;
|
||||||
export let isModified = false;
|
export let isModified = false;
|
||||||
|
export let hideKey = false;
|
||||||
|
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
$: wrap = $settings?.['behaviour.jsonPreviewWrap'];
|
$: wrap = $settings?.['behaviour.jsonPreviewWrap'];
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
class:wrap
|
class:wrap
|
||||||
>
|
>
|
||||||
<JSONNode
|
<JSONNode
|
||||||
|
{hideKey}
|
||||||
{key}
|
{key}
|
||||||
{value}
|
{value}
|
||||||
isParentExpanded={true}
|
isParentExpanded={true}
|
||||||
|
|||||||
@@ -3,10 +3,25 @@
|
|||||||
|
|
||||||
import JSONKey from './JSONKey.svelte';
|
import JSONKey from './JSONKey.svelte';
|
||||||
|
|
||||||
export let key, value, valueGetter = null, isParentExpanded, isParentArray, nodeType;
|
export let key,
|
||||||
|
value,
|
||||||
|
valueGetter = null,
|
||||||
|
labelOverride,
|
||||||
|
isParentExpanded,
|
||||||
|
isParentArray,
|
||||||
|
nodeType;
|
||||||
|
|
||||||
|
const label = labelOverride ?? key;
|
||||||
const { colon } = getContext('json-tree-context-key');
|
const { colon } = getContext('json-tree-context-key');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<li class:indent={isParentExpanded}>
|
||||||
|
<JSONKey key={label} {colon} {isParentExpanded} {isParentArray} />
|
||||||
|
<span class={nodeType}>
|
||||||
|
{valueGetter ? valueGetter(value) : value}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
li {
|
li {
|
||||||
user-select: text;
|
user-select: text;
|
||||||
@@ -45,9 +60,4 @@
|
|||||||
color: var(--symbol-color);
|
color: var(--symbol-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<li class:indent={isParentExpanded}>
|
|
||||||
<JSONKey {key} {colon} {isParentExpanded} {isParentArray} />
|
|
||||||
<span class={nodeType}>
|
|
||||||
{valueGetter ? valueGetter(value) : value}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
|
|||||||
@@ -215,6 +215,8 @@ export const connectionAppObjectSearchSettings = writableWithStorage(
|
|||||||
'connectionAppObjectSearchSettings2'
|
'connectionAppObjectSearchSettings2'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const serverSummarySelectedTab = writableWithStorage(0, 'serverSummary.selectedTab');
|
||||||
|
|
||||||
let currentThemeValue = null;
|
let currentThemeValue = null;
|
||||||
currentTheme.subscribe(value => {
|
currentTheme.subscribe(value => {
|
||||||
currentThemeValue = value;
|
currentThemeValue = value;
|
||||||
|
|||||||
@@ -18,15 +18,17 @@
|
|||||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||||
import registerCommand from '../commands/registerCommand';
|
import registerCommand from '../commands/registerCommand';
|
||||||
import Link from '../elements/Link.svelte';
|
|
||||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||||
|
import TabControl from '../elements/TabControl.svelte';
|
||||||
|
|
||||||
import ObjectListControl from '../elements/ObjectListControl.svelte';
|
|
||||||
import { _t } from '../translations';
|
import { _t } from '../translations';
|
||||||
import { apiCall } from '../utility/api';
|
import { apiCall } from '../utility/api';
|
||||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||||
import formatFileSize from '../utility/formatFileSize';
|
|
||||||
import openNewTab from '../utility/openNewTab';
|
import openNewTab from '../utility/openNewTab';
|
||||||
|
import SummaryVariables from '../widgets/SummaryVariables.svelte';
|
||||||
|
import SummaryProcesses from '../widgets/SummaryProcesses.svelte';
|
||||||
|
import SummaryDatabases from '../widgets/SummaryDatabases.svelte';
|
||||||
|
import { serverSummarySelectedTab } from '../stores';
|
||||||
|
|
||||||
export let conid;
|
export let conid;
|
||||||
|
|
||||||
@@ -78,26 +80,31 @@
|
|||||||
<LoadingInfo message="Loading server details" wrapper />
|
<LoadingInfo message="Loading server details" wrapper />
|
||||||
{:then summary}
|
{:then summary}
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<ObjectListControl
|
<TabControl
|
||||||
collection={summary.databases}
|
isInline
|
||||||
hideDisplayName
|
inlineTabs={true}
|
||||||
title={`Databases (${summary.databases.length})`}
|
containerMaxWidth="100%"
|
||||||
emptyMessage={'No databases'}
|
flex1={true}
|
||||||
columns={summary.columns.map(col => ({
|
value={$serverSummarySelectedTab}
|
||||||
...col,
|
onUserChange={(index) => serverSummarySelectedTab.set(index)}
|
||||||
slot: col.columnType == 'bytes' ? 1 : col.columnType == 'actions' ? 2 : null,
|
tabs={[
|
||||||
}))}
|
{
|
||||||
>
|
label: 'Variables',
|
||||||
<svelte:fragment slot="1" let:row let:col>{formatFileSize(row?.[col.fieldName])}</svelte:fragment>
|
component: SummaryVariables,
|
||||||
<svelte:fragment slot="2" let:row let:col>
|
props: { variables: summary.variables || [] },
|
||||||
{#each col.actions as action, index}
|
},
|
||||||
{#if index > 0}
|
{
|
||||||
<span class="action-separator">|</span>
|
label: 'Processes',
|
||||||
{/if}
|
component: SummaryProcesses,
|
||||||
<Link onClick={() => runAction(action, row)}>{action.header}</Link>
|
props: { processes: summary.processes || [], conid },
|
||||||
{/each}
|
},
|
||||||
</svelte:fragment>
|
{
|
||||||
</ObjectListControl>
|
label: 'Databases',
|
||||||
|
component: SummaryDatabases,
|
||||||
|
props: { databases: summary.databases || [] },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,7 @@
|
|||||||
defaultActionId: 'openTable',
|
defaultActionId: 'openTable',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}>Data</ToolStripButton
|
}}>DataX</ToolStripButton
|
||||||
>
|
>
|
||||||
|
|
||||||
<ToolStripButton
|
<ToolStripButton
|
||||||
|
|||||||
77
packages/web/src/widgets/SummaryDatabases.svelte
Normal file
77
packages/web/src/widgets/SummaryDatabases.svelte
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
|
import CtaButton from '../buttons/CtaButton.svelte';
|
||||||
|
import { _t } from '../translations';
|
||||||
|
import formatFileSize from '../utility/formatFileSize';
|
||||||
|
|
||||||
|
export let databases: any[] = [];
|
||||||
|
|
||||||
|
async function profileOff(database: any) {
|
||||||
|
// TODO: Implement profile off functionality
|
||||||
|
console.log('Profile off:', database.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function profileFiltered(database: any) {
|
||||||
|
// TODO: Implement profile filtered functionality
|
||||||
|
console.log('Profile filtered:', database.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function profileAll(database: any) {
|
||||||
|
// TODO: Implement profile all functionality
|
||||||
|
console.log('Profile all:', database.name);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TableControl
|
||||||
|
rows={databases}
|
||||||
|
columns={[
|
||||||
|
{ header: 'Name', fieldName: 'name', slot: 1 },
|
||||||
|
{ header: 'Size', fieldName: 'sizeOnDisk', slot: 2 },
|
||||||
|
{ header: 'Collections', fieldName: 'collections' },
|
||||||
|
{ header: 'Indexes', fieldName: 'indexes' },
|
||||||
|
{ header: 'Data Size', fieldName: 'dataSize', slot: 3 },
|
||||||
|
{ header: 'Index Size', fieldName: 'indexSize', slot: 4 },
|
||||||
|
{
|
||||||
|
header: 'Actions',
|
||||||
|
fieldName: 'name',
|
||||||
|
slot: 0,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="0" let:row>
|
||||||
|
<CtaButton on:click={() => profileOff(row)}>Profile Off</CtaButton>
|
||||||
|
<span class="action-separator">|</span>
|
||||||
|
<CtaButton on:click={() => profileFiltered(row)}>Profile Filtered</CtaButton>
|
||||||
|
<span class="action-separator">|</span>
|
||||||
|
<CtaButton on:click={() => profileAll(row)}>Profile All</CtaButton>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="1" let:row>
|
||||||
|
<strong>{row.name}</strong>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="2" let:row>
|
||||||
|
<span>{formatFileSize(row.sizeOnDisk)}</span>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="3" let:row>
|
||||||
|
<span>{formatFileSize(row.dataSize)}</span>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="4" let:row>
|
||||||
|
<span>{formatFileSize(row.indexSize)}</span>
|
||||||
|
</svelte:fragment>
|
||||||
|
</TableControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-separator {
|
||||||
|
margin: 0 5px;
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
77
packages/web/src/widgets/SummaryProcesses.svelte
Normal file
77
packages/web/src/widgets/SummaryProcesses.svelte
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DatabaseProcess } from 'dbgate-types';
|
||||||
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
|
import { _t } from '../translations';
|
||||||
|
import CtaButton from '../buttons/CtaButton.svelte';
|
||||||
|
|
||||||
|
export let processes: DatabaseProcess[] = [];
|
||||||
|
|
||||||
|
async function killProcess(processId: string) {
|
||||||
|
// TODO: Implement kill process functionality
|
||||||
|
console.log('Kill process:', processId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRunningTime(seconds: number): string {
|
||||||
|
if (!seconds) return '-';
|
||||||
|
if (seconds < 60) return `${seconds}s`;
|
||||||
|
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
||||||
|
return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TableControl
|
||||||
|
rows={processes}
|
||||||
|
columns={[
|
||||||
|
{ header: 'Process ID', fieldName: 'processId', slot: 1 },
|
||||||
|
{ header: 'Connection ID', fieldName: 'connectionId' },
|
||||||
|
{ header: 'Client', fieldName: 'client' },
|
||||||
|
{ header: 'Operation', fieldName: 'operation' },
|
||||||
|
{ header: 'Namespace', fieldName: 'namespace' },
|
||||||
|
{ header: 'Running Time', fieldName: 'runningTime', slot: 2 },
|
||||||
|
{ header: 'State', fieldName: 'state' },
|
||||||
|
{ header: 'Waiting For', fieldName: 'waitingFor', slot: 3 },
|
||||||
|
{
|
||||||
|
header: 'Actions',
|
||||||
|
fieldName: 'processId',
|
||||||
|
slot: 0,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="0" let:row>
|
||||||
|
<CtaButton on:click={() => killProcess(row.processId)}>
|
||||||
|
{_t('common.kill', { defaultMessage: 'Kill' })}
|
||||||
|
</CtaButton>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="1" let:row>
|
||||||
|
<code>{row.processId}</code>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="2" let:row>
|
||||||
|
<span>{formatRunningTime(row.runningTime)}</span>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="3" let:row>
|
||||||
|
<span class:waiting={row.waitingFor}>{row.waitingFor ? 'Yes' : 'No'}</span>
|
||||||
|
</svelte:fragment>
|
||||||
|
</TableControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: var(--theme-bg-1);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting {
|
||||||
|
color: var(--theme-font-warning);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
29
packages/web/src/widgets/SummaryVariables.svelte
Normal file
29
packages/web/src/widgets/SummaryVariables.svelte
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
|
import JSONTree from '../jsontree/JSONTree.svelte';
|
||||||
|
export let variables: { variable: string; value: any }[] = [];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TableControl
|
||||||
|
rows={variables}
|
||||||
|
columns={[
|
||||||
|
{ header: 'Variable', fieldName: 'variable' },
|
||||||
|
{
|
||||||
|
header: 'Value',
|
||||||
|
fieldName: 'value',
|
||||||
|
slot: 0,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="0" let:row>
|
||||||
|
<JSONTree labelOverride="" hideKey key={row.variable} value={row.value} expandAll={false} />
|
||||||
|
</svelte:fragment>
|
||||||
|
</TableControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user