mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Adding logs tab bottom toolbar (#1951)
* Adding bottom toolbar to logs tab Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Making bottom toolbar responsive Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Using generic search input clear button Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fixing log test selectors Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
c48816ca5c
commit
83ed44f670
@ -505,16 +505,16 @@ describe("Lens integration tests", () => {
|
||||
await app.client.waitForVisible(".Drawer");
|
||||
await app.client.click(".drawer-title .Menu li:nth-child(2)");
|
||||
// Check if controls are available
|
||||
await app.client.waitForVisible(".PodLogs .VirtualList");
|
||||
await app.client.waitForVisible(".PodLogControls");
|
||||
await app.client.waitForVisible(".PodLogControls .SearchInput");
|
||||
await app.client.waitForVisible(".PodLogControls .SearchInput input");
|
||||
await app.client.waitForVisible(".Logs .VirtualList");
|
||||
await app.client.waitForVisible(".LogResourceSelector");
|
||||
await app.client.waitForVisible(".LogResourceSelector .SearchInput");
|
||||
await app.client.waitForVisible(".LogResourceSelector .SearchInput input");
|
||||
// Search for semicolon
|
||||
await app.client.keys(":");
|
||||
await app.client.waitForVisible(".PodLogs .list span.active");
|
||||
await app.client.waitForVisible(".Logs .list span.active");
|
||||
// Click through controls
|
||||
await app.client.click(".PodLogControls .timestamps-icon");
|
||||
await app.client.click(".PodLogControls .undo-icon");
|
||||
await app.client.click(".LogControls .show-timestamps");
|
||||
await app.client.click(".LogControls .show-previous");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -38,4 +38,4 @@ export * from "../../renderer/components/+events/kube-event-details";
|
||||
// specific exports
|
||||
export * from "../../renderer/components/status-brick";
|
||||
export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store";
|
||||
export { createPodLogsTab } from "../../renderer/components/dock/pod-logs.store";
|
||||
export { createPodLogsTab } from "../../renderer/components/dock/log.store";
|
||||
|
||||
@ -30,7 +30,7 @@ export class Checkbox extends React.PureComponent<CheckboxProps> {
|
||||
|
||||
render() {
|
||||
const { label, inline, className, value, theme, children, ...inputProps } = this.props;
|
||||
const componentClass = cssNames("Checkbox flex", className, {
|
||||
const componentClass = cssNames("Checkbox flex align-center", className, {
|
||||
inline,
|
||||
checked: value,
|
||||
disabled: this.props.disabled,
|
||||
|
||||
@ -7,7 +7,7 @@ import { DockTab } from "./dock-tab";
|
||||
import { IDockTab } from "./dock.store";
|
||||
import { isEditResourceTab } from "./edit-resource.store";
|
||||
import { isInstallChartTab } from "./install-chart.store";
|
||||
import { isPodLogsTab } from "./pod-logs.store";
|
||||
import { isLogsTab } from "./log.store";
|
||||
import { TerminalTab } from "./terminal-tab";
|
||||
import { isTerminalTab } from "./terminal.store";
|
||||
import { isUpgradeChartTab } from "./upgrade-chart.store";
|
||||
@ -33,7 +33,7 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) =
|
||||
return <DockTab value={tab} icon={<Icon svg="install" />} />;
|
||||
}
|
||||
|
||||
if (isPodLogsTab(tab)) {
|
||||
if (isLogsTab(tab)) {
|
||||
return <DockTab value={tab} icon="subject" />;
|
||||
}
|
||||
};
|
||||
|
||||
@ -16,8 +16,8 @@ import { EditResource } from "./edit-resource";
|
||||
import { isEditResourceTab } from "./edit-resource.store";
|
||||
import { InstallChart } from "./install-chart";
|
||||
import { isInstallChartTab } from "./install-chart.store";
|
||||
import { PodLogs } from "./pod-logs";
|
||||
import { isPodLogsTab } from "./pod-logs.store";
|
||||
import { Logs } from "./logs";
|
||||
import { isLogsTab } from "./log.store";
|
||||
import { TerminalWindow } from "./terminal-window";
|
||||
import { createTerminalTab, isTerminalTab } from "./terminal.store";
|
||||
import { UpgradeChart } from "./upgrade-chart";
|
||||
@ -64,7 +64,7 @@ export class Dock extends React.Component<Props> {
|
||||
{isInstallChartTab(tab) && <InstallChart tab={tab} />}
|
||||
{isUpgradeChartTab(tab) && <UpgradeChart tab={tab} />}
|
||||
{isTerminalTab(tab) && <TerminalWindow tab={tab} />}
|
||||
{isPodLogsTab(tab) && <PodLogs tab={tab} />}
|
||||
{isLogsTab(tab) && <Logs tab={tab} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
@include hidden-scrollbar;
|
||||
|
||||
background: $dockInfoBackground;
|
||||
border-bottom: 1px solid $dockInfoBorderColor;
|
||||
padding: $padding $padding * 2;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
||||
6
src/renderer/components/dock/log-controls.scss
Normal file
6
src/renderer/components/dock/log-controls.scss
Normal file
@ -0,0 +1,6 @@
|
||||
.LogControls {
|
||||
@include hidden-scrollbar;
|
||||
|
||||
background: $dockInfoBackground;
|
||||
padding: $padding $padding * 2;
|
||||
}
|
||||
68
src/renderer/components/dock/log-controls.tsx
Normal file
68
src/renderer/components/dock/log-controls.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import "./log-controls.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { Pod } from "../../api/endpoints";
|
||||
import { cssNames, saveFileDialog } from "../../utils";
|
||||
import { IPodLogsData, podLogsStore } from "./log.store";
|
||||
import { Checkbox } from "../checkbox";
|
||||
import { Icon } from "../icon";
|
||||
|
||||
interface Props {
|
||||
tabData: IPodLogsData
|
||||
logs: string[]
|
||||
save: (data: Partial<IPodLogsData>) => void
|
||||
reload: () => void
|
||||
}
|
||||
|
||||
export const LogControls = observer((props: Props) => {
|
||||
const { tabData, save, reload, logs } = props;
|
||||
const { showTimestamps, previous } = tabData;
|
||||
const since = logs.length ? podLogsStore.getTimestamps(logs[0]) : null;
|
||||
const pod = new Pod(tabData.pod);
|
||||
|
||||
const toggleTimestamps = () => {
|
||||
save({ showTimestamps: !showTimestamps });
|
||||
};
|
||||
|
||||
const togglePrevious = () => {
|
||||
save({ previous: !previous });
|
||||
reload();
|
||||
};
|
||||
|
||||
const downloadLogs = () => {
|
||||
const fileName = pod.getName();
|
||||
const logsToDownload = showTimestamps ? logs : podLogsStore.logsWithoutTimestamps;
|
||||
|
||||
saveFileDialog(`${fileName}.log`, logsToDownload.join("\n"), "text/plain");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cssNames("LogControls flex gaps align-center justify-space-between wrap")}>
|
||||
<div className="time-range">
|
||||
{since && `Logs from ${new Date(since[0]).toLocaleString()}`}
|
||||
</div>
|
||||
<div className="flex gaps align-center">
|
||||
<Checkbox
|
||||
label="Show timestamps"
|
||||
value={showTimestamps}
|
||||
onChange={toggleTimestamps}
|
||||
className="show-timestamps"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Show previous terminated container"
|
||||
value={previous}
|
||||
onChange={togglePrevious}
|
||||
className="show-previous"
|
||||
/>
|
||||
<Icon
|
||||
material="get_app"
|
||||
onClick={downloadLogs}
|
||||
tooltip="Download"
|
||||
className="download-icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
.PodLogList {
|
||||
.LogList {
|
||||
--overlay-bg: #8cc474b8;
|
||||
--overlay-active-bg: orange;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import "./pod-log-list.scss";
|
||||
import "./log-list.scss";
|
||||
|
||||
import React from "react";
|
||||
import AnsiUp from "ansi_up";
|
||||
@ -14,7 +14,7 @@ import { Button } from "../button";
|
||||
import { Icon } from "../icon";
|
||||
import { Spinner } from "../spinner";
|
||||
import { VirtualList } from "../virtual-list";
|
||||
import { podLogsStore } from "./pod-logs.store";
|
||||
import { podLogsStore } from "./log.store";
|
||||
|
||||
interface Props {
|
||||
logs: string[]
|
||||
@ -26,7 +26,7 @@ interface Props {
|
||||
const colorConverter = new AnsiUp();
|
||||
|
||||
@observer
|
||||
export class PodLogList extends React.Component<Props> {
|
||||
export class LogList extends React.Component<Props> {
|
||||
@observable isJumpButtonVisible = false;
|
||||
@observable isLastLineVisible = true;
|
||||
|
||||
@ -206,19 +206,23 @@ export class PodLogList extends React.Component<Props> {
|
||||
const rowHeights = new Array(this.logs.length).fill(this.lineHeight);
|
||||
|
||||
if (isInitLoading) {
|
||||
return <Spinner center/>;
|
||||
return (
|
||||
<div className="LogList flex box grow align-center justify-center">
|
||||
<Spinner center/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.logs.length) {
|
||||
return (
|
||||
<div className="PodLogList flex box grow align-center justify-center">
|
||||
<div className="LogList flex box grow align-center justify-center">
|
||||
There are no logs available for container
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cssNames("PodLogList flex", { isLoading })}>
|
||||
<div className={cssNames("LogList flex", { isLoading })}>
|
||||
<VirtualList
|
||||
items={this.logs}
|
||||
rowHeights={rowHeights}
|
||||
@ -1,4 +1,4 @@
|
||||
.PodLogControls {
|
||||
.LogResourceSelector {
|
||||
.Select {
|
||||
min-width: 150px;
|
||||
}
|
||||
66
src/renderer/components/dock/log-resource-selector.tsx
Normal file
66
src/renderer/components/dock/log-resource-selector.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import "./log-resource-selector.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { IPodContainer, Pod } from "../../api/endpoints";
|
||||
import { Badge } from "../badge";
|
||||
import { Select, SelectOption } from "../select";
|
||||
import { IPodLogsData } from "./log.store";
|
||||
|
||||
interface Props {
|
||||
tabData: IPodLogsData
|
||||
save: (data: Partial<IPodLogsData>) => void
|
||||
reload: () => void
|
||||
}
|
||||
|
||||
export const LogResourceSelector = observer((props: Props) => {
|
||||
const { tabData, save, reload } = props;
|
||||
const { selectedContainer, containers, initContainers } = tabData;
|
||||
const pod = new Pod(tabData.pod);
|
||||
|
||||
const onContainerChange = (option: SelectOption) => {
|
||||
const { containers, initContainers } = tabData;
|
||||
|
||||
save({
|
||||
selectedContainer: containers
|
||||
.concat(initContainers)
|
||||
.find(container => container.name === option.value)
|
||||
});
|
||||
reload();
|
||||
};
|
||||
|
||||
const getSelectOptions = (containers: IPodContainer[]) => {
|
||||
return containers.map(container => {
|
||||
return {
|
||||
value: container.name,
|
||||
label: container.name
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const containerSelectOptions = [
|
||||
{
|
||||
label: `Containers`,
|
||||
options: getSelectOptions(containers)
|
||||
},
|
||||
{
|
||||
label: `Init Containers`,
|
||||
options: getSelectOptions(initContainers),
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="LogResourceSelector flex gaps align-center">
|
||||
<span>Namespace</span> <Badge label={pod.getNs()}/>
|
||||
<span>Pod</span> <Badge label={pod.getName()}/>
|
||||
<span>Container</span>
|
||||
<Select
|
||||
options={containerSelectOptions}
|
||||
value={{ label: selectedContainer.name, value: selectedContainer.name }}
|
||||
onChange={onContainerChange}
|
||||
autoConvertOptions={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@ -1,10 +1,13 @@
|
||||
.PodLogsSearch {
|
||||
.LogSearch {
|
||||
.SearchInput {
|
||||
min-width: 150px;
|
||||
width: 150px;
|
||||
|
||||
.find-count {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
label {
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import "./pod-log-search.scss";
|
||||
import "./log-search.scss";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
@ -16,7 +16,7 @@ interface Props extends PodLogSearchProps {
|
||||
logs: string[]
|
||||
}
|
||||
|
||||
export const PodLogSearch = observer((props: Props) => {
|
||||
export const LogSearch = observer((props: Props) => {
|
||||
const { logs, onSearch, toPrevOverlay, toNextOverlay } = props;
|
||||
const { setNextOverlayActive, setPrevOverlayActive, searchQuery, occurrences, activeFind, totalFinds } = searchStore;
|
||||
const jumpDisabled = !searchQuery || !occurrences.length;
|
||||
@ -57,11 +57,11 @@ export const PodLogSearch = observer((props: Props) => {
|
||||
}, [logs]);
|
||||
|
||||
return (
|
||||
<div className="PodLogsSearch flex box grow justify-flex-end gaps align-center">
|
||||
<div className="LogSearch flex box grow justify-flex-end gaps align-center">
|
||||
<SearchInput
|
||||
value={searchQuery}
|
||||
onChange={setSearch}
|
||||
showClearIcon={false}
|
||||
showClearIcon={true}
|
||||
contentRight={totalFinds > 0 && findCounts}
|
||||
onClear={onClear}
|
||||
onKeyDown={onKeyDown}
|
||||
@ -78,11 +78,6 @@ export const PodLogSearch = observer((props: Props) => {
|
||||
onClick={onNextOverlay}
|
||||
disabled={jumpDisabled}
|
||||
/>
|
||||
<Icon
|
||||
material="close"
|
||||
tooltip={`Clear`}
|
||||
onClick={onClear}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@ -21,7 +21,7 @@ type PodLogLine = string;
|
||||
export const logRange = 500;
|
||||
|
||||
@autobind()
|
||||
export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
||||
export class LogStore extends DockTabStore<IPodLogsData> {
|
||||
private refresher = interval(10, () => {
|
||||
const id = dockStore.selectedTabId;
|
||||
|
||||
@ -39,7 +39,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
||||
autorun(() => {
|
||||
const { selectedTab, isOpen } = dockStore;
|
||||
|
||||
if (isPodLogsTab(selectedTab) && isOpen) {
|
||||
if (isLogsTab(selectedTab) && isOpen) {
|
||||
this.refresher.start();
|
||||
} else {
|
||||
this.refresher.stop();
|
||||
@ -203,7 +203,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
||||
}
|
||||
}
|
||||
|
||||
export const podLogsStore = new PodLogsStore();
|
||||
export const podLogsStore = new LogStore();
|
||||
|
||||
export function createPodLogsTab(data: IPodLogsData, tabParams: Partial<IDockTab> = {}) {
|
||||
const podId = data.pod.getId();
|
||||
@ -227,6 +227,6 @@ export function createPodLogsTab(data: IPodLogsData, tabParams: Partial<IDockTab
|
||||
return tab;
|
||||
}
|
||||
|
||||
export function isPodLogsTab(tab: IDockTab) {
|
||||
export function isLogsTab(tab: IDockTab) {
|
||||
return tab && tab.kind === TabKind.POD_LOGS;
|
||||
}
|
||||
@ -6,9 +6,11 @@ import { searchStore } from "../../../common/search-store";
|
||||
import { autobind } from "../../utils";
|
||||
import { IDockTab } from "./dock.store";
|
||||
import { InfoPanel } from "./info-panel";
|
||||
import { PodLogControls } from "./pod-log-controls";
|
||||
import { PodLogList } from "./pod-log-list";
|
||||
import { IPodLogsData, podLogsStore } from "./pod-logs.store";
|
||||
import { LogResourceSelector } from "./log-resource-selector";
|
||||
import { LogList } from "./log-list";
|
||||
import { IPodLogsData, podLogsStore } from "./log.store";
|
||||
import { LogSearch } from "./log-search";
|
||||
import { LogControls } from "./log-controls";
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@ -16,10 +18,10 @@ interface Props {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class PodLogs extends React.Component<Props> {
|
||||
export class Logs extends React.Component<Props> {
|
||||
@observable isLoading = true;
|
||||
|
||||
private logListElement = React.createRef<PodLogList>(); // A reference for VirtualList component
|
||||
private logListElement = React.createRef<LogList>(); // A reference for VirtualList component
|
||||
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this,
|
||||
@ -79,25 +81,26 @@ export class PodLogs extends React.Component<Props> {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
render() {
|
||||
renderResourceSelector() {
|
||||
const logs = podLogsStore.logs;
|
||||
|
||||
const searchLogs = this.tabData.showTimestamps ? logs : podLogsStore.logsWithoutTimestamps;
|
||||
const controls = (
|
||||
<PodLogControls
|
||||
ready={!this.isLoading}
|
||||
tabId={this.tabId}
|
||||
<div className="flex gaps">
|
||||
<LogResourceSelector
|
||||
tabData={this.tabData}
|
||||
logs={logs}
|
||||
save={this.save}
|
||||
reload={this.reload}
|
||||
/>
|
||||
<LogSearch
|
||||
onSearch={this.onSearch}
|
||||
logs={searchLogs}
|
||||
toPrevOverlay={this.toOverlay}
|
||||
toNextOverlay={this.toOverlay}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="PodLogs flex column">
|
||||
<InfoPanel
|
||||
tabId={this.props.tab.id}
|
||||
controls={controls}
|
||||
@ -105,13 +108,28 @@ export class PodLogs extends React.Component<Props> {
|
||||
showButtons={false}
|
||||
showStatusPanel={false}
|
||||
/>
|
||||
<PodLogList
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const logs = podLogsStore.logs;
|
||||
|
||||
return (
|
||||
<div className="PodLogs flex column">
|
||||
{this.renderResourceSelector()}
|
||||
<LogList
|
||||
logs={logs}
|
||||
id={this.tabId}
|
||||
isLoading={this.isLoading}
|
||||
load={this.load}
|
||||
ref={this.logListElement}
|
||||
/>
|
||||
<LogControls
|
||||
logs={logs}
|
||||
tabData={this.tabData}
|
||||
save={this.save}
|
||||
reload={this.reload}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,126 +0,0 @@
|
||||
import "./pod-log-controls.scss";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { IPodLogsData, podLogsStore } from "./pod-logs.store";
|
||||
import { Select, SelectOption } from "../select";
|
||||
import { Badge } from "../badge";
|
||||
import { Icon } from "../icon";
|
||||
import { cssNames, saveFileDialog } from "../../utils";
|
||||
import { Pod } from "../../api/endpoints";
|
||||
import { PodLogSearch, PodLogSearchProps } from "./pod-log-search";
|
||||
|
||||
interface Props extends PodLogSearchProps {
|
||||
ready: boolean
|
||||
tabId: string
|
||||
tabData: IPodLogsData
|
||||
logs: string[]
|
||||
save: (data: Partial<IPodLogsData>) => void
|
||||
reload: () => void
|
||||
onSearch: (query: string) => void
|
||||
}
|
||||
|
||||
export const PodLogControls = observer((props: Props) => {
|
||||
const { tabData, save, reload, logs } = props;
|
||||
const { selectedContainer, showTimestamps, previous } = tabData;
|
||||
const since = logs.length ? podLogsStore.getTimestamps(logs[0]) : null;
|
||||
const pod = new Pod(tabData.pod);
|
||||
|
||||
const toggleTimestamps = () => {
|
||||
save({ showTimestamps: !showTimestamps });
|
||||
};
|
||||
|
||||
const togglePrevious = () => {
|
||||
save({ previous: !previous });
|
||||
reload();
|
||||
};
|
||||
|
||||
const downloadLogs = () => {
|
||||
const fileName = selectedContainer ? selectedContainer.name : pod.getName();
|
||||
const logsToDownload = showTimestamps ? logs : podLogsStore.logsWithoutTimestamps;
|
||||
|
||||
saveFileDialog(`${fileName}.log`, logsToDownload.join("\n"), "text/plain");
|
||||
};
|
||||
|
||||
const onContainerChange = (option: SelectOption) => {
|
||||
const { containers, initContainers } = tabData;
|
||||
|
||||
save({
|
||||
selectedContainer: containers
|
||||
.concat(initContainers)
|
||||
.find(container => container.name === option.value)
|
||||
});
|
||||
reload();
|
||||
};
|
||||
|
||||
const containerSelectOptions = () => {
|
||||
const { containers, initContainers } = tabData;
|
||||
|
||||
return [
|
||||
{
|
||||
label: `Containers`,
|
||||
options: containers.map(container => {
|
||||
return { value: container.name };
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: `Init Containers`,
|
||||
options: initContainers.map(container => {
|
||||
return { value: container.name };
|
||||
}),
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const formatOptionLabel = (option: SelectOption) => {
|
||||
const { value, label } = option;
|
||||
|
||||
return label || <><Icon small material="view_carousel"/> {value}</>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="PodLogControls flex gaps align-center">
|
||||
<span>Pod:</span> <Badge label={pod.getName()}/>
|
||||
<span>Namespace:</span> <Badge label={pod.getNs()}/>
|
||||
<span>Container</span>
|
||||
<Select
|
||||
options={containerSelectOptions()}
|
||||
value={{ value: selectedContainer.name }}
|
||||
formatOptionLabel={formatOptionLabel}
|
||||
onChange={onContainerChange}
|
||||
autoConvertOptions={false}
|
||||
/>
|
||||
<div className="time-range">
|
||||
{since && (
|
||||
<>
|
||||
Since{" "}
|
||||
<b>{new Date(since[0]).toLocaleString()}</b>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex box grow gaps align-center">
|
||||
<Icon
|
||||
material="av_timer"
|
||||
onClick={toggleTimestamps}
|
||||
className={cssNames("timestamps-icon", { active: showTimestamps })}
|
||||
tooltip={`${showTimestamps ? `Hide` : `Show`} timestamps`}
|
||||
/>
|
||||
<Icon
|
||||
material="history"
|
||||
onClick={togglePrevious}
|
||||
className={cssNames("undo-icon", { active: previous })}
|
||||
tooltip={(previous ? `Show current logs` : `Show previous terminated container logs`)}
|
||||
/>
|
||||
<Icon
|
||||
material="get_app"
|
||||
onClick={downloadLogs}
|
||||
tooltip={`Save`}
|
||||
className="download-icon"
|
||||
/>
|
||||
<PodLogSearch
|
||||
{...props}
|
||||
logs={showTimestamps ? logs : podLogsStore.logsWithoutTimestamps}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user