mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
feat: optimize vmui-log bundle size (#4602)
Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
443c266406
commit
44b4963ac6
36 changed files with 1734 additions and 2631 deletions
|
@ -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";
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
3635
app/vmui/packages/vmui/package-lock.json
generated
3635
app/vmui/packages/vmui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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": {
|
||||
|
|
|
@ -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();
|
||||
});
|
|
@ -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>
|
||||
|
|
35
app/vmui/packages/vmui/src/AppLogs.tsx
Normal file
35
app/vmui/packages/vmui/src/AppLogs.tsx
Normal 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;
|
|
@ -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"
|
||||
|
|
|
@ -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
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
@ -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]) {
|
|
@ -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"
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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;
|
|
@ -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
|
|
@ -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,
|
|
@ -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>;
|
|
@ -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;
|
|
@ -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;
|
40
app/vmui/packages/vmui/src/layouts/LogsLayout/LogsLayout.tsx
Normal file
40
app/vmui/packages/vmui/src/layouts/LogsLayout/LogsLayout.tsx
Normal 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;
|
|
@ -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;
|
|
@ -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;
|
27
app/vmui/packages/vmui/src/layouts/MainLayout/style.scss
Normal file
27
app/vmui/packages/vmui/src/layouts/MainLayout/style.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
Loading…
Reference in a new issue