mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-04 00:10:57 +00:00 
			
		
		
		
	refactor widget
This commit is contained in:
		
							parent
							
								
									3fa72e48cc
								
							
						
					
					
						commit
						b77909a360
					
				@ -1,46 +0,0 @@
 | 
			
		||||
import { PureComponent } from "react";
 | 
			
		||||
import { AreaChart, Area, ResponsiveContainer, Tooltip } from "recharts";
 | 
			
		||||
 | 
			
		||||
import CustomTooltip from "./custom_tooltip";
 | 
			
		||||
 | 
			
		||||
class Chart extends PureComponent {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { dataPoints, formatter, label } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="overflow-clip z-10 w-full h-full">
 | 
			
		||||
        <ResponsiveContainer width="100%" height="100%">
 | 
			
		||||
          <AreaChart data={dataPoints}>
 | 
			
		||||
            <defs>
 | 
			
		||||
              <linearGradient id="color" x1="0" y1="0" x2="0" y2="1">
 | 
			
		||||
                <stop offset="5%" stopColor="rgb(var(--color-500))" stopOpacity={0.4}/>
 | 
			
		||||
                <stop offset="95%" stopColor="rgb(var(--color-500))" stopOpacity={0.1}/>
 | 
			
		||||
              </linearGradient>
 | 
			
		||||
            </defs>
 | 
			
		||||
            <Area
 | 
			
		||||
              name={label[0]}
 | 
			
		||||
              isAnimationActive={false}
 | 
			
		||||
              type="monotoneX"
 | 
			
		||||
              dataKey="value"
 | 
			
		||||
              stroke="rgb(var(--color-500))"
 | 
			
		||||
              fillOpacity={1} fill="url(#color)"
 | 
			
		||||
              baseLine={0}
 | 
			
		||||
            />
 | 
			
		||||
            <Tooltip
 | 
			
		||||
              allowEscapeViewBox={{ x: false, y: false }}
 | 
			
		||||
              formatter={formatter}
 | 
			
		||||
              content={<CustomTooltip formatter={formatter} />}
 | 
			
		||||
              classNames="rounded-md text-xs p-0.5"
 | 
			
