feat: optimize vmui-log bundle size (#4602)

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
Yury Molodov 2023-07-20 01:49:38 +02:00 committed by Aliaksandr Valialkin
parent 443c266406
commit 44b4963ac6
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
36 changed files with 1734 additions and 2631 deletions

View file

@ -1,7 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires,no-undef
const {override, addExternalBabelPlugin, addWebpackAlias} = require("customize-cra");
/* eslint-disable */
const { override, addExternalBabelPlugin, addWebpackAlias, addWebpackPlugin } = require("customize-cra");
const webpack = require("webpack");
// eslint-disable-next-line no-undef
module.exports = override(
addExternalBabelPlugin("@babel/plugin-proposal-nullish-coalescing-operator"),
addWebpackAlias({
@ -9,5 +9,16 @@ module.exports = override(
"react-dom/test-utils": "preact/test-utils",
"react-dom": "preact/compat", // Must be below test-utils
"react/jsx-runtime": "preact/jsx-runtime"
})
);
}),
addWebpackPlugin(
new webpack.NormalModuleReplacementPlugin(
/\.\/App/,
function (resource) {
// eslint-disable-next-line no-undef
if (process.env.REACT_APP_LOGS === "true") {
resource.request = "./AppLogs";
}
}
)
)
);

File diff suppressed because it is too large Load diff

View file

