mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-04 08:20:58 +00:00 
			
		
		
		
	mini-non-chart charts
This commit is contained in:
		
							parent
							
								
									c2058f353d
								
							
						
					
					
						commit
						17b0f635d8
					
				@ -365,6 +365,7 @@
 | 
			
		||||
        "load": "Load",
 | 
			
		||||
        "wait": "Please wait",
 | 
			
		||||
        "temp": "TEMP",
 | 
			
		||||
        "_temp": "Temp",
 | 
			
		||||
        "warn": "Warn",
 | 
			
		||||
        "uptime": "UP",
 | 
			
		||||
        "total": "Total",
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ import getKubeConfig from "utils/config/kubernetes";
 | 
			
		||||
 | 
			
		||||
const logger = createLogger("service-helpers");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export async function servicesFromConfig() {
 | 
			
		||||
  checkAndCopyConfig("services.yaml");
 | 
			
		||||
 | 
			
		||||
@ -32,14 +31,14 @@ export async function servicesFromConfig() {
 | 
			
		||||
    services: servicesGroup[Object.keys(servicesGroup)[0]].map((entries) => ({
 | 
			
		||||
      name: Object.keys(entries)[0],
 | 
			
		||||
      ...entries[Object.keys(entries)[0]],
 | 
			
		||||
      type: 'service'
 | 
			
		||||
      type: "service",
 | 
			
		||||
    })),
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  // add default weight to services based on their position in the configuration
 | 
			
		||||
  servicesArray.forEach((group, groupIndex) => {
 | 
			
		||||
    group.services.forEach((service, serviceIndex) => {
 | 
			
		||||
      if(!service.weight) {
 | 
			
		||||
      if (!service.weight) {
 | 
			
		||||
        servicesArray[groupIndex].services[serviceIndex].weight = (serviceIndex + 1) * 100;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
@ -66,7 +65,9 @@ export async function servicesFromDocker() {
 | 
			
		||||
        const isSwarm = !!servers[serverName].swarm;
 | 
			
		||||
        const docker = new Docker(getDockerArguments(serverName).conn);
 | 
			
		||||
        const listProperties = { all: true };
 | 
			
		||||
        const containers = await ((isSwarm) ? docker.listServices(listProperties) : docker.listContainers(listProperties));
 | 
			
		||||
        const containers = await (isSwarm
 | 
			
		||||
          ? docker.listServices(listProperties)
 | 
			
		||||
          : docker.listContainers(listProperties));
 | 
			
		||||
 | 
			
		||||
        // bad docker connections can result in a <Buffer ...> object?
 | 
			
		||||
        // in any case, this ensures the result is the expected array
 | 
			
		||||
@ -76,8 +77,8 @@ export async function servicesFromDocker() {
 | 
			
		||||
 | 
			
		||||
        const discovered = containers.map((container) => {
 | 
			
		||||
          let constructedService = null;
 | 
			
		||||
          const containerLabels = isSwarm ? shvl.get(container, 'Spec.Labels') : container.Labels;
 | 
			
		||||
          const containerName = isSwarm ? shvl.get(container, 'Spec.Name') : container.Names[0];
 | 
			
		||||
          const containerLabels = isSwarm ? shvl.get(container, "Spec.Labels") : container.Labels;
 | 
			
		||||
          const containerName = isSwarm ? shvl.get(container, "Spec.Name") : container.Names[0];
 | 
			
		||||
 | 
			
		||||
          Object.keys(containerLabels).forEach((label) => {
 | 
			
		||||
            if (label.startsWith("homepage.")) {
 | 
			
		||||
@ -85,10 +86,14 @@ export async function servicesFromDocker() {
 | 
			
		||||
                constructedService = {
 | 
			
		||||
                  container: containerName.replace(/^\//, ""),
 | 
			
		||||
                  server: serverName,
 | 
			
		||||
                  type: 'service'
 | 
			
		||||
                  type: "service",
 | 
			
		||||
                };
 | 
			
		||||
              }
 | 
			
		||||
              shvl.set(constructedService, label.replace("homepage.", ""), substituteEnvironmentVars(containerLabels[label]));
 | 
			
		||||
              shvl.set(
 | 
			
		||||
                constructedService,
 | 
			
		||||
                label.replace("homepage.", ""),
 | 
			
		||||
                substituteEnvironmentVars(containerLabels[label])
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
@ -132,12 +137,12 @@ export async function servicesFromDocker() {
 | 
			
		||||
function getUrlFromIngress(ingress) {
 | 
			
		||||
  const urlHost = ingress.spec.rules[0].host;
 | 
			
		||||
  const urlPath = ingress.spec.rules[0].http.paths[0].path;
 | 
			
		||||
  const urlSchema = ingress.spec.tls ? 'https' : 'http';
 | 
			
		||||
  const urlSchema = ingress.spec.tls ? "https" : "http";
 | 
			
		||||
  return `${urlSchema}://${urlHost}${urlPath}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function servicesFromKubernetes() {
 | 
			
		||||
  const ANNOTATION_BASE = 'gethomepage.dev';
 | 
			
		||||
  const ANNOTATION_BASE = "gethomepage.dev";
 | 
			
		||||
  const ANNOTATION_WIDGET_BASE = `${ANNOTATION_BASE}/widget.`;
 | 
			
		||||
  const ANNOTATION_POD_SELECTOR = `${ANNOTATION_BASE}/pod-selector`;
 | 
			
		||||
 | 
			
		||||
@ -151,39 +156,52 @@ export async function servicesFromKubernetes() {
 | 
			
		||||
    const networking = kc.makeApiClient(NetworkingV1Api);
 | 
			
		||||
    const crd = kc.makeApiClient(CustomObjectsApi);
 | 
			
		||||
 | 
			
		||||
    const ingressList = await networking.listIngressForAllNamespaces(null, null, null, null)
 | 
			
		||||
    const ingressList = await networking
 | 
			
		||||
      .listIngressForAllNamespaces(null, null, null, null)
 | 
			
		||||
      .then((response) => response.body)
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        logger.error("Error getting ingresses: %d %s %s", error.statusCode, error.body, error.response);
 | 
			
		||||
        return null;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    const traefikIngressListContaino = await crd.listClusterCustomObject("traefik.containo.us", "v1alpha1", "ingressroutes")
 | 
			
		||||
    const traefikIngressListContaino = await crd
 | 
			
		||||
      .listClusterCustomObject("traefik.containo.us", "v1alpha1", "ingressroutes")
 | 
			
		||||
      .then((response) => response.body)
 | 
			
		||||
      .catch(async (error) => {
 | 
			
		||||
        if (error.statusCode !== 404) {
 | 
			
		||||
          logger.error("Error getting traefik ingresses from traefik.containo.us: %d %s %s", error.statusCode, error.body, error.response);
 | 
			
		||||
          logger.error(
 | 
			
		||||
            "Error getting traefik ingresses from traefik.containo.us: %d %s %s",
 | 
			
		||||
            error.statusCode,
 | 
			
		||||
            error.body,
 | 
			
		||||
            error.response
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    const traefikIngressListIo = await crd.listClusterCustomObject("traefik.io", "v1alpha1", "ingressroutes")
 | 
			
		||||
    const traefikIngressListIo = await crd
 | 
			
		||||
      .listClusterCustomObject("traefik.io", "v1alpha1", "ingressroutes")
 | 
			
		||||
      .then((response) => response.body)
 | 
			
		||||
      .catch(async (error) => {
 | 
			
		||||
        if (error.statusCode !== 404) {
 | 
			
		||||
          logger.error("Error getting traefik ingresses from traefik.io: %d %s %s", error.statusCode, error.body, error.response);
 | 
			
		||||
        }        
 | 
			
		||||
        
 | 
			
		||||
          logger.error(
 | 
			
		||||
            "Error getting traefik ingresses from traefik.io: %d %s %s",
 | 
			
		||||
            error.statusCode,
 | 
			
		||||
            error.body,
 | 
			
		||||
            error.response
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
      });
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    const traefikIngressList = [...traefikIngressListContaino, ...traefikIngressListIo];
 | 
			
		||||
 | 
			
		||||
    if (traefikIngressList && traefikIngressList.items.length > 0) {
 | 
			
		||||
      const traefikServices = traefikIngressList.items
 | 
			
		||||
      .filter((ingress) => ingress.metadata.annotations && ingress.metadata.annotations[`${ANNOTATION_BASE}/href`])
 | 
			
		||||
      const traefikServices = traefikIngressList.items.filter(
 | 
			
		||||
        (ingress) => ingress.metadata.annotations && ingress.metadata.annotations[`${ANNOTATION_BASE}/href`]
 | 
			
		||||
      );
 | 
			
		||||
      ingressList.items.push(...traefikServices);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -191,44 +209,52 @@ export async function servicesFromKubernetes() {
 | 
			
		||||
      return [];
 | 
			
		||||
    }
 | 
			
		||||
    const services = ingressList.items
 | 
			
		||||
      .filter((ingress) => ingress.metadata.annotations && ingress.metadata.annotations[`${ANNOTATION_BASE}/enabled`] === 'true')
 | 
			
		||||
      .filter(
 | 
			
		||||
        (ingress) =>
 | 
			
		||||
          ingress.metadata.annotations && ingress.metadata.annotations[`${ANNOTATION_BASE}/enabled`] === "true"
 | 
			
		||||
      )
 | 
			
		||||
      .map((ingress) => {
 | 
			
		||||
      let constructedService = {
 | 
			
		||||
        app: ingress.metadata.name,
 | 
			
		||||
        namespace: ingress.metadata.namespace,
 | 
			
		||||
        href: ingress.metadata.annotations[`${ANNOTATION_BASE}/href`] || getUrlFromIngress(ingress),
 | 
			
		||||
        name: ingress.metadata.annotations[`${ANNOTATION_BASE}/name`] || ingress.metadata.name,
 | 
			
		||||
        group: ingress.metadata.annotations[`${ANNOTATION_BASE}/group`] || "Kubernetes",
 | 
			
		||||
        weight: ingress.metadata.annotations[`${ANNOTATION_BASE}/weight`] || '0',
 | 
			
		||||
        icon: ingress.metadata.annotations[`${ANNOTATION_BASE}/icon`] || '',
 | 
			
		||||
        description: ingress.metadata.annotations[`${ANNOTATION_BASE}/description`] || '',
 | 
			
		||||
        external: false,
 | 
			
		||||
        type: 'service'
 | 
			
		||||
      };
 | 
			
		||||
      if (ingress.metadata.annotations[`${ANNOTATION_BASE}/external`]) {
 | 
			
		||||
        constructedService.external = String(ingress.metadata.annotations[`${ANNOTATION_BASE}/external`]).toLowerCase() === "true"
 | 
			
		||||
      }
 | 
			
		||||
      if (ingress.metadata.annotations[ANNOTATION_POD_SELECTOR]) {
 | 
			
		||||
        constructedService.podSelector = ingress.metadata.annotations[ANNOTATION_POD_SELECTOR];
 | 
			
		||||
      }
 | 
			
		||||
      if (ingress.metadata.annotations[`${ANNOTATION_BASE}/ping`]) {
 | 
			
		||||
        constructedService.ping = ingress.metadata.annotations[`${ANNOTATION_BASE}/ping`];
 | 
			
		||||
      }
 | 
			
		||||
      Object.keys(ingress.metadata.annotations).forEach((annotation) => {
 | 
			
		||||
        if (annotation.startsWith(ANNOTATION_WIDGET_BASE)) {
 | 
			
		||||
          shvl.set(constructedService, annotation.replace(`${ANNOTATION_BASE}/`, ""), ingress.metadata.annotations[annotation]);
 | 
			
		||||
        let constructedService = {
 | 
			
		||||
          app: ingress.metadata.name,
 | 
			
		||||
          namespace: ingress.metadata.namespace,
 | 
			
		||||
          href: ingress.metadata.annotations[`${ANNOTATION_BASE}/href`] || getUrlFromIngress(ingress),
 | 
			
		||||
          name: ingress.metadata.annotations[`${ANNOTATION_BASE}/name`] || ingress.metadata.name,
 | 
			
		||||
          group: ingress.metadata.annotations[`${ANNOTATION_BASE}/group`] || "Kubernetes",
 | 
			
		||||
          weight: ingress.metadata.annotations[`${ANNOTATION_BASE}/weight`] || "0",
 | 
			
		||||
          icon: ingress.metadata.annotations[`${ANNOTATION_BASE}/icon`] || "",
 | 
			
		||||
          description: ingress.metadata.annotations[`${ANNOTATION_BASE}/description`] || "",
 | 
			
		||||
          external: false,
 | 
			
		||||
          type: "service",
 | 
			
		||||
        };
 | 
			
		||||
        if (ingress.metadata.annotations[`${ANNOTATION_BASE}/external`]) {
 | 
			
		||||
          constructedService.external =
 | 
			
		||||
            String(ingress.metadata.annotations[`${ANNOTATION_BASE}/external`]).toLowerCase() === "true";
 | 
			
		||||
        }
 | 
			
		||||
        if (ingress.metadata.annotations[ANNOTATION_POD_SELECTOR]) {
 | 
			
		||||
          constructedService.podSelector = ingress.metadata.annotations[ANNOTATION_POD_SELECTOR];
 | 
			
		||||
        }
 | 
			
		||||
        if (ingress.metadata.annotations[`${ANNOTATION_BASE}/ping`]) {
 | 
			
		||||
          constructedService.ping = ingress.metadata.annotations[`${ANNOTATION_BASE}/ping`];
 | 
			
		||||
        }
 | 
			
		||||
        Object.keys(ingress.metadata.annotations).forEach((annotation) => {
 | 
			
		||||
          if (annotation.startsWith(ANNOTATION_WIDGET_BASE)) {
 | 
			
		||||
            shvl.set(
 | 
			
		||||
              constructedService,
 | 
			
		||||
              annotation.replace(`${ANNOTATION_BASE}/`, ""),
 | 
			
		||||
              ingress.metadata.annotations[annotation]
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
          constructedService = JSON.parse(substituteEnvironmentVars(JSON.stringify(constructedService)));
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          logger.error("Error attempting k8s environment variable substitution.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return constructedService;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        constructedService = JSON.parse(substituteEnvironmentVars(JSON.stringify(constructedService)));
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        logger.error("Error attempting k8s environment variable substitution.");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return constructedService;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const mappedServiceGroups = [];
 | 
			
		||||
 | 
			
		||||
    services.forEach((serverService) => {
 | 
			
		||||
@ -251,7 +277,6 @@ export async function servicesFromKubernetes() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return mappedServiceGroups;
 | 
			
		||||
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    logger.error(e);
 | 
			
		||||
    throw e;
 | 
			
		||||
@ -264,7 +289,7 @@ export function cleanServiceGroups(groups) {
 | 
			
		||||
    services: serviceGroup.services.map((service) => {
 | 
			
		||||
      const cleanedService = { ...service };
 | 
			
		||||
      if (cleanedService.showStats !== undefined) cleanedService.showStats = JSON.parse(cleanedService.showStats);
 | 
			
		||||
      if (typeof service.weight === 'string') {
 | 
			
		||||
      if (typeof service.weight === "string") {
 | 
			
		||||
        const weight = parseInt(service.weight, 10);
 | 
			
		||||
        if (Number.isNaN(weight)) {
 | 
			
		||||
          cleanedService.weight = 0;
 | 
			
		||||
@ -303,6 +328,7 @@ export function cleanServiceGroups(groups) {
 | 
			
		||||
          userEmail, // azuredevops
 | 
			
		||||
          repositoryId,
 | 
			
		||||
          metric, // glances
 | 
			
		||||
          chart, // glances
 | 
			
		||||
          stream, // mjpeg
 | 
			
		||||
          fit,
 | 
			
		||||
          method, // openmediavault widget
 | 
			
		||||
@ -311,9 +337,10 @@ export function cleanServiceGroups(groups) {
 | 
			
		||||
        } = cleanedService.widget;
 | 
			
		||||
 | 
			
		||||
        let fieldsList = fields;
 | 
			
		||||
        if (typeof fields === 'string') {
 | 
			
		||||
          try { JSON.parse(fields) }
 | 
			
		||||
          catch (e) {
 | 
			
		||||
        if (typeof fields === "string") {
 | 
			
		||||
          try {
 | 
			
		||||
            JSON.parse(fields);
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            logger.error("Invalid fields list detected in config for service '%s'", service.name);
 | 
			
		||||
            fieldsList = null;
 | 
			
		||||
          }
 | 
			
		||||
@ -373,6 +400,11 @@ export function cleanServiceGroups(groups) {
 | 
			
		||||
        }
 | 
			
		||||
        if (type === "glances") {
 | 
			
		||||
          if (metric) cleanedService.widget.metric = metric;
 | 
			
		||||
          if (chart !== undefined) {
 | 
			
		||||
            cleanedService.widget.chart = chart;
 | 
			
		||||
          } else {
 | 
			
		||||
            cleanedService.widget.chart = true;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (type === "mjpeg") {
 | 
			
		||||
          if (stream) cleanedService.widget.stream = stream;
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
export default function Container({ children, className = "" }) {
 | 
			
		||||
export default function Container({ children, chart = true, className = "" }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      {children}
 | 
			
		||||
      <div className={`absolute top-0 right-0 bottom-0 left-0 overflow-clip pointer-events-none ${className}`} />
 | 
			
		||||
      <div className="h-[68px] overflow-clip" />
 | 
			
		||||
      { chart && <div className="h-[68px] overflow-clip" /> }
 | 
			
		||||
      { !chart && <div className="h-[16px] overflow-clip" /> }
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,8 @@ const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
@ -44,32 +46,45 @@ export default function Component({ service }) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <Chart
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[t("resources.used")]}
 | 
			
		||||
        formatter={(value) => t("common.number", {
 | 
			
		||||
          value,
 | 
			
		||||
          style: "unit",
 | 
			
		||||
          unit: "percent",
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
          })}
 | 
			
		||||
      />
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      { chart && (
 | 
			
		||||
        <Chart
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          label={[t("resources.used")]}
 | 
			
		||||
          formatter={(value) => t("common.number", {
 | 
			
		||||
            value,
 | 
			
		||||
            style: "unit",
 | 
			
		||||
            unit: "percent",
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
            })}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      { !chart && (
 | 
			
		||||
        <Block position="top-3 right-3">
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {systemData.linux_distro && `${systemData.linux_distro} - ` }
 | 
			
		||||
            {systemData.os_version && systemData.os_version }
 | 
			
		||||
          </div>
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {systemData && !systemError && (
 | 
			
		||||
        <Block position="bottom-3 left-3">
 | 
			
		||||
          {systemData.linux_distro && (
 | 
			
		||||
          {systemData.linux_distro && chart && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.linux_distro}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {systemData.os_version && (
 | 
			
		||||
 | 
			
		||||
          {systemData.os_version && chart && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.os_version}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
 | 
			
		||||
          {systemData.hostname && (
 | 
			
		||||
            <div className="text-xs opacity-75">
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.hostname}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ const pointsLimit = 15;
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
  const [, diskName] = widget.metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ read_bytes: 0, write_bytes: 0, time_since_update: 0 }, 0, pointsLimit));
 | 
			
		||||
@ -65,24 +66,26 @@ export default function Component({ service }) {
 | 
			
		||||
  const currentRate = diskRates[diskRates.length - 1];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
        dataPoints={ratePoints}
 | 
			
		||||
        label={[t("glances.read"), t("glances.write")]}
 | 
			
		||||
        max={diskData.critical}
 | 
			
		||||
        formatter={(value) => t("common.bitrate", {
 | 
			
		||||
          value,
 | 
			
		||||
          })}
 | 
			
		||||
      />
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      { chart && (
 | 
			
		||||
        <ChartDual
 | 
			
		||||
          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">
 | 
			
		||||
        <Block position={chart ? "bottom-3 left-3" : "bottom-3 right-3"}>
 | 
			
		||||
          <div className="text-xs opacity-50 text-right">
 | 
			
		||||
            {t("common.bitrate", {
 | 
			
		||||
              value: currentRate.a,
 | 
			
		||||
            })} {t("glances.read")}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
          <div className="text-xs opacity-50 text-right">
 | 
			
		||||
            {t("common.bitrate", {
 | 
			
		||||
              value: currentRate.b,
 | 
			
		||||
            })} {t("glances.write")}
 | 
			
		||||
@ -90,7 +93,7 @@ export default function Component({ service }) {
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3">
 | 
			
		||||
      <Block position={chart ? "bottom-3 right-3" : "bottom-3 left-3"}>
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bitrate", {
 | 
			
		||||
            value: currentRate.a + currentRate.b,
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
  const [, fsName] = widget.metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(widget, 'fs', {
 | 
			
		||||
@ -30,35 +31,52 @@ export default function Component({ service }) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <div className="absolute top-0 left-0 right-0 bottom-0">
 | 
			
		||||
        <div style={{
 | 
			
		||||
          height: `${Math.max(20, (fsData.size/fsData.free))}%`,
 | 
			
		||||
        }} className="absolute bottom-0 border-t border-t-theme-500 bg-gradient-to-b from-theme-500/40 to-theme-500/10 w-full" />
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      { chart && (
 | 
			
		||||
        <div className="absolute top-0 left-0 right-0 bottom-0">
 | 
			
		||||
          <div style={{
 | 
			
		||||
            top: `${100-Math.max(18, (fsData.size/fsData.free))}%`,
 | 
			
		||||
          }} className="relative -my-5 ml-2.5 text-xs opacity-50">
 | 
			
		||||
            height: `${Math.max(20, (fsData.size/fsData.free))}%`,
 | 
			
		||||
          }} className="absolute bottom-0 border-t border-t-theme-500 bg-gradient-to-b from-theme-500/40 to-theme-500/10 w-full" />
 | 
			
		||||
        </div>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 left-3">
 | 
			
		||||
        { fsData.used && chart && (
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {t("common.bbytes", {
 | 
			
		||||
              value: fsData.used,
 | 
			
		||||
              maximumFractionDigits: 0,
 | 
			
		||||
            })} {t("resources.used")}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div style={{
 | 
			
		||||
            top: `${100-Math.max(22, (fsData.size/fsData.free))}%`,
 | 
			
		||||
          }} className="relative my-7 ml-2.5 text-xs opacity-50">
 | 
			
		||||
            {t("common.bbytes", {
 | 
			
		||||
              value: fsData.free,
 | 
			
		||||
              maximumFractionDigits: 0,
 | 
			
		||||
            })} {t("resources.free")}
 | 
			
		||||
          </div>
 | 
			
		||||
      </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
      <Block position="top-3 right-3">
 | 
			
		||||
        <div className="border rounded-md px-1.5 py-0.5 bg-theme-400/30 border-white/30 font-bold opacity-75">
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bbytes", {
 | 
			
		||||
            value: fsData.free,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
          })} {t("resources.free")}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
 | 
			
		||||
      { !chart && (
 | 
			
		||||
        <Block position="top-3 right-3">
 | 
			
		||||
          {fsData.used && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {t("common.bbytes", {
 | 
			
		||||
                value: fsData.used,
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })} {t("resources.used")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3">
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bbytes", {
 | 
			
		||||
            value: fsData.size,
 | 
			
		||||
            maximumFractionDigits: 1,
 | 
			
		||||
          })}
 | 
			
		||||
          })} {t("resources.total")}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ const pointsLimit = 15;
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
  const [, gpuName] = widget.metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit));
 | 
			
		||||
@ -56,48 +57,84 @@ export default function Component({ service }) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[t("glances.mem"), t("glances.gpu")]}
 | 
			
		||||
        stack={['mem', 'proc']}
 | 
			
		||||
        formatter={(value) => t("common.percent", {
 | 
			
		||||
          value,
 | 
			
		||||
          maximumFractionDigits: 1,
 | 
			
		||||
        })}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 left-3">
 | 
			
		||||
        {gpuData && gpuData.name && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {gpuData.name}
 | 
			
		||||
            </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.number", {
 | 
			
		||||
            value: gpuData.mem,
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      { chart && (
 | 
			
		||||
          <ChartDual
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          label={[t("glances.mem"), t("glances.gpu")]}
 | 
			
		||||
          stack={['mem', 'proc']}
 | 
			
		||||
          formatter={(value) => t("common.percent", {
 | 
			
		||||
            value,
 | 
			
		||||
            maximumFractionDigits: 1,
 | 
			
		||||
          })}% {t("glances.mem")} {t("resources.used")}
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
          })}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      { chart && (
 | 
			
		||||
        <Block position="bottom-3 left-3">
 | 
			
		||||
          {gpuData && gpuData.name && (
 | 
			
		||||
              <div className="text-xs opacity-50">
 | 
			
		||||
                {gpuData.name}
 | 
			
		||||
              </div>
 | 
			
		||||
          )}
 | 
			
		||||
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {t("common.number", {
 | 
			
		||||
              value: gpuData.mem,
 | 
			
		||||
              maximumFractionDigits: 1,
 | 
			
		||||
            })}% {t("resources.mem")}
 | 
			
		||||
          </div>
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      { !chart && (
 | 
			
		||||
        <Block position="bottom-3 left-3">
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {t("common.number", {
 | 
			
		||||
              value: gpuData.temperature,
 | 
			
		||||
              maximumFractionDigits: 1,
 | 
			
		||||
            })}° C
 | 
			
		||||
          </div>
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3">
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.number", {
 | 
			
		||||
            value: gpuData.proc,
 | 
			
		||||
            maximumFractionDigits: 1,
 | 
			
		||||
          })}% {t("glances.gpu")}
 | 
			
		||||
          {!chart && (
 | 
			
		||||
            <div className="inline-block mr-1">
 | 
			
		||||
              {t("common.number", {
 | 
			
		||||
                value: gpuData.proc,
 | 
			
		||||
                maximumFractionDigits: 1,
 | 
			
		||||
              })}% {t("glances.gpu")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          { !chart && (
 | 
			
		||||
            <>•</>
 | 
			
		||||
          )}
 | 
			
		||||
          <div className="inline-block ml-1">
 | 
			
		||||
            {t("common.number", {
 | 
			
		||||
              value: gpuData.proc,
 | 
			
		||||
              maximumFractionDigits: 1,
 | 
			
		||||
            })}% {t("glances.gpu")}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
 | 
			
		||||
      <Block position="top-3 right-3">
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.number", {
 | 
			
		||||
            value: gpuData.temperature,
 | 
			
		||||
            maximumFractionDigits: 1,
 | 
			
		||||
          })}°
 | 
			
		||||
        </div>
 | 
			
		||||
        { chart && (
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {t("common.number", {
 | 
			
		||||
              value: gpuData.temperature,
 | 
			
		||||
              maximumFractionDigits: 1,
 | 
			
		||||
            })}° C
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        {gpuData && gpuData.name && !chart && (
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {gpuData.name}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,65 @@ import Block from "../components/block";
 | 
			
		||||
 | 
			
		||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
 | 
			
		||||
function Swap({ quicklookData, className = "" }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return quicklookData && quicklookData.swap !== 0 && (
 | 
			
		||||
    <div className="text-xs flex place-content-between">
 | 
			
		||||
      <div className={className}>{t("glances.swap")}</div>
 | 
			
		||||
      <div className={className}>
 | 
			
		||||
        {t("common.number", {
 | 
			
		||||
          value: quicklookData.swap,
 | 
			
		||||
          style: "unit",
 | 
			
		||||
          unit: "percent",
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function CPU({ quicklookData, className = "" }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return quicklookData && quicklookData.cpu && (
 | 
			
		||||
    <div className="text-xs flex place-content-between">
 | 
			
		||||
      <div className={className}>{t("glances.cpu")}</div>
 | 
			
		||||
      <div className={className}>
 | 
			
		||||
        {t("common.number", {
 | 
			
		||||
          value: quicklookData.cpu,
 | 
			
		||||
          style: "unit",
 | 
			
		||||
          unit: "percent",
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Mem({ quicklookData, className = "" }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return quicklookData && quicklookData.mem && (
 | 
			
		||||
    <div className="text-xs flex place-content-between">
 | 
			
		||||
      <div className={className}>{t("glances.mem")}</div>
 | 
			
		||||
      <div className={className}>
 | 
			
		||||
        {t("common.number", {
 | 
			
		||||
          value: quicklookData.mem,
 | 
			
		||||
          style: "unit",
 | 
			
		||||
          unit: "percent",
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
 | 
			
		||||
  const { data: quicklookData, errorL: quicklookError } = useWidgetAPI(service.widget, 'quicklook', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
  });
 | 
			
		||||
@ -41,74 +97,59 @@ export default function Component({ service }) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container className="bg-gradient-to-br from-theme-500/30 via-theme-600/20 to-theme-700/10">
 | 
			
		||||
    <Container chart={chart} className="bg-gradient-to-br from-theme-500/30 via-theme-600/20 to-theme-700/10">
 | 
			
		||||
      <Block position="top-3 right-3">
 | 
			
		||||
        {quicklookData && quicklookData.cpu_name && (
 | 
			
		||||
        {quicklookData && quicklookData.cpu_name && chart && (
 | 
			
		||||
          <div className="text-[0.6rem] opacity-50">
 | 
			
		||||
            {quicklookData.cpu_name}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        { !chart && quicklookData?.swap === 0 && (
 | 
			
		||||
          <div className="text-[0.6rem] opacity-50">
 | 
			
		||||
            {quicklookData.cpu_name}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        <div className="w-[4rem]">
 | 
			
		||||
          { !chart && <Swap quicklookData={quicklookData} className="opacity-25" /> }
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
      <Block position="bottom-3 left-3">
 | 
			
		||||
        {systemData && systemData.linux_distro && (
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {systemData.linux_distro}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
        {systemData && systemData.os_version && (
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {systemData.os_version}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
        {systemData && systemData.hostname && (
 | 
			
		||||
          <div className="text-xs opacity-75">
 | 
			
		||||
            {systemData.hostname}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
      </Block>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      {chart && (
 | 
			
		||||
        <Block position="bottom-3 left-3">
 | 
			
		||||
          {systemData && systemData.linux_distro && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.linux_distro}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {systemData && systemData.os_version && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {systemData.os_version}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {systemData && systemData.hostname && (
 | 
			
		||||
            <div className="text-xs opacity-75">
 | 
			
		||||
              {systemData.hostname}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {!chart && (
 | 
			
		||||
        <Block position="bottom-3 left-3 w-[3rem]">
 | 
			
		||||
          <CPU quicklookData={quicklookData} className="opacity-75" />
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3 w-[4rem]">
 | 
			
		||||
        {quicklookData && quicklookData.cpu && (
 | 
			
		||||
          <div className="text-xs opacity-25 flex place-content-between">
 | 
			
		||||
            <div>{t("glances.cpu")}</div>
 | 
			
		||||
            <div className="opacity-75">
 | 
			
		||||
              {t("common.number", {
 | 
			
		||||
                value: quicklookData.cpu,
 | 
			
		||||
                style: "unit",
 | 
			
		||||
                unit: "percent",
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
        { chart && <CPU quicklookData={quicklookData} className="opacity-50" /> }
 | 
			
		||||
 | 
			
		||||
        {quicklookData && quicklookData.mem && (
 | 
			
		||||
          <div className="text-xs opacity-25 flex place-content-between">
 | 
			
		||||
            <div>{t("glances.mem")}</div>
 | 
			
		||||
            <div className="opacity-75">
 | 
			
		||||
              {t("common.number", {
 | 
			
		||||
                value: quicklookData.mem,
 | 
			
		||||
                style: "unit",
 | 
			
		||||
                unit: "percent",
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
        { chart && <Mem quicklookData={quicklookData} className="opacity-50" /> }
 | 
			
		||||
        { !chart && <Mem quicklookData={quicklookData} className="opacity-75" /> }
 | 
			
		||||
 | 
			
		||||
        {quicklookData && quicklookData.swap !== 0 && (
 | 
			
		||||
          <div className="text-xs opacity-25 flex place-content-between">
 | 
			
		||||
            <div>{t("glances.swap")}</div>
 | 
			
		||||
            <div className="opacity-75">
 | 
			
		||||
              {t("common.number", {
 | 
			
		||||
                value: quicklookData.swap,
 | 
			
		||||
                style: "unit",
 | 
			
		||||
                unit: "percent",
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
              })}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
        { chart && <Swap quicklookData={quicklookData} className="opacity-50" /> }
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -14,11 +14,14 @@ const pointsLimit = 15;
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'mem', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
    refreshInterval: chart ? 1000 : 5000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
@ -42,21 +45,23 @@ export default function Component({ service }) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        max={data.total}
 | 
			
		||||
        label={[t("resources.used"), t("resources.free")]}
 | 
			
		||||
        formatter={(value) => t("common.bytes", {
 | 
			
		||||
          value,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
          binary: true,
 | 
			
		||||
        })}
 | 
			
		||||
      />
 | 
			
		||||
    <Container chart={chart} >
 | 
			
		||||
      {chart && (
 | 
			
		||||
        <ChartDual
 | 
			
		||||
          dataPoints={dataPoints}
 | 
			
		||||
          max={data.total}
 | 
			
		||||
          label={[t("resources.used"), t("resources.free")]}
 | 
			
		||||
          formatter={(value) => t("common.bytes", {
 | 
			
		||||
            value,
 | 
			
		||||
            maximumFractionDigits: 0,
 | 
			
		||||
            binary: true,
 | 
			
		||||
          })}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {data && !error && (
 | 
			
		||||
        <Block position="bottom-3 left-3">
 | 
			
		||||
          {data.free && (
 | 
			
		||||
          {data.free && chart && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {t("common.bytes", {
 | 
			
		||||
                value: data.free,
 | 
			
		||||
@ -78,6 +83,20 @@ export default function Component({ service }) {
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      { !chart && (
 | 
			
		||||
        <Block position="top-3 right-3">
 | 
			
		||||
          {data.free && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {t("common.bytes", {
 | 
			
		||||
                value: data.free,
 | 
			
		||||
                maximumFractionDigits: 0,
 | 
			
		||||
                binary: true,
 | 
			
		||||
              })} {t("resources.free")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3">
 | 
			
		||||
        <div className="text-xs font-bold opacity-75">
 | 
			
		||||
          {t("common.bytes", {
 | 
			
		||||
 | 
			
		||||
@ -15,12 +15,13 @@ const pointsLimit = 15;
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const [, interfaceName] = widget.metric.split(':');
 | 
			
		||||
  const { chart, metric } = widget;
 | 
			
		||||
  const [, interfaceName] = metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(widget, 'network', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
    refreshInterval: chart ? 1000 : 5000,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
@ -54,18 +55,20 @@ export default function Component({ service }) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <ChartDual
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[t("docker.tx"), t("docker.rx")]}
 | 
			
		||||
        formatter={(value) => t("common.byterate", {
 | 
			
		||||
          value,
 | 
			
		||||
          maximumFractionDigits: 0,
 | 
			
		||||
        })}
 | 
			
		||||
      />
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      { chart && (
 | 
			
		||||
        <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 && (
 | 
			
		||||
        {interfaceData && interfaceData.interface_name && chart && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {interfaceData.interface_name}
 | 
			
		||||
            </div>
 | 
			
		||||
@ -79,6 +82,16 @@ export default function Component({ service }) {
 | 
			
		||||
        </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
 | 
			
		||||
      { !chart && (
 | 
			
		||||
        <Block position="top-3 right-3">
 | 
			
		||||
          {interfaceData && interfaceData.interface_name && (
 | 
			
		||||
              <div className="text-xs opacity-50">
 | 
			
		||||
                {interfaceData.interface_name}
 | 
			
		||||
              </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3">
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.bitrate", {
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,8 @@ const statusMap = {
 | 
			
		||||
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useWidgetAPI(service.widget, 'processlist', {
 | 
			
		||||
    refreshInterval: 1000,
 | 
			
		||||
@ -32,10 +34,10 @@ export default function Component({ service }) {
 | 
			
		||||
    return <Container><Block position="bottom-3 left-3">-</Block></Container>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data.splice(5);
 | 
			
		||||
  data.splice(chart ? 5 : 1);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      <Block position="top-4 right-3 left-3">
 | 
			
		||||
        <div className="flex items-center text-xs">
 | 
			
		||||
          <div className="grow" />
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ const pointsLimit = 15;
 | 
			
		||||
export default function Component({ service }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { widget } = service;
 | 
			
		||||
  const { chart } = widget;
 | 
			
		||||
  const [, sensorName] = widget.metric.split(':');
 | 
			
		||||
 | 
			
		||||
  const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
 | 
			
		||||
@ -51,37 +52,46 @@ export default function Component({ service }) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container>
 | 
			
		||||
      <Chart
 | 
			
		||||
        dataPoints={dataPoints}
 | 
			
		||||
        label={[sensorData.unit]}
 | 
			
		||||
        max={sensorData.critical}
 | 
			
		||||
        formatter={(value) => t("common.number", {
 | 
			
		||||
          value,
 | 
			
		||||
          })}
 | 
			
		||||
      />
 | 
			
		||||
    <Container chart={chart}>
 | 
			
		||||
      { chart && (
 | 
			
		||||
        <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 && (
 | 
			
		||||
          {sensorData.warning && chart && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {sensorData.warning}{sensorData.unit} {t("glances.warn")}
 | 
			
		||||
              {t("glances.warn")} {sensorData.warning} {sensorData.unit}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {sensorData.critical && (
 | 
			
		||||
            <div className="text-xs opacity-50">
 | 
			
		||||
              {sensorData.critical} {sensorData.unit} {t("glances.crit")}
 | 
			
		||||
              {t("glances.crit")} {sensorData.critical} {sensorData.unit}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </Block>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <Block position="bottom-3 right-3">
 | 
			
		||||
        <div className="text-xs opacity-75">
 | 
			
		||||
          {t("common.number", {
 | 
			
		||||
            value: sensorData.value,
 | 
			
		||||
          })} {sensorData.unit}
 | 
			
		||||
        </div>
 | 
			
		||||
          <div className="text-xs opacity-50">
 | 
			
		||||
            {sensorData.warning && !chart && (
 | 
			
		||||
              <>
 | 
			
		||||
                {t("glances.warn")} {sensorData.warning} {sensorData.unit}
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="text-xs opacity-75">
 | 
			
		||||
            {t("glances.temp")} {t("common.number", {
 | 
			
		||||
              value: sensorData.value,
 | 
			
		||||
            })} {sensorData.unit}
 | 
			
		||||
          </div>
 | 
			
		||||
      </Block>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user