mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
statusbar - show query execution duration
This commit is contained in:
parent
8396e726ec
commit
51ba9d3b5a
@ -100,6 +100,7 @@ export default function Screen() {
|
||||
? dimensions.widgetMenu.iconSize + leftPanelWidth + dimensions.splitter.thickness
|
||||
: dimensions.widgetMenu.iconSize;
|
||||
const toolbarPortalRef = React.useRef();
|
||||
const statusbarPortalRef = React.useRef();
|
||||
const onSplitDown = useSplitterDrag('clientX', diff => setLeftPanelWidth(v => v + diff));
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useUploadsZone();
|
||||
@ -131,10 +132,10 @@ export default function Screen() {
|
||||
<TabsPanel></TabsPanel>
|
||||
</TabsPanelContainer>
|
||||
<BodyDiv contentLeft={contentLeft} theme={theme}>
|
||||
<TabContent toolbarPortalRef={toolbarPortalRef} />
|
||||
<TabContent toolbarPortalRef={toolbarPortalRef} statusbarPortalRef={statusbarPortalRef} />
|
||||
</BodyDiv>
|
||||
<StausBarContainer theme={theme}>
|
||||
<StatusBar />
|
||||
<StatusBar statusbarPortalRef={statusbarPortalRef} />
|
||||
</StausBarContainer>
|
||||
<ModalLayer />
|
||||
<MenuLayer />
|
||||
|
@ -18,12 +18,18 @@ const TabContainerStyled = styled.div`
|
||||
`;
|
||||
|
||||
function TabContainer({ TabComponent, ...props }) {
|
||||
const { tabVisible, tabid, toolbarPortalRef } = props;
|
||||
const { tabVisible, tabid, toolbarPortalRef, statusbarPortalRef } = props;
|
||||
return (
|
||||
// @ts-ignore
|
||||
<TabContainerStyled tabVisible={tabVisible}>
|
||||
<ErrorBoundary>
|
||||
<TabComponent {...props} tabid={tabid} tabVisible={tabVisible} toolbarPortalRef={toolbarPortalRef} />
|
||||
<TabComponent
|
||||
{...props}
|
||||
tabid={tabid}
|
||||
tabVisible={tabVisible}
|
||||
toolbarPortalRef={toolbarPortalRef}
|
||||
statusbarPortalRef={statusbarPortalRef}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</TabContainerStyled>
|
||||
);
|
||||
@ -42,7 +48,7 @@ function createTabComponent(selectedTab) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function TabContent({ toolbarPortalRef }) {
|
||||
export default function TabContent({ toolbarPortalRef, statusbarPortalRef }) {
|
||||
const files = useOpenedTabs();
|
||||
|
||||
const [mountedTabs, setMountedTabs] = React.useState({});
|
||||
@ -84,6 +90,7 @@ export default function TabContent({ toolbarPortalRef }) {
|
||||
tabid={tabid}
|
||||
tabVisible={tabVisible}
|
||||
toolbarPortalRef={toolbarPortalRef}
|
||||
statusbarPortalRef={statusbarPortalRef}
|
||||
TabComponent={TabComponent}
|
||||
/>
|
||||
);
|
||||
|
@ -21,8 +21,19 @@ import useEditorData from '../utility/useEditorData';
|
||||
import applySqlTemplate from '../utility/applySqlTemplate';
|
||||
import LoadingInfo from '../widgets/LoadingInfo';
|
||||
import useExtensions from '../utility/useExtensions';
|
||||
import useTimerLabel from '../utility/useTimerLabel';
|
||||
import { StatusBarItem } from '../widgets/StatusBar';
|
||||
|
||||
export default function QueryTab({ tabid, conid, database, initialArgs, tabVisible, toolbarPortalRef, ...other }) {
|
||||
export default function QueryTab({
|
||||
tabid,
|
||||
conid,
|
||||
database,
|
||||
initialArgs,
|
||||
tabVisible,
|
||||
toolbarPortalRef,
|
||||
statusbarPortalRef,
|
||||
...other
|
||||
}) {
|
||||
const [sessionId, setSessionId] = React.useState(null);
|
||||
const [visibleResultTabs, setVisibleResultTabs] = React.useState(false);
|
||||
const [executeNumber, setExecuteNumber] = React.useState(0);
|
||||
@ -31,6 +42,7 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
const saveFileModalState = useModalState();
|
||||
const extensions = useExtensions();
|
||||
const timerLabel = useTimerLabel();
|
||||
const { editorData, setEditorData, isLoading } = useEditorData({
|
||||
tabid,
|
||||
loadFromArgs:
|
||||
@ -43,6 +55,7 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib
|
||||
|
||||
const handleSessionDone = React.useCallback(() => {
|
||||
setBusy(false);
|
||||
timerLabel.stop();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -77,6 +90,7 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib
|
||||
setSessionId(sesid);
|
||||
}
|
||||
setBusy(true);
|
||||
timerLabel.start();
|
||||
await axios.post('sessions/execute-query', {
|
||||
sesid,
|
||||
sql: selectedText || editorData,
|
||||
@ -95,6 +109,7 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib
|
||||
});
|
||||
setSessionId(null);
|
||||
setBusy(false);
|
||||
timerLabel.stop();
|
||||
};
|
||||
|
||||
const handleKeyDown = (data, hash, keyString, keyCode, event) => {
|
||||
@ -167,6 +182,10 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib
|
||||
/>,
|
||||
toolbarPortalRef.current
|
||||
)}
|
||||
{statusbarPortalRef &&
|
||||
statusbarPortalRef.current &&
|
||||
tabVisible &&
|
||||
ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)}
|
||||
<SaveTabModal
|
||||
modalState={saveFileModalState}
|
||||
tabVisible={tabVisible}
|
||||
|
37
packages/web/src/utility/useTimerLabel.js
Normal file
37
packages/web/src/utility/useTimerLabel.js
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
function formatSeconds(duration) {
|
||||
if (duration == null) return '';
|
||||
const hours = _.padStart(Math.floor(duration / 3600).toString(), 2, '0');
|
||||
const minutes = _.padStart((Math.floor(duration / 60) % 60).toString(), 2, '0');
|
||||
const seconds = _.padStart((duration % 60).toString(), 2, '0');
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
export default function useTimerLabel() {
|
||||
const [duration, setDuration] = React.useState(null);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
if (busy) {
|
||||
setDuration(0);
|
||||
const handle = setInterval(() => setDuration(x => x + 1), 1000);
|
||||
return () => window.clearInterval(handle);
|
||||
}
|
||||
}, [busy]);
|
||||
|
||||
const start = React.useCallback(() => {
|
||||
setBusy(true);
|
||||
}, []);
|
||||
|
||||
const stop = React.useCallback(() => {
|
||||
setBusy(false);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
text: formatSeconds(duration),
|
||||
duration,
|
||||
};
|
||||
}
|
@ -10,9 +10,10 @@ const Container = styled.div`
|
||||
display: flex;
|
||||
color: ${props => props.theme.statusbar_font1};
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const Item = styled.div`
|
||||
export const StatusBarItem = styled.div`
|
||||
padding: 2px 10px;
|
||||
// margin: auto;
|
||||
// flex-grow: 0;
|
||||
@ -30,62 +31,72 @@ const InfoWrapper = styled.span`
|
||||
props.theme.statusbar_font_green[5]};
|
||||
`;
|
||||
|
||||
export default function StatusBar() {
|
||||
const StatusbarContainer = styled.div`
|
||||
display: flex;
|
||||
// align-items: flex-end;
|
||||
// display: flex;
|
||||
// user-select: none;
|
||||
`;
|
||||
|
||||
export default function StatusBar({ statusbarPortalRef }) {
|
||||
const { name, connection } = useCurrentDatabase() || {};
|
||||
const status = useDatabaseStatus(connection ? { conid: connection._id, database: name } : {});
|
||||
const { displayName, server, user, engine } = connection || {};
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Container theme={theme}>
|
||||
{name && (
|
||||
<Item>
|
||||
<FontIcon icon="icon database" /> {name}
|
||||
</Item>
|
||||
)}
|
||||
{(displayName || server) && (
|
||||
<Item>
|
||||
<FontIcon icon="icon server" /> {displayName || server}
|
||||
</Item>
|
||||
)}
|
||||
<StatusbarContainer>
|
||||
{name && (
|
||||
<StatusBarItem>
|
||||
<FontIcon icon="icon database" /> {name}
|
||||
</StatusBarItem>
|
||||
)}
|
||||
{(displayName || server) && (
|
||||
<StatusBarItem>
|
||||
<FontIcon icon="icon server" /> {displayName || server}
|
||||
</StatusBarItem>
|
||||
)}
|
||||
|
||||
{user && (
|
||||
<Item>
|
||||
<FontIcon icon="icon account" /> {user}
|
||||
</Item>
|
||||
)}
|
||||
{user && (
|
||||
<StatusBarItem>
|
||||
<FontIcon icon="icon account" /> {user}
|
||||
</StatusBarItem>
|
||||
)}
|
||||
|
||||
{connection && status && (
|
||||
<Item>
|
||||
{status.name == 'pending' && (
|
||||
{connection && status && (
|
||||
<StatusBarItem>
|
||||
{status.name == 'pending' && (
|
||||
<>
|
||||
<FontIcon icon="icon loading" /> Loading
|
||||
</>
|
||||
)}
|
||||
{status.name == 'ok' && (
|
||||
<>
|
||||
<InfoWrapper theme={theme}>
|
||||
<FontIcon icon="icon ok" />
|
||||
</InfoWrapper>{' '}
|
||||
Connected
|
||||
</>
|
||||
)}
|
||||
{status.name == 'error' && (
|
||||
<>
|
||||
<ErrorWrapper theme={theme}>
|
||||
<FontIcon icon="icon error" />
|
||||
</ErrorWrapper>{' '}
|
||||
Error
|
||||
</>
|
||||
)}
|
||||
</StatusBarItem>
|
||||
)}
|
||||
{!connection && (
|
||||
<StatusBarItem>
|
||||
<>
|
||||
<FontIcon icon="icon loading" /> Loading
|
||||
<FontIcon icon="icon disconnected" /> Not connected
|
||||
</>
|
||||
)}
|
||||
{status.name == 'ok' && (
|
||||
<>
|
||||
<InfoWrapper theme={theme}>
|
||||
<FontIcon icon="icon ok" />
|
||||
</InfoWrapper>{' '}
|
||||
Connected
|
||||
</>
|
||||
)}
|
||||
{status.name == 'error' && (
|
||||
<>
|
||||
<ErrorWrapper theme={theme}>
|
||||
<FontIcon icon="icon error" />
|
||||
</ErrorWrapper>{' '}
|
||||
Error
|
||||
</>
|
||||
)}
|
||||
</Item>
|
||||
)}
|
||||
{!connection && (
|
||||
<Item>
|
||||
<>
|
||||
<FontIcon icon="icon disconnected" /> Not connected
|
||||
</>
|
||||
</Item>
|
||||
)}
|
||||
</StatusBarItem>
|
||||
)}
|
||||
</StatusbarContainer>
|
||||
<StatusbarContainer ref={statusbarPortalRef}></StatusbarContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user