		||||
              contentStyle={{
 | 
			
		||||
                backgroundColor: "rgb(var(--color-800))",
 | 
			
		||||
                color: "rgb(var(--color-100))"
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </AreaChart>
 | 
			
		||||
        </ResponsiveContainer>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Chart;
 | 
			
		||||
@ -1,61 +0,0 @@
 | 
			
		||||
import { PureComponent } from "react";
 | 
			
		||||
import { AreaChart, Area, ResponsiveContainer, Tooltip } from "recharts";
 | 
			
		||||
 | 
			
		||||
import CustomTooltip from "./custom_tooltip";
 | 
			
		||||
 | 
			
		||||
class ChartDual extends PureComponent {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { dataPoints, formatter, stack, label } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="overflow-clip z-10 w-full h-full">
 | 
			
		||||
        <ResponsiveContainer width="100%" height="100%">
 | 
			
		||||
          <AreaChart data={dataPoints}  stackOffset={stack ?? "none"}>
 | 
			
		||||
            <defs>
 | 
			
		||||
              <linearGradient id="colorA" x1="0" y1="0" x2="0" y2="1">
 | 
			
		||||
                <stop offset="5%" stopColor="rgb(var(--color-800))" stopOpacity={0.8}/>
 | 
			
		||||
                <stop offset="95%" stopColor="rgb(var(--color-800))" stopOpacity={0.5}/>
 | 
			
		||||
              </linearGradient>
 | 
			
		||||
              <linearGradient id="colorB" x1="0" y1="0" x2="0" y2="1">
 | 
			
		||||
                <stop offset="5%" stopColor="rgb(var(--color-500))" stopOpacity={0.4}/>
 | 
			
		||||
                <stop offset="95%" stopColor="rgb(var(--color-500))" stopOpacity={0.1}/>
 | 
			
		||||
              </linearGradient>
 | 
			
		||||
            </defs>
 | 
			
		||||
 | 
			
		||||
            <Area
 | 
			
		||||
              name={label[0]}
 | 
			
		||||
              stackId="1"
 | 
			
		||||
              isAnimationActive={false}
 | 
			
		||||
              type="monotoneX"
 | 
			
		||||
              dataKey="a"
 | 
			
		||||
              stroke="rgb(var(--color-700))"
 | 
			
		||||
              fillOpacity={1} fill="url(#colorA)"
 | 
			
		||||
            />
 | 
			
		||||
            <Area
 | 
			
		||||
              name={label[1]}
 | 
			
		||||
              stackId="1"
 | 
			
		||||
              isAnimationActive={false}
 | 
			
		||||
              type="monotoneX"
 | 
			
		||||
              dataKey="b"
 | 
			
		||||
              stroke="rgb(var(--color-500))"
 | 
			
		||||
              fillOpacity={1} fill="url(#colorB)"
 | 
			
		||||
            />
 | 
			
		||||
            <Tooltip
 | 
			
		||||
              allowEscapeViewBox={{ x: false, y: false }}
 | 
			
		||||
              formatter={formatter}
 | 
			
		||||
              content={<CustomTooltip formatter={formatter} />}
 | 
			
		||||
              classNames="rounded-md text-xs p-0.5"
 | 
			
		||||
              contentStyle={{
 | 
			
		||||
                backgroundColor: "rgb(var(--color-800))",
 | 
			
		||||
                color: "rgb(var(--color-100))"
 | 
			
		||||
              }}
 | 
			
		||||
 | 
			
		||||
            />
 | 
			
		||||
          </AreaChart>
 | 
			
		||||
        </ResponsiveContainer>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ChartDual;
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
import Memory from "./memory";
 | 
			
		||||
import Cpu from "./cpu";
 | 
			
		||||
import Sensor from "./sensor";
 | 
			
		||||
import Net from "./net";
 | 
			
		||||
import Process from "./process";
 | 
			
		||||
import Disk from "./disk";
 | 
			
		||||
import Memory from "./metrics/memory";
 | 
			
		||||
import Cpu from "./metrics/cpu";
 | 
			
		||||
import Sensor from "./metrics/sensor";
 | 
			
		||||
import Net from "./metrics/net";
 | 
			
		||||
import Process from "./metrics/process";
 | 
			
		||||
import Disk from "./metrics/disk";
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/widgets/glances/components/block.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/widgets/glances/components/block.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
export default function Block({ position, children }) {
 | 
			
		||||
  const positionClasses = Object.entries(position).map(([key, value]) => `${key}-${value}`).join(' ');
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={`absolute ${positionClasses} z-20 text-sm`}>
 | 
			
		||||
      {children}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								src/widgets/glances/components/chart.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/widgets/glances/components/chart.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
import { PureComponent } from "react";
 | 
			
		||||
import { AreaChart, Area, ResponsiveContainer, Tooltip } from "recharts";
 | 
			
		||||
 | 
			
		||||
import CustomTooltip from "./custom_tooltip";
 | 
			
		||||
 | 
			
		||||
class Chart extends PureComponent {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { dataPoints, formatter, label } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
        <div className="overflow-clip z-10 w-full h-full">
 | 
			
		||||
          <ResponsiveContainer width="100%" height="100%">
 | 
			
		||||
            <AreaChart data={dataPoints}>
 | 
			
		||||
              <defs>
 | 
			
		||||
                <linearGradient id="color" x1="0" y1="0" x2="0" y2="1">
 | 
			
		||||
                  <stop offset="5%" stopColor="rgb(var(--color-500))" stopOpacity={0.4}/>
 | 
			
		||||
                  <stop offset="95%" stopColor="rgb(var(--color-500))" stopOpacity={0.1}/>
 | 
			
		||||
                </linearGradient>
 | 
			
		||||
              </defs>
 | 
			
		||||
              <Area
 | 
			
		||||
                name={label[0]}
 | 
			
		||||
                isAnimationActive={false}
 | 
			
		||||
                type="monotoneX"
 | 
			
		||||
                dataKey="value"
 | 
			
		||||
                stroke="rgb(var(--color-500))"
 | 
			
		||||
                fillOpacity={1} fill="url(#color)"
 | 
			
		||||
                baseLine={0}
 | 
			
		||||
              />
 | 
			
		||||
              <Tooltip
 | 
			
		||||
                allowEscapeViewBox={{ x: false, y: false }}
 | 
			
		||||
                formatter={formatter}
 | 
			
		||||
                content={<CustomTooltip formatter={formatter} />}
 | 
			
		||||
                classNames="rounded-md text-xs p-0.5"
 | 
			
		||||
                contentStyle={{
 | 
			
		||||
                  backgroundColor: "rgb(var(--color-800))",
 | 
			
		||||
                  color: "rgb(var(--color-100))"
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            </AreaChart>
 | 
			
		||||
          </ResponsiveContainer>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Chart;
 | 
			
		||||
							
								
								
									
										63
									
								
								src/widgets/glances/components/chart_dual.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/widgets/glances/components/chart_dual.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
import { PureComponent } from "react";
 | 
			
		||||
import { AreaChart, Area, ResponsiveContainer, Tooltip } from "recharts";
 | 
			
		||||
 | 
			
		||||
import CustomTooltip from "./custom_tooltip";
 | 
			
		||||
 | 
			
		||||
class ChartDual extends PureComponent {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { dataPoints, formatter, stack, label } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
        <div className="overflow-clip z-10 w-full h-full">
 | 
			
		||||
          <ResponsiveContainer width="100%" height="100%">
 | 
			
		||||
            <AreaChart data={dataPoints}  stackOffset={stack ?? "none"}>
 | 
			
		||||
              <defs>
 | 
			
		||||
                <linearGradient id="colorA" x1="0" y1="0" x2="0" y2="1">
 | 
			
		||||
                  <stop offset="5%" stopColor="rgb(var(--color-800))" stopOpacity={0.8}/>
 | 
			
		||||
                  <stop offset="95%" stopColor="rgb(var(--color-800))" stopOpacity={0.5}/>
 | 
			
		||||
                </linearGradient>
 | 
			
		||||
                <linearGradient id="colorB" x1="0" y1="0" x2="0" y2="1">
 | 
			
		||||
                  <stop offset="5%" stopColor="rgb(var(--color-500))" stopOpacity={0.4}/>
 | 
			
		||||
                  <stop offset="95%" stopColor="rgb(var(--color-500))" stopOpacity={0.1}/>
 | 
			
		||||
                </linearGradient>
 | 
			
		||||
              </defs>
 | 
			
		||||
 | 
			
		||||
              <Area
 | 
			
		||||
                name={label[0]}
 | 
			
		||||
                stackId="1"
 | 
			
		||||
                isAnimationActive={false}
 | 
			
		||||
                type="monotoneX"
 | 
			
		||||
                dataKey="a"
 | 
			
		||||
                stroke="rgb(var(--color-700))"
 | 
			
		||||
                fillOpacity={1} fill="url(#colorA)"
 | 
			
		||||
              />
 | 
			
		||||
              <Area
 | 
			
		||||
                name={label[1]}
 | 
			
		||||
                stackId="1"
 | 
			
		||||
                isAnimationActive={false}
 | 
			
		||||
                type="monotoneX"
 | 
			
		||||
                dataKey="b"
 | 
			
		||||
                stroke="rgb(var(--color-500))"
 | 
			
		||||
                fillOpacity={1} fill="url(#colorB)"
 | 
			
		||||
              />
 | 
			
		||||
              <Tooltip
 | 
			
		||||
                allowEscapeViewBox={{ x: false, y: false }}
 | 
			
		||||
                formatter={formatter}
 | 
			
		||||
                content={<CustomTooltip formatter={formatter} />}
 | 
			
		||||
                classNames="rounded-md text-xs p-0.5"
 | 
			
		||||
                contentStyle={{
 | 
			
		||||
                  backgroundColor: "rgb(var(--color-800))",
 | 
			
		||||
                  color: "rgb(var(--color-100))"
 | 
			
		||||
                }}
 | 
			
		||||
 | 
			
		||||
              />
 | 
			
		||||
            </AreaChart>
 | 
			
		||||
          </ResponsiveContainer>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ChartDual;
 | 
			
		||||
							
								
								
									
										8
									
								
								src/widgets/glances/components/container.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/widgets/glances/components/container.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
export default function Container({ children }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      {children}
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/widgets/glances/components/error.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/widgets/glances/components/error.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
export default function Error() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-75">
 | 
			
		||||
    {t("widget.api_error")}
 | 
			
		||||
  </div>;
 | 
			
		||||
}
 | 
			
		||||
@ -1,100 +0,0 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const Chart = dynamic(() => import("./chart"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'cpu', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const { data: systemData, error: systemError } = useWidgetAPI(service.widget, 'system');
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
      setDataPoints((prevDataPoints) => {
 | 
			
		||||
        const newDataPoints = [...prevDataPoints, { value: data.total }];
 | 
			
		||||
          if (newDataPoints.length > pointsLimit) {
 | 
			
		||||
              newDataPoints.shift();
 | 
			
		||||
          }
 | 
			
		||||
          return newDataPoints;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-75">
 | 
			
		||||
      {t("widget.api_error")}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-xs opacity-75">
 | 
			
		||||
        -
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
      <Chart
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          label={[t("resources.used")]}
 | 
			
		||||
          formatter={(value) => t("common.number", {
 | 
			
		||||
            value,
 | 
			
		||||
            style: "unit",
 | 
			
		||||
            unit: "percent",
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
            })}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 left-3 opacity-50 z-10 pointer-events-none">
 | 
			
		||||
        {systemData && !systemError && (
 | 
			
		||||
          <>
 | 
			
		||||
            {systemData.linux_distro && (
 | 
			
		||||
              <div className="text-xs opacity-80">
 | 
			
		||||
                {systemData.linux_distro}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
            {systemData.os_version && (
 | 
			
		||||
              <div className="text-xs opacity-80">
 | 
			
		||||
                {systemData.os_version}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
            {systemData.hostname && (
 | 
			
		||||
              <div className="text-xs font-bold">
 | 
			
		||||
                {systemData.hostname}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 right-3 z-10 text-xs opacity-80 pointer-events-none">
 | 
			
		||||
        {t("common.number", {
 | 
			
		||||
          value: data.total,
 | 
			
		||||
          style: "unit",
 | 
			
		||||
          unit: "percent",
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })} {t("resources.used")}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -1,96 +0,0 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const ChartDual = dynamic(() => import("./chart_dual"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'mem', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
      setDataPoints((prevDataPoints) => {
 | 
			
		||||
        const newDataPoints = [...prevDataPoints, { a: data.used, b: data.free }];
 | 
			
		||||
          if (newDataPoints.length > pointsLimit) {
 | 
			
		||||
              newDataPoints.shift();
 | 
			
		||||
          }
 | 
			
		||||
          return newDataPoints;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-80">
 | 
			
		||||
      {t("widget.api_error")}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
  }
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-xs opacity-80">
 | 
			
		||||
        -
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
        <ChartDual
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          max={data.total}
 | 
			
		||||
          label={[t("resources.used"), t("resources.free")]}
 | 
			
		||||
          formatter={(value) => t("common.bytes", {
 | 
			
		||||
            value,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
          })}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 left-3 z-10 opacity-50 pointer-events-none">
 | 
			
		||||
        {data && !error && (
 | 
			
		||||
          <>
 | 
			
		||||
            {data.free && (
 | 
			
		||||
              <div className="text-xs opacity-80">
 | 
			
		||||
                {t("common.bytes", {
 | 
			
		||||
                  value: data.free,
 | 
			
		||||
                  maximumFractionDigits: 0,
 | 
			
		||||
                })} {t("resources.free")}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {data.total && (
 | 
			
		||||
              <div className="text-xs font-bold">
 | 
			
		||||
                {t("common.bytes", {
 | 
			
		||||
                  value: data.total,
 | 
			
		||||
                  maximumFractionDigits: 0,
 | 
			
		||||
                })} {t("resources.total")}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 right-3 z-10 text-xs opacity-80 pointer-events-none">
 | 
			
		||||
        {t("common.bytes", {
 | 
			
		||||
          value: data.used,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })} {t("resources.used")}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										91
									
								
								src/widgets/glances/metrics/cpu.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/widgets/glances/metrics/cpu.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import Error from "../components/error";
 | 
			
		||||
import Container from "../components/container";
 | 
			
		||||
import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const Chart = dynamic(() => import("../components/chart"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'cpu', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const { data: systemData, error: systemError } = useWidgetAPI(service.widget, 'system');
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
      setDataPoints((prevDataPoints) => {
 | 
			
		||||
        const newDataPoints = [...prevDataPoints, { value: data.total }];
 | 
			
		||||
          if (newDataPoints.length > pointsLimit) {
 | 
			
		||||
              newDataPoints.shift();
 | 
			
		||||
          }
 | 
			
		||||
          return newDataPoints;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <Container><Error error={error} /></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <Chart
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[t("resources.used")]}
 | 
			
		||||
        formatter={(value) => t("common.number", {
 | 
			
		||||
          value,
 | 
			
		||||
          style: "unit",
 | 
			
		||||
          unit: "percent",
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
          })}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      {systemData && !systemError && (
 | 
			
		||||
        <Block position={{bottom: 3, left: 3}}>
 | 
			
		||||
          {systemData.linux_distro && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.linux_distro}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {systemData.os_version && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.os_version}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {systemData.hostname && (
 | 
			
		||||
            <div className="text-xs opacity-75">
 | 
			
		||||
              {systemData.hostname}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 3, right: 3}}>
 | 
			
		||||
        <div className="text-xs font-bold opacity-75">
 | 
			
		||||
            {t("common.number", {
 | 
			
		||||
              value: data.total,
 | 
			
		||||
              style: "unit",
 | 
			
		||||
              unit: "percent",
 | 
			
		||||
              maximumFractionDigits: 0,
 | 
			
		||||
            })} {t("resources.used")}
 | 
			
		||||
          </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -2,9 +2,13 @@ import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import Error from "../components/error";
 | 
			
		||||
import Container from "../components/container";
 | 
			
		||||
import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const ChartDual = dynamic(() => import("./chart_dual"), { ssr: false });
 | 
			
		||||
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
@ -44,70 +48,55 @@ export default function Component({ service }) {
 | 
			
		||||
  }, [dataPoints]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-80">
 | 
			
		||||
      {t("widget.api_error")}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
    return <Container><Error error={error} /></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-xs opacity-80">
 | 
			
		||||
        -
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const diskData = data.find((item) => item.disk_name === diskName);
 | 
			
		||||
 | 
			
		||||
  if (!diskData) {
 | 
			
		||||
    return <div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </div>;
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const diskRates = calculateRates(dataPoints);
 | 
			
		||||
  const currentRate = diskRates[diskRates.length - 1];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
          dataPoints={ratePoints}
 | 
			
		||||
          label={[t("glances.read"), t("glances.write")]}
 | 
			
		||||
          max={diskData.critical}
 | 
			
		||||
          formatter={(value) => t("common.bitrate", {
 | 
			
		||||
            value,
 | 
			
		||||
            })}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 left-3 opacity-50 z-10 pointer-events-none">
 | 
			
		||||
        {currentRate && !error && (
 | 
			
		||||
          <>
 | 
			
		||||
            <div className="text-xs opacity-80">
 | 
			
		||||
              {t("common.bitrate", {
 | 
			
		||||
                value: currentRate.a,
 | 
			
		||||
              })} {t("glances.read")}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="text-xs opacity-80">
 | 
			
		||||
              {t("common.bitrate", {
 | 
			
		||||
                value: currentRate.b,
 | 
			
		||||
              })} {t("glances.write")}
 | 
			
		||||
            </div>
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 right-3 z-10 text-xs opacity-80 pointer-events-none">
 | 
			
		||||
        {t("common.bitrate", {
 | 
			
		||||
          value: currentRate.a + currentRate.b,
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </>
 | 
			
		||||
        dataPoints={ratePoints}
 | 
			
		||||
        label={[t("glances.read"), t("glances.write")]}
 | 
			
		||||
        max={diskData.critical}
 | 
			
		||||
        formatter={(value) => t("common.bitrate", {
 | 
			
		||||
          value,
 | 
			
		||||
          })}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      {currentRate && !error && (
 | 
			
		||||
        <Block position={{bottom: 3, left: 3}}>
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {t("common.bitrate", {
 | 
			
		||||
              value: currentRate.a,
 | 
			
		||||
            })} {t("glances.read")}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {t("common.bitrate", {
 | 
			
		||||
              value: currentRate.b,
 | 
			
		||||
            })} {t("glances.write")}
 | 
			
		||||
          </div>
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 3, right: 3}}>
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bitrate", {
 | 
			
		||||
            value: currentRate.a + currentRate.b,
 | 
			
		||||
          })}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								src/widgets/glances/metrics/memory.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/widgets/glances/metrics/memory.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import Error from "../components/error";
 | 
			
		||||
import Container from "../components/container";
 | 
			
		||||
import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'mem', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
      setDataPoints((prevDataPoints) => {
 | 
			
		||||
        const newDataPoints = [...prevDataPoints, { a: data.used, b: data.free }];
 | 
			
		||||
          if (newDataPoints.length > pointsLimit) {
 | 
			
		||||
              newDataPoints.shift();
 | 
			
		||||
          }
 | 
			
		||||
          return newDataPoints;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <Container><Error error={error} /></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        max={data.total}
 | 
			
		||||
        label={[t("resources.used"), t("resources.free")]}
 | 
			
		||||
        formatter={(value) => t("common.bytes", {
 | 
			
		||||
          value,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      {data && !error && (
 | 
			
		||||
        <Block position={{bottom: 3, left: 3}}>
 | 
			
		||||
          {data.free && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {t("common.bytes", {
 | 
			
		||||
                value: data.free,
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })} {t("resources.free")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
 | 
			
		||||
          {data.total && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {t("common.bytes", {
 | 
			
		||||
                value: data.total,
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })} {t("resources.total")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 3, right: 3}}>
 | 
			
		||||
        <div className="text-xs font-bold opacity-75">
 | 
			
		||||
          {t("common.bytes", {
 | 
			
		||||
            value: data.used,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
          })} {t("resources.used")}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -2,9 +2,13 @@ import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import Error from "../components/error";
 | 
			
		||||
import Container from "../components/container";
 | 
			
		||||
import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const ChartDual = dynamic(() => import("./chart_dual"), { ssr: false });
 | 
			
		||||
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
@ -36,61 +40,53 @@ export default function Component({ service }) {
 | 
			
		||||
  }, [data, interfaceName]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-80">
 | 
			
		||||
      {t("widget.api_error")}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
    return <Container><Error error={error} /></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-xs opacity-80">
 | 
			
		||||
        -
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const interfaceData = data.find((item) => item[item.key] === interfaceName);
 | 
			
		||||
 | 
			
		||||
  if (!interfaceData) {
 | 
			
		||||
    return <div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </div>;
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
        <ChartDual
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          label={[t("docker.tx"), t("docker.rx")]}
 | 
			
		||||
          formatter={(value) => t("common.byterate", {
 | 
			
		||||
            value,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
          })}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 left-3 z-10 text-xs opacity-80 pointer-events-none">
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[t("docker.tx"), t("docker.rx")]}
 | 
			
		||||
        formatter={(value) => t("common.byterate", {
 | 
			
		||||
          value,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 3, left: 3}}>
 | 
			
		||||
        {interfaceData && interfaceData.interface_name && (
 | 
			
		||||
            <div className="text-xs opacity-80">
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {interfaceData.interface_name}
 | 
			
		||||
            </div>
 | 
			
		||||
        )}
 | 
			
		||||
        {t("common.bitrate", {
 | 
			
		||||
          value: interfaceData.tx,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })} {t("docker.tx")}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 right-3 z-10 text-xs opacity-80 pointer-events-none">
 | 
			
		||||
        {t("common.bitrate", {
 | 
			
		||||
          value: interfaceData.rx,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })} {t("docker.rx")}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </>
 | 
			
		||||
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bitrate", {
 | 
			
		||||
            value: interfaceData.tx,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
          })} {t("docker.tx")}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 3, right: 3}}>
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bitrate", {
 | 
			
		||||
            value: interfaceData.rx,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
          })} {t("docker.rx")}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import Error from "../components/error";
 | 
			
		||||
import Container from "../components/container";
 | 
			
		||||
import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
import ResolvedIcon from "components/resolvedicon";
 | 
			
		||||
 | 
			
		||||
@ -21,52 +25,42 @@ export default function Component({ service }) {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-80">
 | 
			
		||||
      {t("widget.api_error")}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
    return <Container><Error error={error} /></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-xs opacity-80">
 | 
			
		||||
        -
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data.splice(5);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className="absolute top-4 right-3 left-3 opacity-30 z-10 pointer-events-none">
 | 
			
		||||
    <Container>
 | 
			
		||||
      <Block position={{top: 4, right: 3, left: 3}}>
 | 
			
		||||
        <div className="flex items-center text-xs">
 | 
			
		||||
          <div className="grow" />
 | 
			
		||||
          <div className="w-14 text-right italic">{t("resources.cpu")}</div>
 | 
			
		||||
          <div className="w-14 text-right">{t("resources.mem")}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-4 right-3 left-3 z-10 pointer-events-none text-theme-900 dark:text-theme-200">
 | 
			
		||||
        { data.map((item) => <div key={item.pid} className="text-[0.75rem] h-[0.8rem]">
 | 
			
		||||
      </Block>
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 4, right: 3, left: 3}}>
 | 
			
		||||
        <div className="pointer-events-none text-theme-900 dark:text-theme-200">
 | 
			
		||||
          { data.map((item) => <div key={item.pid} className="text-[0.75rem] h-[0.8rem]">
 | 
			
		||||
            <div className="flex items-center">
 | 
			
		||||
              <div className="w-3 h-3 mr-1.5 opacity-60">
 | 
			
		||||
              <div className="w-3 h-3 mr-1.5 opacity-50">
 | 
			
		||||
                {statusMap[item.status]}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div className="opacity-60 grow">{item.name}</div>
 | 
			
		||||
              <div className="opacity-30 w-14 text-right">{item.cpu_percent.toFixed(1)}%</div>
 | 
			
		||||
              <div className="opacity-30 w-14 text-right">{t("common.bytes", {
 | 
			
		||||
              <div className="opacity-75 grow">{item.name}</div>
 | 
			
		||||
              <div className="opacity-25 w-14 text-right">{item.cpu_percent.toFixed(1)}%</div>
 | 
			
		||||
              <div className="opacity-25 w-14 text-right">{t("common.bytes", {
 | 
			
		||||
                value: item.memory_info[0],
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })}</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>) }
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </>
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								src/widgets/glances/metrics/sensor.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/widgets/glances/metrics/sensor.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import Error from "../components/error";
 | 
			
		||||
import Container from "../components/container";
 | 
			
		||||
import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const Chart = dynamic(() => import("../components/chart"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const [, sensorName] = widget.metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'sensors', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
      const sensorData = data.find((item) => item.label === sensorName);
 | 
			
		||||
      setDataPoints((prevDataPoints) => {
 | 
			
		||||
        const newDataPoints = [...prevDataPoints, { value: sensorData.value }];
 | 
			
		||||
          if (newDataPoints.length > pointsLimit) {
 | 
			
		||||
              newDataPoints.shift();
 | 
			
		||||
          }
 | 
			
		||||
          return newDataPoints;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data, sensorName]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <Container><Error error={error} /></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const sensorData = data.find((item) => item.label === sensorName);
 | 
			
		||||
 | 
			
		||||
  if (!sensorData) {
 | 
			
		||||
    return <Container><Block position={{bottom: 2, left: 2}}>-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <Chart
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[sensorData.unit]}
 | 
			
		||||
        max={sensorData.critical}
 | 
			
		||||
        formatter={(value) => t("common.number", {
 | 
			
		||||
          value,
 | 
			
		||||
          })}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      {sensorData && !error && (
 | 
			
		||||
        <Block position={{bottom: 3, left: 3}}>
 | 
			
		||||
          {sensorData.warning && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {sensorData.warning}{sensorData.unit} {t("glances.warn")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {sensorData.critical && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {sensorData.critical} {sensorData.unit} {t("glances.crit")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position={{bottom: 3, right: 3}}>
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.number", {
 | 
			
		||||
            value: sensorData.value,
 | 
			
		||||
          })} {sensorData.unit}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -1,99 +0,0 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useState, useEffect } from "react";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
const Chart = dynamic(() => import("./chart"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const [, sensorName] = widget.metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'sensors', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (data) {
 | 
			
		||||
      const sensorData = data.find((item) => item.label === sensorName);
 | 
			
		||||
      setDataPoints((prevDataPoints) => {
 | 
			
		||||
        const newDataPoints = [...prevDataPoints, { value: sensorData.value }];
 | 
			
		||||
          if (newDataPoints.length > pointsLimit) {
 | 
			
		||||
              newDataPoints.shift();
 | 
			
		||||
          }
 | 
			
		||||
          return newDataPoints;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, [data, sensorName]);
 | 
			
		||||
 | 
			
		||||
  if (error) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-red-400 text-xs opacity-80">
 | 
			
		||||
      {t("widget.api_error")}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return <div>
 | 
			
		||||
    <div className="h-[68px] overflow-clip">
 | 
			
		||||
      <div className="absolute bottom-2 left-2 z-20 text-xs opacity-80">
 | 
			
		||||
        -
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const sensorData = data.find((item) => item.label === sensorName);
 | 
			
		||||
 | 
			
		||||
  if (!sensorData) {
 | 
			
		||||
    return <div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div className="absolute -top-1 -left-1 h-[120px] w-[calc(100%+0.5em)] z-0">
 | 
			
		||||
      <Chart
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          label={[sensorData.unit]}
 | 
			
		||||
          max={sensorData.critical}
 | 
			
		||||
          formatter={(value) => t("common.number", {
 | 
			
		||||
            value,
 | 
			
		||||
            })}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 left-3 opacity-50 z-10 pointer-events-none">
 | 
			
		||||
        {sensorData && !error && (
 | 
			
		||||
          <>
 | 
			
		||||
            {sensorData.warning && (
 | 
			
		||||
              <div className="text-xs opacity-80">
 | 
			
		||||
                {sensorData.warning}{sensorData.unit} {t("glances.warn")}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
            {sensorData.critical && (
 | 
			
		||||
              <div className="text-xs opacity-80">
 | 
			
		||||
                {sensorData.critical} {sensorData.unit} {t("glances.crit")}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="absolute bottom-3 right-3 z-10 text-xs opacity-80 pointer-events-none">
 | 
			
		||||
        {t("common.number", {
 | 
			
		||||
          value: sensorData.value,
 | 
			
		||||
        })} {sensorData.unit}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user