@ -4,15 +4,11 @@
"private": true,
"homepage": "./",
"dependencies": {
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^14.0.4",
"@types/jest": "^27.4.1",
"@types/lodash.debounce": "^4.0.6",
"@types/lodash.get": "^4.4.6",
"@types/lodash.throttle": "^4.1.6",
"@types/marked": "^4.0.2",
"@types/node": "^17.0.21",
"@types/marked": "^5.0.0",
"@types/node": "^20.4.0",
"@types/qs": "^6.9.7",
"@types/react-input-mask": "^3.0.2",
"@types/react-router-dom": "^5.3.3",
@ -22,24 +18,25 @@
"lodash.debounce": "^4.0.8",
"lodash.get": "^4.4.2",
"lodash.throttle": "^4.1.1",
"marked": "^4.0.14",
"marked": "^5.1.0",
"preact": "^10.7.1",
"qs": "^6.10.3",
"react-input-mask": "^2.0.4",
"react-router-dom": "^6.10.0",
"sass": "^1.56.0",
"source-map-explorer": "^2.5.3",
"typescript": "~4.6.2",
"uplot": "^1.6.19",
"web-vitals": "^2.1.4"
"web-vitals": "^3.3.2"
},
"scripts": {
"start": "react-app-rewired start",
"start:logs": "cross-env REACT_APP_LOGS=true npm run start",
"build": "GENERATE_SOURCEMAP=false react-app-rewired build",
"build:logs": "cross-env REACT_APP_LOGS=true npm run build",
"test": "react-app-rewired test",
"lint": "eslint src --ext tsx,ts",
"lint:fix": "eslint src --ext tsx,ts --fix"
"lint:fix": "eslint src --ext tsx,ts --fix",
"analyze": "source-map-explorer 'build/static/js/*.js'"
},
"eslintConfig": {
"extends": [
@ -61,12 +58,17 @@
},
"devDependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@types/react-dom": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"cross-env": "^7.0.3",
"customize-cra": "^1.0.0",
"eslint": "^8.44.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react": "^7.29.4",
"react-app-rewired": "^2.2.1"
"react-app-rewired": "^2.2.1",
"webpack": "^5.88.1"
},
"overrides": {
"react-app-rewired": {

View file

@ -1,9 +0,0 @@
import React from "preact/compat";
import { render, screen } from "@testing-library/react";
import App from "./App";
test("renders header", () => {
render(<App />);
const headerElement = screen.getByText(/VMUI/i);
expect(headerElement).toBeInTheDocument();
});

View file

@ -2,7 +2,7 @@ import React, { FC, useState } from "preact/compat";
import { HashRouter, Route, Routes } from "react-router-dom";
import router from "./router";
import AppContextProvider from "./contexts/AppContextProvider";
import Layout from "./components/Layout/Layout";
import MainLayout from "./layouts/MainLayout/MainLayout";
import CustomPanel from "./pages/CustomPanel";
import DashboardsLayout from "./pages/PredefinedPanels";
import CardinalityPanel from "./pages/CardinalityPanel";
@ -14,11 +14,9 @@ import PreviewIcons from "./components/Main/Icons/PreviewIcons";
import WithTemplate from "./pages/WithTemplate";
import Relabel from "./pages/Relabel";
import ExploreLogs from "./pages/ExploreLogs/ExploreLogs";
import ActiveQueries from "./pages/ActiveQueries";
import from "./pages/ActiveQueries";
const App: FC = () => {
const { REACT_APP_LOGS } = process.env;
const [loadedTheme, setLoadedTheme] = useState(false);
return <>
@ -30,54 +28,50 @@ const App: FC = () => {
<Routes>
<Route
path={"/"}
element={<Layout/>}
element={<MainLayout/>}
>
{!REACT_APP_LOGS && (
<>
<Route
path={router.home}
element={<CustomPanel/>}
/>
<Route
path={router.metrics}
element={<ExploreMetrics/>}
/>
<Route
path={router.cardinality}
element={<CardinalityPanel/>}
/>
<Route
path={router.topQueries}
element={<TopQueries/>}
/>
<Route
path={router.trace}
element={<TracePage/>}
/>
<Route
path={router.dashboards}
element={<DashboardsLayout/>}
/>
<Route
path={router.withTemplate}
element={<WithTemplate/>}
/>
<Route
path={router.relabel}
element={<Relabel/>}
/>
<Route
path={router.activeQueries}
element={<ActiveQueries/>}
/>
<Route
path={router.icons}
element={<PreviewIcons/>}
/>
</>
)}
<Route
path={REACT_APP_LOGS ? "/" : router.logs}
path={router.home}
element={<CustomPanel/>}
/>
<Route
path={router.metrics}
element={<ExploreMetrics/>}
/>
<Route
path={router.cardinality}
element={<CardinalityPanel/>}
/>
<Route
path={router.topQueries}
element={<TopQueries/>}
/>
<Route
path={router.trace}
element={<TracePage/>}
/>
<Route
path={router.dashboards}
element={<DashboardsLayout/>}
/>
<Route
path={router.withTemplate}
element={<WithTemplate/>}
/>
<Route
path={router.relabel}
element={<Relabel/>}
/>
<Route
path={router.activeQueries}
element={<ActiveQueries/>}
/>
<Route
path={router.icons}
element={<PreviewIcons/>}
/>
<Route
path={router.logs}
element={<ExploreLogs/>}
/>
</Route>

View file

@ -0,0 +1,35 @@
import React, { FC, useState } from "preact/compat";
import { HashRouter, Route, Routes } from "react-router-dom";
import AppContextProvider from "./contexts/AppContextProvider";
import ThemeProvider from "./components/Main/ThemeProvider/ThemeProvider";
import ExploreLogs from "./pages/ExploreLogs/ExploreLogs";
import LogsLayout from "./layouts/LogsLayout/LogsLayout";
const AppLogs: FC = () => {
const [loadedTheme, setLoadedTheme] = useState(false);
return <>
<HashRouter>
<AppContextProvider>
<>
<ThemeProvider onLoaded={setLoadedTheme}/>
{loadedTheme && (
<Routes>
<Route
path={"/"}
element={<LogsLayout/>}
>
<Route
path={"/"}
element={<ExploreLogs/>}
/>
</Route>
</Routes>
)}
</>
</AppContextProvider>
</HashRouter>
</>;
};
export default AppLogs;

View file

@ -21,6 +21,7 @@ import { getTenantIdFromUrl } from "../../../utils/tenants";
const title = "Settings";
const GlobalSettings: FC = () => {
const { REACT_APP_LOGS } = process.env;
const { isMobile } = useDeviceDetect();
const appModeEnable = getAppModeEnable();
@ -74,6 +75,40 @@ const GlobalSettings: FC = () => {
setServerUrl(stateServerUrl);
}, [stateServerUrl]);
const controls = [
{
show: !appModeEnable && !REACT_APP_LOGS,
component: <ServerConfigurator
stateServerUrl={stateServerUrl}
serverUrl={serverUrl}
onChange={setServerUrl}
onEnter={handlerApply}
/>
},
{
show: !REACT_APP_LOGS,
component: <LimitsConfigurator
limits={limits}
onChange={setLimits}
onEnter={handlerApply}
/>
},
{
show: true,
component: <Timezones
timezoneState={timezone}
onChange={setTimezone}
/>
},
{
show: !appModeEnable,
component: <ThemeControl
theme={theme}
onChange={handleChangeTheme}
/>
}
].filter(control => control.show);
return <>
{isMobile ? (
<div
@ -110,37 +145,14 @@ const GlobalSettings: FC = () => {
"vm-server-configurator_mobile": isMobile
})}
>
{!appModeEnable && (
<div className="vm-server-configurator__input">
<ServerConfigurator
stateServerUrl={stateServerUrl}
serverUrl={serverUrl}
onChange={setServerUrl}
onEnter={handlerApply}
/>
{controls.map((control, index) => (
<div
className="vm-server-configurator__input"
key={index}
>
{control.component}
</div>
)}
<div className="vm-server-configurator__input">
<LimitsConfigurator
limits={limits}
onChange={setLimits}
onEnter={handlerApply}
/>
</div>
<div className="vm-server-configurator__input">
<Timezones
timezoneState={timezone}
onChange={setTimezone}
/>
</div>
{!appModeEnable && (
<div className="vm-server-configurator__input">
<ThemeControl
theme={theme}
onChange={handleChangeTheme}
/>
</div>
)}
))}
<div className="vm-server-configurator-footer">
<Button
color="error"

View file

@ -1,105 +0,0 @@
import React, { FC, useMemo } from "preact/compat";
import { RouterOptions, routerOptions, RouterOptionsHeader } from "../../../../router";
import TenantsConfiguration from "../../../Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration";
import StepConfigurator from "../../../Configurators/StepConfigurator/StepConfigurator";
import { TimeSelector } from "../../../Configurators/TimeRangeSettings/TimeSelector/TimeSelector";
import CardinalityDatePicker from "../../../Configurators/CardinalityDatePicker/CardinalityDatePicker";
import { ExecutionControls } from "../../../Configurators/TimeRangeSettings/ExecutionControls/ExecutionControls";
import GlobalSettings from "../../../Configurators/GlobalSettings/GlobalSettings";
import ShortcutKeys from "../../../Main/ShortcutKeys/ShortcutKeys";
import { useLocation } from "react-router-dom";
import { useFetchAccountIds } from "../../../Configurators/GlobalSettings/TenantsConfiguration/hooks/useFetchAccountIds";
import Button from "../../../Main/Button/Button";
import { MoreIcon } from "../../../Main/Icons";
import "./style.scss";
import classNames from "classnames";
import { getAppModeEnable } from "../../../../utils/app-mode";
import Modal from "../../../Main/Modal/Modal";
import useBoolean from "../../../../hooks/useBoolean";
interface HeaderControlsProp {
displaySidebar: boolean
isMobile?: boolean
headerSetup?: RouterOptionsHeader
accountIds?: string[]
}
const Controls: FC<HeaderControlsProp> = ({
displaySidebar,
isMobile,
headerSetup,
accountIds
}) => {
return (
<div
className={classNames({
"vm-header-controls": true,
"vm-header-controls_mobile": isMobile,
})}
>
{headerSetup?.tenant && <TenantsConfiguration accountIds={accountIds || []}/>}
{headerSetup?.stepControl && <StepConfigurator/>}
{headerSetup?.timeSelector && <TimeSelector/>}
{headerSetup?.cardinalityDatePicker && <CardinalityDatePicker/>}
{headerSetup?.executionControls && <ExecutionControls/>}
<GlobalSettings/>
{!displaySidebar && <ShortcutKeys/>}
</div>
);
};
const HeaderControls: FC<HeaderControlsProp> = (props) => {
const appModeEnable = getAppModeEnable();
const { pathname } = useLocation();
const { accountIds } = useFetchAccountIds();
const {
value: openList,
toggle: handleToggleList,
setFalse: handleCloseList,
} = useBoolean(false);
const headerSetup = useMemo(() => {
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
}, [pathname]);
if (props.isMobile) {
return (
<>
<div>
<Button
className={classNames({
"vm-header-button": !appModeEnable
})}
startIcon={<MoreIcon/>}
onClick={handleToggleList}
/>
</div>
<Modal
title={"Controls"}
onClose={handleCloseList}
isOpen={openList}
className={classNames({
"vm-header-controls-modal": true,
"vm-header-controls-modal_open": openList,
})}
>
<Controls
{...props}
accountIds={accountIds}
headerSetup={headerSetup}
/>
</Modal>
</>
);
}
return <Controls
{...props}
accountIds={accountIds}
headerSetup={headerSetup}
/>;
};
export default HeaderControls;

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
import React, { useState, useMemo } from "react";
import classNames from "classnames";
import { ArrowDropDownIcon, CopyIcon, DoneIcon } from "../Main/Icons";
import { getComparator, stableSort } from "../../pages/CardinalityPanel/Table/helpers";
import { getComparator, stableSort } from "./helpers";
import Tooltip from "../Main/Tooltip/Tooltip";
import Button from "../Main/Button/Button";
import { useEffect } from "preact/compat";

View file

@ -1,15 +1,15 @@
import React, { FC, useEffect, useRef, useMemo } from "preact/compat";
import Button from "../../../../components/Main/Button/Button";
import { RestartIcon, SettingsIcon } from "../../../../components/Main/Icons";
import Popper from "../../../../components/Main/Popper/Popper";
import Button from "../../Main/Button/Button";
import { RestartIcon, SettingsIcon } from "../../Main/Icons";
import Popper from "../../Main/Popper/Popper";
import "./style.scss";
import Checkbox from "../../../../components/Main/Checkbox/Checkbox";
import Tooltip from "../../../../components/Main/Tooltip/Tooltip";
import Switch from "../../../../components/Main/Switch/Switch";
import { arrayEquals } from "../../../../utils/array";
import Checkbox from "../../Main/Checkbox/Checkbox";
import Tooltip from "../../Main/Tooltip/Tooltip";
import Switch from "../../Main/Switch/Switch";
import { arrayEquals } from "../../../utils/array";
import classNames from "classnames";
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
import useBoolean from "../../../../hooks/useBoolean";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import useBoolean from "../../../hooks/useBoolean";
const title = "Table settings";

View file

@ -1,4 +1,4 @@
import { Order } from "./types";
import { Order } from "../../pages/CardinalityPanel/Table/types";
export function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
if (b[orderBy] < a[orderBy]) {

View file

@ -1,8 +1,8 @@
import React, { FC } from "preact/compat";
import dayjs from "dayjs";
import "./style.scss";
import { IssueIcon, LogoIcon, WikiIcon } from "../../Main/Icons";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import { CodeIcon, IssueIcon, LogoShortIcon, WikiIcon } from "../../components/Main/Icons";
import useDeviceDetect from "../../hooks/useDeviceDetect";
const Footer: FC = () => {
const { isMobile } = useDeviceDetect();
@ -15,9 +15,18 @@ const Footer: FC = () => {
href="https://victoriametrics.com/"
rel="me noreferrer"
>
<LogoIcon/>
<LogoShortIcon/>
victoriametrics.com
</a>
<a
className="vm-link vm-footer__link"
target="_blank"
href="https://docs.victoriametrics.com/MetricsQL.html"
rel="help noreferrer"
>
<CodeIcon/>
MetricsQL
</a>
<a
className="vm-link vm-footer__link"
target="_blank"

View file

@ -1,19 +1,24 @@
import React, { FC, useMemo } from "preact/compat";
import { useNavigate } from "react-router-dom";
import router from "../../../router";
import { getAppModeEnable, getAppModeParams } from "../../../utils/app-mode";
import { LogoFullIcon, LogoLogsIcon } from "../../Main/Icons";
import { getCssVariable } from "../../../utils/theme";
import router from "../../router";
import { getAppModeEnable, getAppModeParams } from "../../utils/app-mode";
import { LogoIcon, LogoLogsIcon } from "../../components/Main/Icons";
import { getCssVariable } from "../../utils/theme";
import "./style.scss";
import classNames from "classnames";
import { useAppState } from "../../../state/common/StateContext";
import { useAppState } from "../../state/common/StateContext";
import HeaderNav from "./HeaderNav/HeaderNav";
import SidebarHeader from "./SidebarNav/SidebarHeader";
import HeaderControls from "./HeaderControls/HeaderControls";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import useWindowSize from "../../../hooks/useWindowSize";
import HeaderControls, { ControlsProps } from "./HeaderControls/HeaderControls";
import useDeviceDetect from "../../hooks/useDeviceDetect";
import useWindowSize from "../../hooks/useWindowSize";
import { ComponentType } from "react";
const Header: FC = () => {
export interface HeaderProps {
controlsComponent: ComponentType<ControlsProps>
}
const Header: FC<HeaderProps> = ({ controlsComponent }) => {
const { REACT_APP_LOGS } = process.env;
const { isMobile } = useDeviceDetect();
@ -49,6 +54,7 @@ const Header: FC = () => {
"vm-header": true,
"vm-header_app": appModeEnable,
"vm-header_dark": isDarkTheme,
"vm-header_sidebar": displaySidebar,
"vm-header_mobile": isMobile
})}
style={{ background, color }}
@ -69,7 +75,7 @@ const Header: FC = () => {
onClick={onClickLogo}
style={{ color }}
>
{REACT_APP_LOGS ? <LogoLogsIcon/> : <LogoFullIcon/>}
{REACT_APP_LOGS ? <LogoLogsIcon/> : <LogoIcon/>}
</div>
)}
<HeaderNav
@ -78,7 +84,7 @@ const Header: FC = () => {
/>
</>
)}
{isMobile && (
{displaySidebar && (
<div
className={classNames({
"vm-header-logo": true,
@ -88,15 +94,14 @@ const Header: FC = () => {
onClick={onClickLogo}
style={{ color }}
>
{REACT_APP_LOGS ? <LogoLogsIcon/> : <LogoFullIcon/>}
{REACT_APP_LOGS ? <LogoLogsIcon/> : <LogoIcon/>}
</div>
)}
{
REACT_APP_LOGS ? null : <HeaderControls
displaySidebar={displaySidebar}
isMobile={isMobile}
/>
}
<HeaderControls
controlsComponent={controlsComponent}
displaySidebar={displaySidebar}
isMobile={isMobile}
/>
</header>;
};

View file

@ -0,0 +1,81 @@
import React, { FC, useMemo } from "preact/compat";
import { RouterOptions, routerOptions, RouterOptionsHeader } from "../../../router";
import { useLocation } from "react-router-dom";
import {
useFetchAccountIds
} from "../../../components/Configurators/GlobalSettings/TenantsConfiguration/hooks/useFetchAccountIds";
import Button from "../../../components/Main/Button/Button";
import { MoreIcon } from "../../../components/Main/Icons";
import classNames from "classnames";
import { getAppModeEnable } from "../../../utils/app-mode";
import Modal from "../../../components/Main/Modal/Modal";
import useBoolean from "../../../hooks/useBoolean";
import { HeaderProps } from "../Header";
import "./style.scss";
export interface ControlsProps {
displaySidebar: boolean;
isMobile?: boolean;
headerSetup?: RouterOptionsHeader;
accountIds?: string[];
}
const HeaderControls: FC<ControlsProps & HeaderProps> = ({
controlsComponent: ControlsComponent,
isMobile,
...props
}) => {
const appModeEnable = getAppModeEnable();
const { pathname } = useLocation();
const { accountIds } = useFetchAccountIds();
const {
value: openList,
toggle: handleToggleList,
setFalse: handleCloseList,
} = useBoolean(false);
const headerSetup = useMemo(() => {
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
}, [pathname]);
const controls = (
<ControlsComponent
{...props}
isMobile={isMobile}
accountIds={accountIds}
headerSetup={headerSetup}
/>
);
if (isMobile) {
return (
<>
<div>
<Button
className={classNames({
"vm-header-button": !appModeEnable
})}
startIcon={<MoreIcon/>}
onClick={handleToggleList}
/>
</div>
<Modal
title={"Controls"}
onClose={handleCloseList}
isOpen={openList}
className={classNames({
"vm-header-controls-modal": true,
"vm-header-controls-modal_open": openList,
})}
>
{controls}
</Modal>
</>
);
}
return controls;
};
export default HeaderControls;

View file

@ -1,14 +1,14 @@
import React, { FC, useMemo, useState } from "preact/compat";
import router, { routerOptions } from "../../../../router";
import { getAppModeEnable } from "../../../../utils/app-mode";
import router, { routerOptions } from "../../../router";
import { getAppModeEnable } from "../../../utils/app-mode";
import { useLocation } from "react-router-dom";
import { useDashboardsState } from "../../../../state/dashboards/DashboardsStateContext";
import { useDashboardsState } from "../../../state/dashboards/DashboardsStateContext";
import { useEffect } from "react";
import "./style.scss";
import NavItem from "./NavItem";
import NavSubItem from "./NavSubItem";
import classNames from "classnames";
import { defaultNavigation, logsNavigation } from "../../../../constants/navigation";
import { defaultNavigation, logsNavigation } from "../../../constants/navigation";
interface HeaderNavProps {
color: string

View file

@ -1,12 +1,12 @@
import React, { FC, useRef, useState } from "preact/compat";
import { useLocation } from "react-router-dom";
import classNames from "classnames";
import { ArrowDropDownIcon } from "../../../Main/Icons";
import Popper from "../../../Main/Popper/Popper";
import { ArrowDropDownIcon } from "../../../components/Main/Icons";
import Popper from "../../../components/Main/Popper/Popper";
import NavItem from "./NavItem";
import { useEffect } from "react";
import useBoolean from "../../../../hooks/useBoolean";
import { NavigationItem } from "../../../../constants/navigation";
import useBoolean from "../../../hooks/useBoolean";
import { NavigationItem } from "../../../constants/navigation";
interface NavItemProps {
activeMenu: string,

View file

@ -1,13 +1,13 @@
import React, { FC, useEffect, useRef } from "preact/compat";
import { useLocation } from "react-router-dom";
import ShortcutKeys from "../../../Main/ShortcutKeys/ShortcutKeys";
import ShortcutKeys from "../../../components/Main/ShortcutKeys/ShortcutKeys";
import classNames from "classnames";
import HeaderNav from "../HeaderNav/HeaderNav";
import useClickOutside from "../../../../hooks/useClickOutside";
import MenuBurger from "../../../Main/MenuBurger/MenuBurger";
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
import useClickOutside from "../../../hooks/useClickOutside";
import MenuBurger from "../../../components/Main/MenuBurger/MenuBurger";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import "./style.scss";
import useBoolean from "../../../../hooks/useBoolean";
import useBoolean from "../../../hooks/useBoolean";
interface SidebarHeaderProps {
background: string
@ -18,6 +18,7 @@ const SidebarHeader: FC<SidebarHeaderProps> = ({
background,
color,
}) => {
const { REACT_APP_LOGS } = process.env;
const { pathname } = useLocation();
const { isMobile } = useDeviceDetect();
@ -60,7 +61,7 @@ const SidebarHeader: FC<SidebarHeaderProps> = ({
/>
</div>
<div className="vm-header-sidebar-menu-settings">
{!isMobile && <ShortcutKeys showTitle={true}/>}
{!isMobile && !REACT_APP_LOGS && <ShortcutKeys showTitle={true}/>}
</div>
</div>
</div>;

View file

@ -21,6 +21,11 @@
padding: $padding-small;
}
&_sidebar {
display: grid;
grid-template-columns: 40px auto 1fr;
}
&_mobile {
display: grid;
grid-template-columns: 33px 1fr 33px;
@ -47,13 +52,6 @@
margin-bottom: 2px;
overflow: hidden;
&:not(&_logs) {
@media (max-width: 1200px) {
min-width: 14px;
max-width: 14px;
}
}
svg {
max-width: 65px;
min-width: 65px;

View file

@ -0,0 +1,21 @@
import React, { FC } from "preact/compat";
import classNames from "classnames";
import GlobalSettings from "../../components/Configurators/GlobalSettings/GlobalSettings";
import { ControlsProps } from "../Header/HeaderControls/HeaderControls";
const ControlsLogsLayout: FC<ControlsProps> = ({ isMobile }) => {
return (
<div
className={classNames({
"vm-header-controls": true,
"vm-header-controls_mobile": isMobile,
})}
>
<GlobalSettings/>
</div>
);
};
export default ControlsLogsLayout;

View file

@ -0,0 +1,40 @@
import Header from "../Header/Header";
import React, { FC, useEffect } from "preact/compat";
import { Outlet, useLocation } from "react-router-dom";
import "./style.scss";
import { getAppModeEnable } from "../../utils/app-mode";
import classNames from "classnames";
import Footer from "../Footer/Footer";
import router, { routerOptions } from "../../router";
import useDeviceDetect from "../../hooks/useDeviceDetect";
import ControlsLogsLayout from "./ControlsLogsLayout";
const LogsLayout: FC = () => {
const appModeEnable = getAppModeEnable();
const { isMobile } = useDeviceDetect();
const { pathname } = useLocation();
const setDocumentTitle = () => {
const defaultTitle = "vmui for VictoriaLogs";
const routeTitle = routerOptions[router.logs]?.title;
document.title = routeTitle ? `${routeTitle} - ${defaultTitle}` : defaultTitle;
};
useEffect(setDocumentTitle, [pathname]);
return <section className="vm-container">
<Header controlsComponent={ControlsLogsLayout}/>
<div
className={classNames({
"vm-container-body": true,
"vm-container-body_mobile": isMobile,
"vm-container-body_app": appModeEnable
})}
>
<Outlet/>
</div>
{!appModeEnable && <Footer/>}
</section>;
};
export default LogsLayout;

View file

@ -0,0 +1,38 @@
import React, { FC } from "preact/compat";
import classNames from "classnames";
import TenantsConfiguration
from "../../components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration";
import StepConfigurator from "../../components/Configurators/StepConfigurator/StepConfigurator";
import { TimeSelector } from "../../components/Configurators/TimeRangeSettings/TimeSelector/TimeSelector";
import CardinalityDatePicker from "../../components/Configurators/CardinalityDatePicker/CardinalityDatePicker";
import { ExecutionControls } from "../../components/Configurators/TimeRangeSettings/ExecutionControls/ExecutionControls";
import GlobalSettings from "../../components/Configurators/GlobalSettings/GlobalSettings";
import ShortcutKeys from "../../components/Main/ShortcutKeys/ShortcutKeys";
import { ControlsProps } from "../Header/HeaderControls/HeaderControls";
const ControlsMainLayout: FC<ControlsProps> = ({
displaySidebar,
isMobile,
headerSetup,
accountIds
}) => {
return (
<div
className={classNames({
"vm-header-controls": true,
"vm-header-controls_mobile": isMobile,
})}
>
{headerSetup?.tenant && <TenantsConfiguration accountIds={accountIds || []}/>}
{headerSetup?.stepControl && <StepConfigurator/>}
{headerSetup?.timeSelector && <TimeSelector/>}
{headerSetup?.cardinalityDatePicker && <CardinalityDatePicker/>}
{headerSetup?.executionControls && <ExecutionControls/>}
<GlobalSettings/>
{!displaySidebar && <ShortcutKeys/>}
</div>
);
};
export default ControlsMainLayout;

View file

@ -1,16 +1,17 @@
import Header from "./Header/Header";
import Header from "../Header/Header";
import React, { FC, useEffect } from "preact/compat";
import { Outlet, useLocation, useSearchParams } from "react-router-dom";
import qs from "qs";
import "./style.scss";
import { getAppModeEnable } from "../../utils/app-mode";
import classNames from "classnames";
import Footer from "./Footer/Footer";
import Footer from "../Footer/Footer";
import router, { routerOptions } from "../../router";
import { useFetchDashboards } from "../../pages/PredefinedPanels/hooks/useFetchDashboards";
import useDeviceDetect from "../../hooks/useDeviceDetect";
import ControlsMainLayout from "./ControlsMainLayout";
const Layout: FC = () => {
const MainLayout: FC = () => {
const { REACT_APP_LOGS } = process.env;
const appModeEnable = getAppModeEnable();
const { isMobile } = useDeviceDetect();
@ -42,7 +43,7 @@ const Layout: FC = () => {
useEffect(redirectSearchToHashParams, []);
return <section className="vm-container">
<Header/>
<Header controlsComponent={ControlsMainLayout}/>
<div
className={classNames({
"vm-container-body": true,
@ -56,4 +57,4 @@ const Layout: FC = () => {
</section>;
};
export default Layout;
export default MainLayout;

View file

@ -0,0 +1,27 @@
@use "src/styles/variables" as *;
.vm-container {
display: flex;
flex-direction: column;
min-height: calc(($vh * 100) - var(--scrollbar-height));
&-body {
flex-grow: 1;
min-height: 100%;
padding: $padding-medium;
background-color: $color-background-body;
&_mobile {
padding: $padding-small 0 0;
}
@media (max-width: 768px) {
padding: $padding-small 0 0;
}
&_app {
padding: $padding-small 0;
background-color: transparent;
}
}
}

View file

@ -2,7 +2,7 @@ import React, { FC, useState } from "preact/compat";
import { MouseEvent } from "react";
import { Data, Order, TableProps, } from "./types";
import { EnhancedTableHead } from "./TableHead";
import { getComparator, stableSort } from "./helpers";
import { getComparator, stableSort } from "../../../components/Table/helpers";
const EnhancedTable: FC<TableProps> = ({
rows,

View file

@ -11,7 +11,7 @@ import Spinner from "../../components/Main/Spinner/Spinner";
import { useFetchQueryOptions } from "../../hooks/useFetchQueryOptions";
import TracingsView from "../../components/TraceQuery/TracingsView";
import Trace from "../../components/TraceQuery/Trace";
import TableSettings from "../CardinalityPanel/Table/TableSettings/TableSettings";
import TableSettings from "../../components/Table/TableSettings/TableSettings";
import { useCustomPanelState, useCustomPanelDispatch } from "../../state/customPanel/CustomPanelStateContext";
import { useQueryState } from "../../state/query/QueryStateContext";
import { useTimeDispatch, useTimeState } from "../../state/time/TimeStateContext";

View file

@ -12,7 +12,7 @@ import SelectLimit from "../../../components/Main/Pagination/SelectLimit/SelectL
import useStateSearchParams from "../../../hooks/useStateSearchParams";
import useSearchParamsFromObject from "../../../hooks/useSearchParamsFromObject";
import { getFromStorage, saveToStorage } from "../../../utils/storage";
import TableSettings from "../../CardinalityPanel/Table/TableSettings/TableSettings";
import TableSettings from "../../../components/Table/TableSettings/TableSettings";
import useBoolean from "../../../hooks/useBoolean";
import TableLogs from "./TableLogs";
import GroupLogs from "./GroupLogs";

View file

@ -1,6 +1,6 @@
import React, { FC, useState, useMemo } from "react";
import { TopQuery } from "../../../types";
import { getComparator, stableSort } from "../../CardinalityPanel/Table/helpers";
import { getComparator, stableSort } from "../../../components/Table/helpers";
import { TopQueryPanelProps } from "../TopQueryPanel/TopQueryPanel";
import classNames from "classnames";
import { ArrowDropDownIcon, CopyIcon, PlayCircleOutlineIcon } from "../../../components/Main/Icons";

View file

@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom";