mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-04 08:20:58 +00:00 
			
		
		
		
	Revert "Added optional boxed styling for information widgets and refactored information widgets"
This commit is contained in:
		
							parent
							
								
									347761fcad
								
							
						
					
					
						commit
						6b2930ab8d
					
				@ -1,9 +1,6 @@
 | 
				
			|||||||
import { useState, useEffect } from "react";
 | 
					import { useState, useEffect } from "react";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const textSizes = {
 | 
					const textSizes = {
 | 
				
			||||||
  "4xl": "text-4xl",
 | 
					  "4xl": "text-4xl",
 | 
				
			||||||
  "3xl": "text-3xl",
 | 
					  "3xl": "text-3xl",
 | 
				
			||||||
@ -30,14 +27,12 @@ export default function DateTime({ options }) {
 | 
				
			|||||||
  }, [date, setDate, dateLocale, format]);
 | 
					  }, [date, setDate, dateLocale, format]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container options={options}>
 | 
					    <div className="flex flex-col justify-center first:ml-0 ml-4">
 | 
				
			||||||
      <Raw>
 | 
					 | 
				
			||||||
      <div className="flex flex-row items-center grow justify-end">
 | 
					      <div className="flex flex-row items-center grow justify-end">
 | 
				
			||||||
        <span className={`text-theme-800 dark:text-theme-200 tabular-nums ${textSizes[textSize || "lg"]}`}>
 | 
					        <span className={`text-theme-800 dark:text-theme-200 tabular-nums ${textSizes[textSize || "lg"]}`}>
 | 
				
			||||||
          {date}
 | 
					          {date}
 | 
				
			||||||
        </span>
 | 
					        </span>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      </Raw>
 | 
					    </div>
 | 
				
			||||||
    </Container>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,11 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { useContext } from "react";
 | 
					import { useContext } from "react";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa";
 | 
					import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa";
 | 
				
			||||||
import { FiCpu, FiHardDrive } from "react-icons/fi";
 | 
					import { FiCpu, FiHardDrive } from "react-icons/fi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Error from "../widget/error";
 | 
					import UsageBar from "../resources/usage-bar";
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					 | 
				
			||||||
import Resources from "../widget/resources";
 | 
					 | 
				
			||||||
import WidgetLabel from "../widget/widget_label";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SettingsContext } from "utils/contexts/settings";
 | 
					import { SettingsContext } from "utils/contexts/settings";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,17 +26,52 @@ export default function Widget({ options }) {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4">
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-row items-center">
 | 
				
			||||||
 | 
					            <BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Resources options={options}>
 | 
					    return (
 | 
				
			||||||
      <Resource icon={FiCpu} label={t("glances.wait")} percentage="0" />
 | 
					      <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4">
 | 
				
			||||||
      <Resource icon={FaMemory} label={t("glances.wait")} percentage="0" />
 | 
					        <div className="flex flex-row self-center flex-wrap justify-between">
 | 
				
			||||||
      { options.cputemp && <Resource icon={FaThermometerHalf} label={t("glances.wait")} percentage="0" /> }
 | 
					           <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
      { options.uptime && <Resource icon={FaRegClock} label={t("glances.wait")} percentage="0" /> }
 | 
					            <FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
      { options.label && <WidgetLabel label={options.label} /> }
 | 
					            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
    </Resources>;
 | 
					              <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					                <div className="pl-0.5 text-xs">
 | 
				
			||||||
 | 
					                  {t("glances.wait")}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <UsageBar percent="0" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					            <FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					              <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					                <div className="pl-0.5 text-xs">
 | 
				
			||||||
 | 
					                  {t("glances.wait")}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <UsageBar percent="0" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {options.label && (
 | 
				
			||||||
 | 
					          <div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const unit = options.units === "imperial" ? "fahrenheit" : "celsius";
 | 
					  const unit = options.units === "imperial" ? "fahrenheit" : "celsius";
 | 
				
			||||||
@ -68,84 +101,131 @@ export default function Widget({ options }) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Resources options={options} target={settings.target ?? "_blank"}>
 | 
					    <a href={options.url} target={settings.target ?? "_blank"} className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
      <Resource
 | 
					      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
				
			||||||
        icon={FiCpu}
 | 
					         <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
        value={t("common.number", {
 | 
					          <FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					              <div className="pl-0.5">
 | 
				
			||||||
 | 
					                {t("common.number", {
 | 
				
			||||||
                  value: data.cpu.total,
 | 
					                  value: data.cpu.total,
 | 
				
			||||||
                  style: "unit",
 | 
					                  style: "unit",
 | 
				
			||||||
                  unit: "percent",
 | 
					                  unit: "percent",
 | 
				
			||||||
                  maximumFractionDigits: 0,
 | 
					                  maximumFractionDigits: 0,
 | 
				
			||||||
                })}
 | 
					                })}
 | 
				
			||||||
        label={t("glances.cpu")}
 | 
					              </div>
 | 
				
			||||||
        expandedValue={t("common.number", {
 | 
					              <div className="pr-1">{t("glances.cpu")}</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            {options.expanded && (
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					                <div className="pl-0.5 pr-1">
 | 
				
			||||||
 | 
					                {t("common.number", {
 | 
				
			||||||
                  value: data.load.min15,
 | 
					                  value: data.load.min15,
 | 
				
			||||||
                  style: "unit",
 | 
					                  style: "unit",
 | 
				
			||||||
                  unit: "percent",
 | 
					                  unit: "percent",
 | 
				
			||||||
          maximumFractionDigits: 0
 | 
					                  maximumFractionDigits: 0,
 | 
				
			||||||
                })}
 | 
					                })}
 | 
				
			||||||
        expandedLabel={t("glances.load")}
 | 
					                </div>
 | 
				
			||||||
        percentage={data.cpu.total}
 | 
					                <div className="pr-1">{t("glances.load")}</div>
 | 
				
			||||||
        expanded={options.expanded}
 | 
					              </span>
 | 
				
			||||||
      />
 | 
					            )}
 | 
				
			||||||
      <Resource
 | 
					            <UsageBar percent={data.cpu.total} />
 | 
				
			||||||
        icon={FaMemory}
 | 
					          </div>
 | 
				
			||||||
        value={t("common.bytes", {
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					          <FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					              <div className="pl-0.5">
 | 
				
			||||||
 | 
					                {t("common.bytes", {
 | 
				
			||||||
                  value: data.mem.free,
 | 
					                  value: data.mem.free,
 | 
				
			||||||
                  maximumFractionDigits: 1,
 | 
					                  maximumFractionDigits: 1,
 | 
				
			||||||
                  binary: true,
 | 
					                  binary: true,
 | 
				
			||||||
                })}
 | 
					                })}
 | 
				
			||||||
        label={t("glances.free")}
 | 
					              </div>
 | 
				
			||||||
        expandedValue={t("common.bytes", {
 | 
					              <div className="pr-1">{t("glances.free")}</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            {options.expanded && (
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					                <div className="pl-0.5 pr-1">
 | 
				
			||||||
 | 
					                  {t("common.bytes", {
 | 
				
			||||||
                    value: data.mem.total,
 | 
					                    value: data.mem.total,
 | 
				
			||||||
                    maximumFractionDigits: 1,
 | 
					                    maximumFractionDigits: 1,
 | 
				
			||||||
                    binary: true,
 | 
					                    binary: true,
 | 
				
			||||||
                  })}
 | 
					                  })}
 | 
				
			||||||
        expandedLabel={t("glances.total")}
 | 
					                </div>
 | 
				
			||||||
        percentage={data.mem.percent}
 | 
					                <div className="pr-1">{t("glances.total")}</div>
 | 
				
			||||||
        expanded={options.expanded}
 | 
					              </span>
 | 
				
			||||||
      />
 | 
					            )}
 | 
				
			||||||
 | 
					            <UsageBar percent={data.mem.percent} />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        {disks.map((disk) => (
 | 
					        {disks.map((disk) => (
 | 
				
			||||||
        <Resource key={disk.mnt_point}
 | 
					          <div key={disk.mnt_point} className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
          icon={FiHardDrive}
 | 
					            <FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
          value={t("common.bytes", { value: disk.free })}
 | 
					            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
          label={t("glances.free")}
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
          expandedValue={t("common.bytes", { value: disk.size })}
 | 
					                <div className="pl-0.5">{t("common.bytes", { value: disk.free })}</div>
 | 
				
			||||||
          expandedLabel={t("glances.total")}
 | 
					                <div className="pr-1">{t("glances.free")}</div>
 | 
				
			||||||
          percentage={disk.percent}
 | 
					              </span>
 | 
				
			||||||
          expanded={options.expanded}
 | 
					              {options.expanded && (
 | 
				
			||||||
        />
 | 
					                <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
      ))}
 | 
					                  <div className="pl-0.5 pr-1">{t("common.bytes", { value: disk.size })}</div>
 | 
				
			||||||
 | 
					                  <div className="pr-1">{t("glances.total")}</div>
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					              <UsageBar percent={disk.percent} />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>))}
 | 
				
			||||||
        {options.cputemp && mainTemp > 0 &&
 | 
					        {options.cputemp && mainTemp > 0 &&
 | 
				
			||||||
        <Resource
 | 
					            (<div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
          icon={FaThermometerHalf}
 | 
					            <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
          value={t("common.number", {
 | 
					            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					                <div className="pl-0.5">
 | 
				
			||||||
 | 
					                  {t("common.number", { 
 | 
				
			||||||
                    value: mainTemp,
 | 
					                    value: mainTemp,
 | 
				
			||||||
                    maximumFractionDigits: 1,
 | 
					                    maximumFractionDigits: 1,
 | 
				
			||||||
                    style: "unit",
 | 
					                    style: "unit",
 | 
				
			||||||
                    unit
 | 
					                    unit
 | 
				
			||||||
                  })}
 | 
					                  })}
 | 
				
			||||||
          label={t("glances.temp")}
 | 
					                </div>
 | 
				
			||||||
          expandedValue={t("common.number", {
 | 
					                <div className="pr-1">{t("glances.temp")}</div>
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					              {options.expanded && (
 | 
				
			||||||
 | 
					                <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					                  <div className="pl-0.5 pr-1">
 | 
				
			||||||
 | 
					                  {t("common.number", { 
 | 
				
			||||||
                    value: maxTemp,
 | 
					                    value: maxTemp,
 | 
				
			||||||
                    maximumFractionDigits: 1,
 | 
					                    maximumFractionDigits: 1,
 | 
				
			||||||
                    style: "unit",
 | 
					                    style: "unit",
 | 
				
			||||||
                    unit
 | 
					                    unit
 | 
				
			||||||
                  })}
 | 
					                  })}
 | 
				
			||||||
          expandedLabel={t("glances.warn")}
 | 
					                  </div>
 | 
				
			||||||
          percentage={tempPercent}
 | 
					                  <div className="pr-1">{t("glances.warn")}</div>
 | 
				
			||||||
          expanded={options.expanded}
 | 
					                </span>
 | 
				
			||||||
        />
 | 
					              )}
 | 
				
			||||||
      }
 | 
					              <UsageBar percent={tempPercent} />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>)}
 | 
				
			||||||
        {options.uptime && data.uptime &&
 | 
					        {options.uptime && data.uptime &&
 | 
				
			||||||
        <Resource
 | 
					            (<div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
          icon={FaRegClock}
 | 
					            <FaRegClock className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
          value={data.uptime.replace(" days,", t("glances.days")).replace(/:\d\d:\d\d$/g, t("glances.hours"))}
 | 
					            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
          label={t("glances.uptime")}
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
          percentage={Math.round((new Date().getSeconds() / 60) * 100).toString()}
 | 
					                <div className="pl-0.5">
 | 
				
			||||||
        />
 | 
					                  {data.uptime.replace(" days,", t("glances.days")).replace(/:\d\d:\d\d$/g, t("glances.hours"))}
 | 
				
			||||||
      }
 | 
					                </div>
 | 
				
			||||||
      {options.label && <WidgetLabel label={options.label} />}
 | 
					                <div className="pr-1">{t("glances.uptime")}</div>
 | 
				
			||||||
    </Resources>
 | 
					              </span>
 | 
				
			||||||
 | 
					              <UsageBar percent={Math.round((new Date().getSeconds() / 60) * 100)} />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>)}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      {options.label && (
 | 
				
			||||||
 | 
					        <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,3 @@
 | 
				
			|||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const textSizes = {
 | 
					const textSizes = {
 | 
				
			||||||
  "4xl": "text-4xl",
 | 
					  "4xl": "text-4xl",
 | 
				
			||||||
  "3xl": "text-3xl",
 | 
					  "3xl": "text-3xl",
 | 
				
			||||||
@ -14,12 +11,12 @@ const textSizes = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default function Greeting({ options }) {
 | 
					export default function Greeting({ options }) {
 | 
				
			||||||
  if (options.text) {
 | 
					  if (options.text) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <Raw>
 | 
					      <div className="flex flex-row items-center justify-start">
 | 
				
			||||||
        <span className={`text-theme-800 dark:text-theme-200 mr-3 ${textSizes[options.text_size || "xl"]}`}>
 | 
					        <span className={`text-theme-800 dark:text-theme-200 mr-3 ${textSizes[options.text_size || "xl"]}`}>
 | 
				
			||||||
          {options.text}
 | 
					          {options.text}
 | 
				
			||||||
        </span>
 | 
					        </span>
 | 
				
			||||||
      </Raw>
 | 
					      </div>
 | 
				
			||||||
    </Container>;
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,12 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Node from "./node";
 | 
					import Node from "./node";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Widget({ options }) {
 | 
					export default function Widget({ options }) {
 | 
				
			||||||
  const { cluster, nodes } = options;
 | 
					  const { cluster, nodes } = options;
 | 
				
			||||||
  const { i18n } = useTranslation();
 | 
					  const { t, i18n } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const defaultData = {
 | 
					  const defaultData = {
 | 
				
			||||||
    cpu: {
 | 
					    cpu: {
 | 
				
			||||||
@ -21,7 +18,7 @@ export default function Widget({ options }) {
 | 
				
			|||||||
      used: 0,
 | 
					      used: 0,
 | 
				
			||||||
      total: 0,
 | 
					      total: 0,
 | 
				
			||||||
      free: 0,
 | 
					      free: 0,
 | 
				
			||||||
      percent: 0
 | 
					      precent: 0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,12 +29,23 @@ export default function Widget({ options }) {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4">
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-row items-center">
 | 
				
			||||||
 | 
					            <BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <Raw>
 | 
					      <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
        <div className="flex flex-row self-center flex-wrap justify-between">
 | 
					        <div className="flex flex-row self-center flex-wrap justify-between">
 | 
				
			||||||
          {cluster.show &&
 | 
					          {cluster.show &&
 | 
				
			||||||
            <Node type="cluster" key="cluster" options={options.cluster} data={defaultData} />
 | 
					            <Node type="cluster" key="cluster" options={options.cluster} data={defaultData} />
 | 
				
			||||||
@ -46,12 +54,12 @@ export default function Widget({ options }) {
 | 
				
			|||||||
            <Node type="node" key="nodes" options={options.nodes} data={defaultData} />
 | 
					            <Node type="node" key="nodes" options={options.nodes} data={defaultData} />
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </Raw>
 | 
					      </div>
 | 
				
			||||||
    </Container>;
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Container options={options}>
 | 
					  return (
 | 
				
			||||||
    <Raw>
 | 
					    <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
					      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
				
			||||||
        {cluster.show &&
 | 
					        {cluster.show &&
 | 
				
			||||||
          <Node key="cluster" type="cluster" options={options.cluster} data={data.cluster} />
 | 
					          <Node key="cluster" type="cluster" options={options.cluster} data={data.cluster} />
 | 
				
			||||||
@ -61,6 +69,6 @@ export default function Widget({ options }) {
 | 
				
			|||||||
            <Node key={node.name} type="node" options={options.nodes} data={node} />)
 | 
					            <Node key={node.name} type="node" options={options.nodes} data={node} />)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </Raw>
 | 
					    </div>
 | 
				
			||||||
  </Container>;
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,8 @@ import { FiAlertTriangle, FiCpu, FiServer } from "react-icons/fi";
 | 
				
			|||||||
import { SiKubernetes } from "react-icons/si";
 | 
					import { SiKubernetes } from "react-icons/si";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import UsageBar from "../resources/usage-bar";
 | 
					import UsageBar from "./usage-bar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Node({ type, options, data }) {
 | 
					export default function Node({ type, options, data }) {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
@ -28,7 +29,7 @@ export default function Node({ type, options, data }) {
 | 
				
			|||||||
            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
					            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
              <div className="pl-0.5">
 | 
					              <div className="pl-0.5">
 | 
				
			||||||
                {t("common.number", {
 | 
					                {t("common.number", {
 | 
				
			||||||
                  value: data?.cpu?.percent ?? 0,
 | 
					                  value: data.cpu.percent,
 | 
				
			||||||
                  style: "unit",
 | 
					                  style: "unit",
 | 
				
			||||||
                  unit: "percent",
 | 
					                  unit: "percent",
 | 
				
			||||||
                  maximumFractionDigits: 0
 | 
					                  maximumFractionDigits: 0
 | 
				
			||||||
@ -36,18 +37,18 @@ export default function Node({ type, options, data }) {
 | 
				
			|||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <FiCpu className="text-theme-800 dark:text-theme-200 w-3 h-3" />
 | 
					              <FiCpu className="text-theme-800 dark:text-theme-200 w-3 h-3" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <UsageBar percent={data?.cpu?.percent ?? 0} />
 | 
					            <UsageBar percent={data.cpu.percent} />
 | 
				
			||||||
            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
					            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
              <div className="pl-0.5">
 | 
					              <div className="pl-0.5">
 | 
				
			||||||
                {t("common.bytes", {
 | 
					                {t("common.bytes", {
 | 
				
			||||||
                  value: data?.memory?.free ?? 0,
 | 
					                  value: data.memory.free,
 | 
				
			||||||
                  maximumFractionDigits: 0,
 | 
					                  maximumFractionDigits: 0,
 | 
				
			||||||
                  binary: true
 | 
					                  binary: true
 | 
				
			||||||
                })}
 | 
					                })}
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <FaMemory className="text-theme-800 dark:text-theme-200 w-3 h-3" />
 | 
					              <FaMemory className="text-theme-800 dark:text-theme-200 w-3 h-3" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <UsageBar percent={data?.memory?.percent} />
 | 
					            <UsageBar percent={data.memory.percent} />
 | 
				
			||||||
            {options.showLabel && (
 | 
					            {options.showLabel && (
 | 
				
			||||||
              <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{type === "cluster" ? options.label : data.name}</div>
 | 
					              <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{type === "cluster" ? options.label : data.name}</div>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/components/widgets/kubernetes/usage-bar.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/components/widgets/kubernetes/usage-bar.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					export default function UsageBar({ percent }) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-theme-200/20">
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        className="bg-theme-800/70 h-1 rounded-full dark:bg-theme-200/50 transition-all duration-1000"
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          width: `${percent}%`,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,12 +1,8 @@
 | 
				
			|||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ResolvedIcon from "components/resolvedicon"
 | 
					import ResolvedIcon from "components/resolvedicon"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Logo({ options }) {
 | 
					export default function Logo({ options }) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container options={options}>
 | 
					    <div className="w-12 h-12 flex flex-row items-center align-middle mr-3 self-center">
 | 
				
			||||||
      <Raw>
 | 
					 | 
				
			||||||
      {options.icon ?
 | 
					      {options.icon ?
 | 
				
			||||||
        <ResolvedIcon icon={options.icon} width={48} height={48} /> :
 | 
					        <ResolvedIcon icon={options.icon} width={48} height={48} /> :
 | 
				
			||||||
        // fallback to homepage logo
 | 
					        // fallback to homepage logo
 | 
				
			||||||
@ -61,7 +57,6 @@ export default function Logo({ options }) {
 | 
				
			|||||||
          </g>
 | 
					          </g>
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      </Raw>
 | 
					    </div>
 | 
				
			||||||
    </Container>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,31 +1,37 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import Error from "../widget/error";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Node from "./node";
 | 
					import Node from "./node";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Longhorn({ options }) {
 | 
					export default function Longhorn({ options }) {
 | 
				
			||||||
  const { expanded, total, labels, include, nodes } = options;
 | 
					  const { expanded, total, labels, include, nodes } = options;
 | 
				
			||||||
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
  const { data, error } = useSWR(`/api/widgets/longhorn`, {
 | 
					  const { data, error } = useSWR(`/api/widgets/longhorn`, {
 | 
				
			||||||
    refreshInterval: 1500
 | 
					    refreshInterval: 1500
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
 | 
					        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <Raw>
 | 
					      <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
        <div className="flex flex-row self-center flex-wrap justify-between" />
 | 
					        <div className="flex flex-row self-center flex-wrap justify-between" />
 | 
				
			||||||
      </Raw>
 | 
					      </div>
 | 
				
			||||||
    </Container>;
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Container options={options}>
 | 
					  return (
 | 
				
			||||||
    <Raw>
 | 
					    <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
					      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
				
			||||||
        {data.nodes
 | 
					        {data.nodes
 | 
				
			||||||
          .filter((node) => {
 | 
					          .filter((node) => {
 | 
				
			||||||
@ -46,6 +52,6 @@ export default function Longhorn({ options }) {
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </Raw>
 | 
					    </div>
 | 
				
			||||||
  </Container>;
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,32 @@
 | 
				
			|||||||
 | 
					import { FiHardDrive } from "react-icons/fi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
import { FaThermometerHalf } from "react-icons/fa";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					import UsageBar from "../resources/usage-bar";
 | 
				
			||||||
import WidgetLabel from "../widget/widget_label";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Node({ data, expanded, labels }) {
 | 
					export default function Node({ data, expanded, labels }) {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Resource
 | 
					  return (
 | 
				
			||||||
    icon={FaThermometerHalf}
 | 
					    <>
 | 
				
			||||||
    value={t("common.bytes", { value: data.node.available })}
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
    label={t("resources.free")}
 | 
					        <FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
    expandedValue={t("common.bytes", { value: data.node.maximum })}
 | 
					        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
    expandedLabel={t("resources.total")}
 | 
					        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
    percentage={Math.round(((data.node.maximum - data.node.available) / data.node.maximum) * 100)}
 | 
					          <div className="pl-0.5">{t("common.bytes", { value: data.node.available })}</div>
 | 
				
			||||||
    expanded={expanded}
 | 
					          <div className="pr-1">{t("resources.free")}</div>
 | 
				
			||||||
  >{ labels && <WidgetLabel label={data.node.id} /> }
 | 
					        </span>
 | 
				
			||||||
  </Resource>
 | 
					          {expanded && (
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5">{t("common.bytes", { value: data.node.maximum })}</div>
 | 
				
			||||||
 | 
					            <div className="pr-1">{t("resources.total")}</div>
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          <UsageBar percent={Math.round(((data.node.maximum - data.node.available) / data.node.maximum) * 100)} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      {labels && (
 | 
				
			||||||
 | 
					        <div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{data.node.id}</div>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,10 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { WiCloudDown } from "react-icons/wi";
 | 
					import { WiCloudDown } from "react-icons/wi";
 | 
				
			||||||
import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
 | 
					import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import ContainerButton from "../widget/container_button";
 | 
					 | 
				
			||||||
import WidgetIcon from "../widget/widget_icon";
 | 
					 | 
				
			||||||
import PrimaryText from "../widget/primary_text";
 | 
					 | 
				
			||||||
import SecondaryText from "../widget/secondary_text";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Icon from "./icon";
 | 
					import Icon from "./icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Widget({ options }) {
 | 
					function Widget({ options }) {
 | 
				
			||||||
@ -21,35 +15,60 @@ function Widget({ options }) {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2">
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					            <BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs">-</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("weather.updating")}</PrimaryText>
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2">
 | 
				
			||||||
      <SecondaryText>{t("weather.wait")}</SecondaryText>
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
      <WidgetIcon icon={WiCloudDown} size="l" />
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
    </Container>;
 | 
					            <WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-sm">{t("weather.updating")}</span>
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("weather.wait")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const unit = options.units === "metric" ? "celsius" : "fahrenheit";
 | 
					  const unit = options.units === "metric" ? "celsius" : "fahrenheit";
 | 
				
			||||||
  const weatherInfo = {
 | 
					  const timeOfDay = data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night";
 | 
				
			||||||
    condition: data.current_weather.weathercode,
 | 
					 | 
				
			||||||
    timeOfDay: data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night"
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Container options={options}>
 | 
					  return (
 | 
				
			||||||
    <PrimaryText>
 | 
					    <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2">
 | 
				
			||||||
 | 
					      <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					        <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					          <Icon condition={data.current_weather.weathercode} timeOfDay={timeOfDay} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-sm">
 | 
				
			||||||
            {options.label && `${options.label}, `}
 | 
					            {options.label && `${options.label}, `}
 | 
				
			||||||
            {t("common.number", {
 | 
					            {t("common.number", {
 | 
				
			||||||
              value: data.current_weather.temperature,
 | 
					              value: data.current_weather.temperature,
 | 
				
			||||||
              style: "unit",
 | 
					              style: "unit",
 | 
				
			||||||
              unit,
 | 
					              unit,
 | 
				
			||||||
            })}
 | 
					            })}
 | 
				
			||||||
    </PrimaryText>
 | 
					          </span>
 | 
				
			||||||
    <SecondaryText>{t(`wmo.${data.current_weather.weathercode}-${weatherInfo.timeOfDay}`)}</SecondaryText>
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t(`wmo.${data.current_weather.weathercode}-${timeOfDay}`)}</span>
 | 
				
			||||||
    <WidgetIcon icon={Icon} size="xl" weatherInfo={weatherInfo} />
 | 
					        </div>
 | 
				
			||||||
  </Container>;
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function OpenMeteo({ options }) {
 | 
					export default function OpenMeteo({ options }) {
 | 
				
			||||||
@ -84,11 +103,27 @@ export default function OpenMeteo({ options }) {
 | 
				
			|||||||
  // if (!requesting && !location) requestLocation();
 | 
					  // if (!requesting && !location) requestLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!location) {
 | 
					  if (!location) {
 | 
				
			||||||
    return <ContainerButton options={options} callback={requestLocation} >
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("weather.current")}</PrimaryText>
 | 
					      <button
 | 
				
			||||||
      <SecondaryText>{t("weather.allow")}</SecondaryText>
 | 
					        type="button"
 | 
				
			||||||
      <WidgetIcon icon={ requesting ? MdLocationSearching : MdLocationDisabled} size="m" pulse />
 | 
					        onClick={() => requestLocation()}
 | 
				
			||||||
    </ContainerButton>;
 | 
					        className="flex flex-col justify-center first:ml-0 ml-4 mr-2"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					            {requesting ? (
 | 
				
			||||||
 | 
					              <MdLocationSearching className="w-6 h-6 text-theme-800 dark:text-theme-200 animate-pulse" />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <MdLocationDisabled className="w-6 h-6 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-sm">{t("weather.current")}</span>
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("weather.allow")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Widget options={{ ...location, ...options }} />;
 | 
					  return <Widget options={{ ...location, ...options }} />;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,12 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { WiCloudDown } from "react-icons/wi";
 | 
					import { WiCloudDown } from "react-icons/wi";
 | 
				
			||||||
import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
 | 
					import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import ContainerButton from "../widget/container_button";
 | 
					 | 
				
			||||||
import PrimaryText from "../widget/primary_text";
 | 
					 | 
				
			||||||
import SecondaryText from "../widget/secondary_text";
 | 
					 | 
				
			||||||
import WidgetIcon from "../widget/widget_icon";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Icon from "./icon";
 | 
					import Icon from "./icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
function Widget({ options }) {
 | 
					function Widget({ options }) {
 | 
				
			||||||
  const { t, i18n } = useTranslation();
 | 
					  const { t, i18n } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,30 +15,58 @@ function Widget({ options }) {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.cod === 401 || data?.error) {
 | 
					  if (error || data?.cod === 401 || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col justify-center first:ml-auto ml-4 mr-2">
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="hidden sm:flex flex-col items-center">
 | 
				
			||||||
 | 
					            <BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs">-</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("weather.updating")}</PrimaryText>
 | 
					      <div className="flex flex-col justify-center first:ml-auto ml-4 mr-2">
 | 
				
			||||||
      <SecondaryText>{t("weather.wait")}</SecondaryText>
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
      <WidgetIcon icon={WiCloudDown} size="l" />
 | 
					          <div className="hidden sm:flex flex-col items-center">
 | 
				
			||||||
    </Container>;
 | 
					            <WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-sm">{t("weather.updating")}</span>
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("weather.wait")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const unit = options.units === "metric" ? "celsius" : "fahrenheit";
 | 
					  const unit = options.units === "metric" ? "celsius" : "fahrenheit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const weatherInfo = {
 | 
					  return (
 | 
				
			||||||
    condition: data.weather[0].id,
 | 
					    <div className="flex flex-col justify-center first:ml-auto ml-2 mr-2">
 | 
				
			||||||
    timeOfDay: data.dt > data.sys.sunrise && data.dt < data.sys.sunset ? "day" : "night"
 | 
					      <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
  };
 | 
					        <div className="hidden sm:flex flex-col items-center">
 | 
				
			||||||
 | 
					          <Icon
 | 
				
			||||||
  return <Container options={options}>
 | 
					            condition={data.weather[0].id}
 | 
				
			||||||
    <PrimaryText>{options.label && `${options.label}, `}</PrimaryText>
 | 
					            timeOfDay={data.dt > data.sys.sunrise && data.dt < data.sys.sunset ? "day" : "night"}
 | 
				
			||||||
    <PrimaryText>{t("common.number", { value: data.main.temp, style: "unit", unit })}</PrimaryText>
 | 
					          />
 | 
				
			||||||
    <SecondaryText>{data.weather[0].description}</SecondaryText>
 | 
					        </div>
 | 
				
			||||||
    <WidgetIcon icon={Icon} size="xl" weatherInfo={weatherInfo} />
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
  </Container>;
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-sm">
 | 
				
			||||||
 | 
					            {options.label && `${options.label}, `}
 | 
				
			||||||
 | 
					            {t("common.number", { value: data.main.temp, style: "unit", unit })}
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{data.weather[0].description}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function OpenWeatherMap({ options }) {
 | 
					export default function OpenWeatherMap({ options }) {
 | 
				
			||||||
@ -77,12 +98,30 @@ export default function OpenWeatherMap({ options }) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // if (!requesting && !location) requestLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!location) {
 | 
					  if (!location) {
 | 
				
			||||||
    return <ContainerButton options={options} callback={requestLocation} >
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("weather.current")}</PrimaryText>
 | 
					      <button
 | 
				
			||||||
      <SecondaryText>{t("weather.allow")}</SecondaryText>
 | 
					        type="button"
 | 
				
			||||||
      <WidgetIcon icon={requesting ? MdLocationSearching : MdLocationDisabled} size="m" pulse />
 | 
					        onClick={() => requestLocation()}
 | 
				
			||||||
    </ContainerButton>;
 | 
					        className="flex flex-col justify-center first:ml-auto ml-4 mr-2"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="hidden sm:flex flex-col items-center">
 | 
				
			||||||
 | 
					            {requesting ? (
 | 
				
			||||||
 | 
					              <MdLocationSearching className="w-6 h-6 text-theme-800 dark:text-theme-200 animate-pulse" />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <MdLocationDisabled className="w-6 h-6 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-sm">{t("weather.current")}</span>
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("weather.allow")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Widget options={{ ...location, ...options }} />;
 | 
					  return <Widget options={{ ...location, ...options }} />;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { FiCpu } from "react-icons/fi";
 | 
					import { FiCpu } from "react-icons/fi";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					import UsageBar from "./usage-bar";
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Cpu({ expanded }) {
 | 
					export default function Cpu({ expanded }) {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
@ -13,29 +13,67 @@ export default function Cpu({ expanded }) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Resource icon={FiCpu} value="-" label={t("resources.cpu")} expandedValue="-"
 | 
					    return (
 | 
				
			||||||
                     expandedLabel={t("resources.load")} percentage="0" expanded={expanded} />
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
				
			||||||
 | 
					        <FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					          <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5 pr-1">-</div>
 | 
				
			||||||
 | 
					            <div className="pr-1">{t("resources.cpu")}</div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          {expanded && (
 | 
				
			||||||
 | 
					            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					              <div className="pl-0.5 pr-1">-</div>
 | 
				
			||||||
 | 
					              <div className="pr-1">{t("resources.load")}</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          <UsageBar percent={0} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Resource
 | 
					  const percent = data.cpu.usage;
 | 
				
			||||||
    icon={FiCpu}
 | 
					
 | 
				
			||||||
    value={t("common.number", {
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					      <FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					        <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					          <div className="pl-0.5 pr-1">
 | 
				
			||||||
 | 
					            {t("common.number", {
 | 
				
			||||||
              value: data.cpu.usage,
 | 
					              value: data.cpu.usage,
 | 
				
			||||||
              style: "unit",
 | 
					              style: "unit",
 | 
				
			||||||
              unit: "percent",
 | 
					              unit: "percent",
 | 
				
			||||||
              maximumFractionDigits: 0,
 | 
					              maximumFractionDigits: 0,
 | 
				
			||||||
            })}
 | 
					            })}
 | 
				
			||||||
    label={t("resources.cpu")}
 | 
					          </div>
 | 
				
			||||||
    expandedValue={t("common.number", {
 | 
					          <div className="pr-1">{t("resources.cpu")}</div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {expanded && (
 | 
				
			||||||
 | 
					          <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5 pr-1">
 | 
				
			||||||
 | 
					              {t("common.number", {
 | 
				
			||||||
                value: data.cpu.load,
 | 
					                value: data.cpu.load,
 | 
				
			||||||
                maximumFractionDigits: 2,
 | 
					                maximumFractionDigits: 2,
 | 
				
			||||||
              })}
 | 
					              })}
 | 
				
			||||||
    expandedLabel={t("resources.load")}
 | 
					            </div>
 | 
				
			||||||
    percentage={data.cpu.usage}
 | 
					            <div className="pr-1">{t("resources.load")}</div>
 | 
				
			||||||
    expanded={expanded}
 | 
					          </div>
 | 
				
			||||||
  />
 | 
					        )}
 | 
				
			||||||
 | 
					        <UsageBar percent={percent} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { FaThermometerHalf } from "react-icons/fa";
 | 
					import { FaThermometerHalf } from "react-icons/fa";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					import UsageBar from "./usage-bar";
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function convertToFahrenheit(t) {
 | 
					function convertToFahrenheit(t) {
 | 
				
			||||||
  return t * 9/5 + 32
 | 
					  return t * 9/5 + 32
 | 
				
			||||||
@ -17,18 +17,34 @@ export default function CpuTemp({ expanded, units }) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data || !data.cputemp) {
 | 
					  if (!data || !data.cputemp) {
 | 
				
			||||||
    return <Resource
 | 
					    return (
 | 
				
			||||||
      icon={FaThermometerHalf}
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
				
			||||||
      value="-"
 | 
					        <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
      label={t("resources.temp")}
 | 
					        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
      expandedValue="-"
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
      expandedLabel={t("resources.max")}
 | 
					            <div className="pl-0.5">-</div>
 | 
				
			||||||
      expanded={expanded}
 | 
					            <div className="pr-1">{t("resources.temp")}</div>
 | 
				
			||||||
    />;
 | 
					          </span>
 | 
				
			||||||
 | 
					          {expanded && (
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					              <div className="pl-0.5">-</div>
 | 
				
			||||||
 | 
					              <div className="pr-1">{t("resources.max")}</div>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let mainTemp = data.cputemp.main;
 | 
					  let mainTemp = data.cputemp.main;
 | 
				
			||||||
@ -38,24 +54,38 @@ export default function CpuTemp({ expanded, units }) {
 | 
				
			|||||||
  const unit = units === "imperial" ? "fahrenheit" : "celsius";
 | 
					  const unit = units === "imperial" ? "fahrenheit" : "celsius";
 | 
				
			||||||
  mainTemp = (unit === "celsius") ? mainTemp : convertToFahrenheit(mainTemp);
 | 
					  mainTemp = (unit === "celsius") ? mainTemp : convertToFahrenheit(mainTemp);
 | 
				
			||||||
  const maxTemp = (unit === "celsius") ? data.cputemp.max : convertToFahrenheit(data.cputemp.max);
 | 
					  const maxTemp = (unit === "celsius") ? data.cputemp.max : convertToFahrenheit(data.cputemp.max);
 | 
				
			||||||
 | 
					  const percent = Math.round((mainTemp / maxTemp) * 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return  <Resource
 | 
					  return (
 | 
				
			||||||
    icon={FaThermometerHalf}
 | 
					    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
    value={t("common.number", {
 | 
					      <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					          <div className="pl-0.5">
 | 
				
			||||||
 | 
					            {t("common.number", { 
 | 
				
			||||||
              value: mainTemp,
 | 
					              value: mainTemp,
 | 
				
			||||||
              maximumFractionDigits: 1,
 | 
					              maximumFractionDigits: 1,
 | 
				
			||||||
              style: "unit",
 | 
					              style: "unit",
 | 
				
			||||||
              unit
 | 
					              unit
 | 
				
			||||||
            })}
 | 
					            })}
 | 
				
			||||||
    label={t("resources.temp")}
 | 
					          </div>
 | 
				
			||||||
    expandedValue={t("common.number", {
 | 
					          <div className="pr-1">{t("resources.temp")}</div>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        {expanded && (
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5">
 | 
				
			||||||
 | 
					              {t("common.number", {
 | 
				
			||||||
                value: maxTemp,
 | 
					                value: maxTemp,
 | 
				
			||||||
                maximumFractionDigits: 1,
 | 
					                maximumFractionDigits: 1,
 | 
				
			||||||
                style: "unit",
 | 
					                style: "unit",
 | 
				
			||||||
                unit
 | 
					                unit
 | 
				
			||||||
              })}
 | 
					              })}
 | 
				
			||||||
    expandedLabel={t("resources.max")}
 | 
					            </div>
 | 
				
			||||||
    percentage={Math.round((mainTemp / maxTemp) * 100)}
 | 
					            <div className="pr-1">{t("resources.max")}</div>
 | 
				
			||||||
    expanded={expanded}
 | 
					          </span>
 | 
				
			||||||
  />;
 | 
					        )}
 | 
				
			||||||
 | 
					        <UsageBar percent={percent} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { FiHardDrive } from "react-icons/fi";
 | 
					import { FiHardDrive } from "react-icons/fi";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					import UsageBar from "./usage-bar";
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Disk({ options, expanded }) {
 | 
					export default function Disk({ options, expanded }) {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
@ -13,31 +13,56 @@ export default function Disk({ options, expanded }) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Resource
 | 
					    return (
 | 
				
			||||||
      icon={FiHardDrive}
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
				
			||||||
      value="-"
 | 
					        <FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
      label={t("resources.free")}
 | 
					        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
      expandedValue="-"
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
      expandedLabel={t("resources.total")}
 | 
					            <div className="pl-0.5 pr-1">-</div>
 | 
				
			||||||
      expanded={expanded}
 | 
					            <div className="pr-1">{t("resources.free")}</div>
 | 
				
			||||||
      percentage="0"
 | 
					          </span>
 | 
				
			||||||
    />;
 | 
					          {expanded && (
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					              <div className="pl-0.5 pr-1">-</div>
 | 
				
			||||||
 | 
					              <div className="pr-1">{t("resources.total")}</div>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          <UsageBar percent={0} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // data.drive.used not accurate?
 | 
					  // data.drive.used not accurate?
 | 
				
			||||||
  const percent = Math.round(((data.drive.size - data.drive.available) / data.drive.size) * 100);
 | 
					  const percent = Math.round(((data.drive.size - data.drive.available) / data.drive.size) * 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Resource
 | 
					  return (
 | 
				
			||||||
    icon={FiHardDrive}
 | 
					    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
    value={t("common.bytes", { value: data.drive.available })}
 | 
					      <FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
    label={t("resources.free")}
 | 
					      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
    expandedValue={t("common.bytes", { value: data.drive.size })}
 | 
					        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
    expandedLabel={t("resources.total")}
 | 
					          <div className="pl-0.5 pr-1">{t("common.bytes", { value: data.drive.available })}</div>
 | 
				
			||||||
    percentage={percent}
 | 
					          <div className="pr-1">{t("resources.free")}</div>
 | 
				
			||||||
    expanded={expanded}
 | 
					        </span>
 | 
				
			||||||
  />;
 | 
					        {expanded && (
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5 pr-1">{t("common.bytes", { value: data.drive.size })}</div>
 | 
				
			||||||
 | 
					            <div className="pr-1">{t("resources.total")}</div>
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        <UsageBar percent={percent} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { FaMemory } from "react-icons/fa";
 | 
					import { FaMemory } from "react-icons/fa";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					import UsageBar from "./usage-bar";
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Memory({ expanded }) {
 | 
					export default function Memory({ expanded }) {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
@ -13,30 +13,63 @@ export default function Memory({ expanded }) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Resource
 | 
					    return (
 | 
				
			||||||
      icon={FaMemory}
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
				
			||||||
      value="-"
 | 
					        <FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
      label={t("resources.free")}
 | 
					        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
      expandedValue="-"
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
      expandedLabel={t("resources.total")}
 | 
					            <div className="pl-0.5 pr-1">-</div>
 | 
				
			||||||
      expanded={expanded}
 | 
					            <div className="pr-1">{t("resources.free")}</div>
 | 
				
			||||||
      percentage="0"
 | 
					          </span>
 | 
				
			||||||
    />;
 | 
					          {expanded && (
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					              <div className="pl-0.5 pr-1">-</div>
 | 
				
			||||||
 | 
					              <div className="pr-1">{t("resources.total")}</div>
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          <UsageBar percent={0} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const percent = Math.round((data.memory.active / data.memory.total) * 100);
 | 
					  const percent = Math.round((data.memory.active / data.memory.total) * 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Resource
 | 
					  return (
 | 
				
			||||||
    icon={FaMemory}
 | 
					    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
    value={t("common.bytes", { value: data.memory.available, maximumFractionDigits: 1, binary: true })}
 | 
					      <FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
    label={t("resources.free")}
 | 
					      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
    expandedValue={t("common.bytes", { value: data.memory.total, maximumFractionDigits: 1, binary: true })}
 | 
					        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
    expandedLabel={t("resources.total")}
 | 
					          <div className="pl-0.5 pr-1">
 | 
				
			||||||
    percentage={percent}
 | 
					            {t("common.bytes", { value: data.memory.available, maximumFractionDigits: 1, binary: true })}
 | 
				
			||||||
    expanded={expanded}
 | 
					          </div>
 | 
				
			||||||
  />;
 | 
					          <div className="pr-1">{t("resources.free")}</div>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        {expanded && (
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5 pr-1">
 | 
				
			||||||
 | 
					              {t("common.bytes", {
 | 
				
			||||||
 | 
					                value: data.memory.total,
 | 
				
			||||||
 | 
					                maximumFractionDigits: 1,
 | 
				
			||||||
 | 
					                binary: true,
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="pr-1">{t("resources.total")}</div>
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        <UsageBar percent={percent} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,3 @@
 | 
				
			|||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Disk from "./disk";
 | 
					import Disk from "./disk";
 | 
				
			||||||
import Cpu from "./cpu";
 | 
					import Cpu from "./cpu";
 | 
				
			||||||
import Memory from "./memory";
 | 
					import Memory from "./memory";
 | 
				
			||||||
@ -9,8 +6,8 @@ import Uptime from "./uptime";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default function Resources({ options }) {
 | 
					export default function Resources({ options }) {
 | 
				
			||||||
  const { expanded, units } = options;
 | 
					  const { expanded, units } = options;
 | 
				
			||||||
  return <Container options={options}>
 | 
					  return (
 | 
				
			||||||
    <Raw>
 | 
					    <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
				
			||||||
      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
					      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
				
			||||||
        {options.cpu && <Cpu expanded={expanded} />}
 | 
					        {options.cpu && <Cpu expanded={expanded} />}
 | 
				
			||||||
        {options.memory && <Memory expanded={expanded} />}
 | 
					        {options.memory && <Memory expanded={expanded} />}
 | 
				
			||||||
@ -23,6 +20,6 @@ export default function Resources({ options }) {
 | 
				
			|||||||
      {options.label && (
 | 
					      {options.label && (
 | 
				
			||||||
        <div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
 | 
					        <div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
    </Raw>
 | 
					    </div>
 | 
				
			||||||
  </Container>;
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { FaRegClock } from "react-icons/fa";
 | 
					import { FaRegClock } from "react-icons/fa";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Resource from "../widget/resource";
 | 
					import UsageBar from "./usage-bar";
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Uptime() {
 | 
					export default function Uptime() {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
@ -13,11 +13,28 @@ export default function Uptime() {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Resource icon={FaRegClock} value="-" label={t("resources.uptime")} percentage="0" />;
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
				
			||||||
 | 
					        <FaRegClock className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					            <div className="pl-0.5">-</div>
 | 
				
			||||||
 | 
					            <div className="pr-1">{t("resources.temp")}</div>
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mo = Math.floor(data.uptime / (3600 * 24 * 31));
 | 
					  const mo = Math.floor(data.uptime / (3600 * 24 * 31));
 | 
				
			||||||
@ -30,7 +47,20 @@ export default function Uptime() {
 | 
				
			|||||||
  else if (d > 0) uptime = `${d}${t("resources.days")} ${h}${t("resources.hours")}`;
 | 
					  else if (d > 0) uptime = `${d}${t("resources.days")} ${h}${t("resources.hours")}`;
 | 
				
			||||||
  else uptime = `${h}${t("resources.hours")} ${m}${t("resources.minutes")}`;
 | 
					  else uptime = `${h}${t("resources.hours")} ${m}${t("resources.minutes")}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const percent = Math.round((new Date().getSeconds() / 60) * 100).toString();
 | 
					  const percent = Math.round((new Date().getSeconds() / 60) * 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Resource icon={FaRegClock} value={uptime} label={t("resources.uptime")} percentage={percent} />;
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
 | 
					      <FaRegClock className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
				
			||||||
 | 
					      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
				
			||||||
 | 
					        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
				
			||||||
 | 
					          <div className="pl-0.5">
 | 
				
			||||||
 | 
					            {uptime}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="pr-1">{t("resources.uptime")}</div>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <UsageBar percent={percent} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,10 @@
 | 
				
			|||||||
import { useState, useEffect, useCallback, Fragment } from "react";
 | 
					import { useState, useEffect, Fragment } from "react";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
import { FiSearch } from "react-icons/fi";
 | 
					import { FiSearch } from "react-icons/fi";
 | 
				
			||||||
import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si";
 | 
					import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si";
 | 
				
			||||||
import { Listbox, Transition } from "@headlessui/react";
 | 
					import { Listbox, Transition } from "@headlessui/react";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ContainerForm from "../widget/container_form";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const searchProviders = {
 | 
					export const searchProviders = {
 | 
				
			||||||
  google: {
 | 
					  google: {
 | 
				
			||||||
    name: "Google",
 | 
					    name: "Google",
 | 
				
			||||||
@ -80,8 +77,13 @@ export default function Search({ options }) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [availableProviderIds]);
 | 
					  }, [availableProviderIds]);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  const submitCallback = useCallback(event =>  {
 | 
					  if (!availableProviderIds) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function handleSubmit(event) {
 | 
				
			||||||
    const q = encodeURIComponent(query);
 | 
					    const q = encodeURIComponent(query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { url } = selectedProvider;
 | 
					    const { url } = selectedProvider;
 | 
				
			||||||
    if (url) {
 | 
					    if (url) {
 | 
				
			||||||
      window.open(`${url}${q}`, options.target || "_blank");
 | 
					      window.open(`${url}${q}`, options.target || "_blank");
 | 
				
			||||||
@ -92,10 +94,6 @@ export default function Search({ options }) {
 | 
				
			|||||||
    event.preventDefault();
 | 
					    event.preventDefault();
 | 
				
			||||||
    event.target.reset();
 | 
					    event.target.reset();
 | 
				
			||||||
    setQuery("");
 | 
					    setQuery("");
 | 
				
			||||||
  }, [options.target, options.url, query, selectedProvider]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!availableProviderIds) {
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onChangeProvider = (provider) => {
 | 
					  const onChangeProvider = (provider) => {
 | 
				
			||||||
@ -103,9 +101,8 @@ export default function Search({ options }) {
 | 
				
			|||||||
    localStorage.setItem(localStorageKey, provider.name);
 | 
					    localStorage.setItem(localStorageKey, provider.name);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <ContainerForm options={options} callback={submitCallback} additionalClassNames="grow" >
 | 
					  return (
 | 
				
			||||||
    <Raw>
 | 
					    <form className="flex-col relative h-8 my-4 min-w-fit grow first:ml-0 ml-4" onSubmit={handleSubmit}>
 | 
				
			||||||
      <div className="flex-col relative h-8 my-4 min-w-fit">
 | 
					 | 
				
			||||||
      <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-white" />
 | 
					      <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-white" />
 | 
				
			||||||
      <input
 | 
					      <input
 | 
				
			||||||
        type="text"
 | 
					        type="text"
 | 
				
			||||||
@ -175,7 +172,6 @@ export default function Search({ options }) {
 | 
				
			|||||||
          </Listbox.Options>
 | 
					          </Listbox.Options>
 | 
				
			||||||
        </Transition>
 | 
					        </Transition>
 | 
				
			||||||
      </Listbox>
 | 
					      </Listbox>
 | 
				
			||||||
      </div>
 | 
					    </form>
 | 
				
			||||||
    </Raw>
 | 
					  );
 | 
				
			||||||
  </ContainerForm>;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,12 +3,6 @@ import { MdSettingsEthernet } from "react-icons/md";
 | 
				
			|||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
import { SiUbiquiti } from "react-icons/si";
 | 
					import { SiUbiquiti } from "react-icons/si";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import Raw from "../widget/raw";
 | 
					 | 
				
			||||||
import WidgetIcon from "../widget/widget_icon";
 | 
					 | 
				
			||||||
import PrimaryText from "../widget/primary_text";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
					import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Widget({ options }) {
 | 
					export default function Widget({ options }) {
 | 
				
			||||||
@ -19,16 +13,35 @@ export default function Widget({ options }) {
 | 
				
			|||||||
  const { data: statsData, error: statsError } = useWidgetAPI(options, "stat/sites", { index: options.index });
 | 
					  const { data: statsData, error: statsError } = useWidgetAPI(options, "stat/sites", { index: options.index });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (statsError) {
 | 
					  if (statsError) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4">
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					            <BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const defaultSite = options.site ? statsData?.data.find(s => s.desc === options.site) : statsData?.data?.find(s => s.name === "default");
 | 
					  const defaultSite = options.site ? statsData?.data.find(s => s.desc === options.site) : statsData?.data?.find(s => s.name === "default");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!defaultSite) {
 | 
					  if (!defaultSite) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("unifi.wait")}</PrimaryText>
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4">
 | 
				
			||||||
      <WidgetIcon icon={SiUbiquiti} />
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
    </Container>;
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					            <SiUbiquiti className="w-5 h-5 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("unifi.wait")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const wan = defaultSite.health.find(h => h.subsystem === "wan");
 | 
					  const wan = defaultSite.health.find(h => h.subsystem === "wan");
 | 
				
			||||||
@ -43,8 +56,7 @@ export default function Widget({ options }) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const dataEmpty = !(wan.show || lan.show || wlan.show || uptime);
 | 
					  const dataEmpty = !(wan.show || lan.show || wlan.show || uptime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Container options={options}>
 | 
					  return (
 | 
				
			||||||
    <Raw>
 | 
					 | 
				
			||||||
    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
					    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
				
			||||||
      <div className="flex flex-col">
 | 
					      <div className="flex flex-col">
 | 
				
			||||||
        <div className="flex flex-row ml-3 mb-0.5">
 | 
					        <div className="flex flex-row ml-3 mb-0.5">
 | 
				
			||||||
@ -130,6 +142,5 @@ export default function Widget({ options }) {
 | 
				
			|||||||
        </div>}
 | 
					        </div>}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    </Raw>
 | 
					  );
 | 
				
			||||||
  </Container>
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,10 @@
 | 
				
			|||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					import { BiError } from "react-icons/bi";
 | 
				
			||||||
import { WiCloudDown } from "react-icons/wi";
 | 
					import { WiCloudDown } from "react-icons/wi";
 | 
				
			||||||
import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
 | 
					import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Error from "../widget/error";
 | 
					 | 
				
			||||||
import Container from "../widget/container";
 | 
					 | 
				
			||||||
import PrimaryText from "../widget/primary_text";
 | 
					 | 
				
			||||||
import SecondaryText from "../widget/secondary_text";
 | 
					 | 
				
			||||||
import WidgetIcon from "../widget/widget_icon";
 | 
					 | 
				
			||||||
import ContainerButton from "../widget/container_button";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Icon from "./icon";
 | 
					import Icon from "./icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Widget({ options }) {
 | 
					function Widget({ options }) {
 | 
				
			||||||
@ -21,35 +15,59 @@ function Widget({ options }) {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (error || data?.error) {
 | 
					  if (error || data?.error) {
 | 
				
			||||||
    return <Error options={options} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2">
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					            <BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
 | 
				
			||||||
 | 
					              <span className="text-theme-800 dark:text-theme-200 text-xs">-</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!data) {
 | 
					  if (!data) {
 | 
				
			||||||
    return <Container options={options}>
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("weather.updating")}</PrimaryText>
 | 
					      <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2">
 | 
				
			||||||
      <SecondaryText>{t("weather.wait")}</SecondaryText>
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
      <WidgetIcon icon={WiCloudDown} size="l" />
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
    </Container>;
 | 
					            <WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-sm">{t("weather.updating")}</span>
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("weather.wait")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const unit = options.units === "metric" ? "celsius" : "fahrenheit";
 | 
					  const unit = options.units === "metric" ? "celsius" : "fahrenheit";
 | 
				
			||||||
  const weatherInfo = {
 | 
					 | 
				
			||||||
    condition: data.current.condition.code,
 | 
					 | 
				
			||||||
    timeOfDay: data.current.is_day ? "day" : "night",
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Container options={options}>
 | 
					  return (
 | 
				
			||||||
    <PrimaryText>
 | 
					    <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2">
 | 
				
			||||||
 | 
					      <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					        <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					          <Icon condition={data.current.condition.code} timeOfDay={data.current.is_day ? "day" : "night"} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-sm">
 | 
				
			||||||
            {options.label && `${options.label}, `}
 | 
					            {options.label && `${options.label}, `}
 | 
				
			||||||
            {t("common.number", {
 | 
					            {t("common.number", {
 | 
				
			||||||
              value: options.units === "metric" ? data.current.temp_c : data.current.temp_f,
 | 
					              value: options.units === "metric" ? data.current.temp_c : data.current.temp_f,
 | 
				
			||||||
              style: "unit",
 | 
					              style: "unit",
 | 
				
			||||||
              unit,
 | 
					              unit,
 | 
				
			||||||
            })}
 | 
					            })}
 | 
				
			||||||
    </PrimaryText>
 | 
					          </span>
 | 
				
			||||||
    <SecondaryText>{data.current.condition.text}</SecondaryText>
 | 
					          <span className="text-theme-800 dark:text-theme-200 text-xs">{data.current.condition.text}</span>
 | 
				
			||||||
    <WidgetIcon icon={Icon} size="xl" weatherInfo={weatherInfo} />
 | 
					        </div>
 | 
				
			||||||
  </Container>;
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function WeatherApi({ options }) {
 | 
					export default function WeatherApi({ options }) {
 | 
				
			||||||
@ -81,12 +99,30 @@ export default function WeatherApi({ options }) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // if (!requesting && !location) requestLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!location) {
 | 
					  if (!location) {
 | 
				
			||||||
    return <ContainerButton options={options} callback={requestLocation} >
 | 
					    return (
 | 
				
			||||||
      <PrimaryText>{t("weather.current")}</PrimaryText>
 | 
					      <button
 | 
				
			||||||
      <SecondaryText>{t("weather.allow")}</SecondaryText>
 | 
					        type="button"
 | 
				
			||||||
      <WidgetIcon icon={requesting ? MdLocationSearching : MdLocationDisabled} size="m" pulse />
 | 
					        onClick={() => requestLocation()}
 | 
				
			||||||
    </ContainerButton>;
 | 
					        className="flex flex-col justify-center first:ml-0 ml-4 mr-2"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <div className="flex flex-row items-center justify-end">
 | 
				
			||||||
 | 
					          <div className="flex flex-col items-center">
 | 
				
			||||||
 | 
					            {requesting ? (
 | 
				
			||||||
 | 
					              <MdLocationSearching className="w-6 h-6 text-theme-800 dark:text-theme-200 animate-pulse" />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <MdLocationDisabled className="w-6 h-6 text-theme-800 dark:text-theme-200" />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="flex flex-col ml-3 text-left">
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-sm">{t("weather.current")}</span>
 | 
				
			||||||
 | 
					            <span className="text-theme-800 dark:text-theme-200 text-xs">{t("weather.allow")}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Widget options={{ ...location, ...options }} />;
 | 
					  return <Widget options={{ ...location, ...options }} />;
 | 
				
			||||||
 | 
				
			|||||||
@ -17,13 +17,13 @@ const widgetMappings = {
 | 
				
			|||||||
  kubernetes: dynamic(() => import("components/widgets/kubernetes/kubernetes")),
 | 
					  kubernetes: dynamic(() => import("components/widgets/kubernetes/kubernetes")),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Widget({ widget, style }) {
 | 
					export default function Widget({ widget }) {
 | 
				
			||||||
  const InfoWidget = widgetMappings[widget.type];
 | 
					  const InfoWidget = widgetMappings[widget.type];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (InfoWidget) {
 | 
					  if (InfoWidget) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <ErrorBoundary>
 | 
					      <ErrorBoundary>
 | 
				
			||||||
        <InfoWidget options={{ ...widget.options, style }} />
 | 
					        <InfoWidget options={widget.options} />
 | 
				
			||||||
      </ErrorBoundary>
 | 
					      </ErrorBoundary>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,42 +0,0 @@
 | 
				
			|||||||
import classNames from "classnames";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import WidgetIcon from "./widget_icon";
 | 
					 | 
				
			||||||
import PrimaryText from "./primary_text";
 | 
					 | 
				
			||||||
import SecondaryText from "./secondary_text";
 | 
					 | 
				
			||||||
import Raw from "./raw";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getAllClasses(options, additionalClassNames = '') {
 | 
					 | 
				
			||||||
  return classNames(
 | 
					 | 
				
			||||||
    "flex flex-col justify-center first:ml-0 ml-4 mr-2",
 | 
					 | 
				
			||||||
    additionalClassNames,
 | 
					 | 
				
			||||||
    options?.style === "boxedWidgets" && " ml-4 mt-2 m:mb-0 rounded-md shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 dark:bg-white/5 p-2 pl-3",
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getInnerBlock(children) {
 | 
					 | 
				
			||||||
  // children won't be an array if it's Raw component
 | 
					 | 
				
			||||||
  return Array.isArray(children) && <div className="flex flex-row items-center justify-end">
 | 
					 | 
				
			||||||
    <div className="flex flex-col items-center">{children.find(child => child.type === WidgetIcon)}</div>
 | 
					 | 
				
			||||||
    <div className="flex flex-col ml-3 text-left">
 | 
					 | 
				
			||||||
      <span className="text-theme-800 dark:text-theme-200 text-sm">{children.find(child => child.type === PrimaryText)}</span>
 | 
					 | 
				
			||||||
      <span className="text-theme-800 dark:text-theme-200 text-xs">{children.find(child => child.type === SecondaryText)}</span>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getBottomBlock(children) {
 | 
					 | 
				
			||||||
  if (children.type !== Raw) {
 | 
					 | 
				
			||||||
    return children.find(child => child.type === Raw) || [];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return [children];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Container({ children = [], options, additionalClassNames = '' }) {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div className={getAllClasses(options, additionalClassNames)}>
 | 
					 | 
				
			||||||
      {getInnerBlock(children)}
 | 
					 | 
				
			||||||
      {getBottomBlock(children)}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,10 +0,0 @@
 | 
				
			|||||||
import { getAllClasses, getInnerBlock, getBottomBlock } from "./container";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function ContainerButton ({ children = [], options, additionalClassNames = '', callback }) {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <button type="button" onClick={callback} className={getAllClasses(options, additionalClassNames)}>
 | 
					 | 
				
			||||||
      {getInnerBlock(children)}
 | 
					 | 
				
			||||||
      {getBottomBlock(children)}
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,10 +0,0 @@
 | 
				
			|||||||
import { getAllClasses, getInnerBlock, getBottomBlock } from "./container";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function ContainerForm ({ children = [], options, additionalClassNames = '', callback }) {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <form type="button" onSubmit={callback} className={getAllClasses(options, additionalClassNames)}>
 | 
					 | 
				
			||||||
      {getInnerBlock(children)}
 | 
					 | 
				
			||||||
      {getBottomBlock(children)}
 | 
					 | 
				
			||||||
    </form>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,10 +0,0 @@
 | 
				
			|||||||
import { getAllClasses, getInnerBlock, getBottomBlock } from "./container";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function ContainerLink ({ children = [], options, additionalClassNames = '', target }) {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <a href={options.url} target={target} className={getAllClasses(options, additionalClassNames)}>
 | 
					 | 
				
			||||||
      {getInnerBlock(children)}
 | 
					 | 
				
			||||||
      {getBottomBlock(children)}
 | 
					 | 
				
			||||||
    </a>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
import { useTranslation } from "react-i18next";
 | 
					 | 
				
			||||||
import { BiError } from "react-icons/bi";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Container from "./container";
 | 
					 | 
				
			||||||
import PrimaryText from "./primary_text";
 | 
					 | 
				
			||||||
import WidgetIcon from "./widget_icon";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Error({ options }) {
 | 
					 | 
				
			||||||
  const { t } = useTranslation();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return <Container options={options}>
 | 
					 | 
				
			||||||
    <PrimaryText>{t("widget.api_error")}</PrimaryText>
 | 
					 | 
				
			||||||
    <WidgetIcon icon={BiError} size="l" />
 | 
					 | 
				
			||||||
  </Container>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,5 +0,0 @@
 | 
				
			|||||||
export default function PrimaryText({ children }) {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <span className="text-theme-800 dark:text-theme-200 text-sm">{children}</span>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,7 +0,0 @@
 | 
				
			|||||||
export default function Raw({ children }) {
 | 
					 | 
				
			||||||
  if (children.type === Raw) {
 | 
					 | 
				
			||||||
      return [children];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return children;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,22 +0,0 @@
 | 
				
			|||||||
import UsageBar from "../resources/usage-bar";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Resource({ children, icon, value, label, expandedValue, expandedLabel, percentage, key, expanded = false }) {
 | 
					 | 
				
			||||||
  const Icon = icon;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return <div key={key} className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
					 | 
				
			||||||
    <Icon className="text-theme-800 dark:text-theme-200 w-5 h-5"/>
 | 
					 | 
				
			||||||
    <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
					 | 
				
			||||||
      <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
					 | 
				
			||||||
        <div className="pl-0.5">{value}</div>
 | 
					 | 
				
			||||||
        <div className="pr-1">{label}</div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      { expanded && <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
					 | 
				
			||||||
          <div className="pl-0.5">{expandedValue}</div>
 | 
					 | 
				
			||||||
          <div className="pr-1">{expandedLabel}</div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      { percentage && <UsageBar percent={percentage} /> }
 | 
					 | 
				
			||||||
      { children }
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
import ContainerLink from "./container_link";
 | 
					 | 
				
			||||||
import Resource from "./resource";
 | 
					 | 
				
			||||||
import Raw from "./raw";
 | 
					 | 
				
			||||||
import WidgetLabel from "./widget_label";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Resources({ options, children, target }) {
 | 
					 | 
				
			||||||
  return <ContainerLink options={options} target={target}>
 | 
					 | 
				
			||||||
    <Raw>
 | 
					 | 
				
			||||||
      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
					 | 
				
			||||||
        { children.filter(child => child && child.type === Resource) }
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      { children.filter(child => child && child.type === WidgetLabel) }
 | 
					 | 
				
			||||||
    </Raw>
 | 
					 | 
				
			||||||
  </ContainerLink>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,5 +0,0 @@
 | 
				
			|||||||
export default function SecondaryText({ children }) {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <span className="text-theme-800 dark:text-theme-200 text-xs">{children}</span>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,18 +0,0 @@
 | 
				
			|||||||
export default function WidgetIcon({ icon, size = "s", pulse = false, weatherInfo = {} }) {
 | 
					 | 
				
			||||||
  const Icon = icon;
 | 
					 | 
				
			||||||
  const { condition, timeOfDay } = weatherInfo;
 | 
					 | 
				
			||||||
  let additionalClasses = "text-theme-800 dark:text-theme-200 ";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  switch (size) {
 | 
					 | 
				
			||||||
    case "m": additionalClasses += "w-6 h-6 "; break;
 | 
					 | 
				
			||||||
    case "l": additionalClasses += "w-8 h-8 "; break;
 | 
					 | 
				
			||||||
    case "xl": additionalClasses += "w-10 h-10 "; break;
 | 
					 | 
				
			||||||
    default: additionalClasses += "w-5 h-5 ";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (pulse) {
 | 
					 | 
				
			||||||
    additionalClasses += "animate-pulse ";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return <Icon className={additionalClasses} condition={condition} timeOfDay={timeOfDay} />;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,3 +0,0 @@
 | 
				
			|||||||
export default function WidgetLabel({ label = "" }) {
 | 
					 | 
				
			||||||
  return <div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{label}</div>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -46,7 +46,7 @@ function parseLonghornData(data) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default async function handler(req, res) {
 | 
					export default async function handler(req, res) {
 | 
				
			||||||
  const settings = getSettings();
 | 
					  const settings = getSettings();
 | 
				
			||||||
  const longhornSettings = settings?.providers?.longhorn || {};
 | 
					  const longhornSettings = settings?.providers?.longhorn;
 | 
				
			||||||
  const {url, username, password} = longhornSettings;
 | 
					  const {url, username, password} = longhornSettings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!url) {
 | 
					  if (!url) {
 | 
				
			||||||
 | 
				
			|||||||
@ -160,7 +160,6 @@ const headerStyles = {
 | 
				
			|||||||
    "m-4 mb-0 sm:m-8 sm:mb-0 rounded-md shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 dark:bg-white/5 p-3",
 | 
					    "m-4 mb-0 sm:m-8 sm:mb-0 rounded-md shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 dark:bg-white/5 p-3",
 | 
				
			||||||
  underlined: "m-4 mb-0 sm:m-8 sm:mb-1 border-b-2 pb-4 border-theme-800 dark:border-theme-200/50",
 | 
					  underlined: "m-4 mb-0 sm:m-8 sm:mb-1 border-b-2 pb-4 border-theme-800 dark:border-theme-200/50",
 | 
				
			||||||
  clean: "m-4 mb-0 sm:m-8 sm:mb-0",
 | 
					  clean: "m-4 mb-0 sm:m-8 sm:mb-0",
 | 
				
			||||||
  boxedWidgets: "m-4 mb-0 sm:m-8 sm:mb-0 sm:mt-1",
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Home({ initialSettings }) {
 | 
					function Home({ initialSettings }) {
 | 
				
			||||||
@ -209,7 +208,6 @@ function Home({ initialSettings }) {
 | 
				
			|||||||
      searchProvider = searchProviders[searchWidget.options?.provider];
 | 
					      searchProvider = searchProviders[searchWidget.options?.provider];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const headerStyle = initialSettings?.headerStyle || "underlined";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    function handleKeyDown(e) {
 | 
					    function handleKeyDown(e) {
 | 
				
			||||||
@ -258,7 +256,7 @@ function Home({ initialSettings }) {
 | 
				
			|||||||
        <div
 | 
					        <div
 | 
				
			||||||
          className={classNames(
 | 
					          className={classNames(
 | 
				
			||||||
            "flex flex-row flex-wrap  justify-between",
 | 
					            "flex flex-row flex-wrap  justify-between",
 | 
				
			||||||
            headerStyles[headerStyle]
 | 
					            headerStyles[initialSettings.headerStyle || "underlined"]
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <QuickLaunch
 | 
					          <QuickLaunch
 | 
				
			||||||
@ -274,14 +272,14 @@ function Home({ initialSettings }) {
 | 
				
			|||||||
              {widgets
 | 
					              {widgets
 | 
				
			||||||
                .filter((widget) => !rightAlignedWidgets.includes(widget.type))
 | 
					                .filter((widget) => !rightAlignedWidgets.includes(widget.type))
 | 
				
			||||||
                .map((widget, i) => (
 | 
					                .map((widget, i) => (
 | 
				
			||||||
                  <Widget key={i} widget={widget} style={headerStyle} />
 | 
					                  <Widget key={i} widget={widget} />
 | 
				
			||||||
                ))}
 | 
					                ))}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <div className="m-auto sm:ml-4 flex flex-wrap grow sm:basis-auto justify-between md:justify-end">
 | 
					              <div className="m-auto sm:ml-2 flex flex-wrap grow sm:basis-auto justify-between md:justify-end">
 | 
				
			||||||
                {widgets
 | 
					                {widgets
 | 
				
			||||||
                  .filter((widget) => rightAlignedWidgets.includes(widget.type))
 | 
					                  .filter((widget) => rightAlignedWidgets.includes(widget.type))
 | 
				
			||||||
                  .map((widget, i) => (
 | 
					                  .map((widget, i) => (
 | 
				
			||||||
                    <Widget key={i} widget={widget} style={headerStyle} />
 | 
					                    <Widget key={i} widget={widget} />
 | 
				
			||||||
                  ))}
 | 
					                  ))}
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
            </>
 | 
					            </>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user