From c79d45f91e775b0d2eed070263021d0cffe695c9 Mon Sep 17 00:00:00 2001 From: Denis Papec <denis.papec@gmail.com> Date: Sun, 16 Apr 2023 00:05:50 +0100 Subject: [PATCH] Add optional boxed styling and error component to information widgets Signed-off-by: Denis Papec <denis.papec@gmail.com> --- src/components/widgets/datetime/datetime.jsx | 8 +++-- src/components/widgets/error.jsx | 23 +++++++++++++ src/components/widgets/glances/glances.jsx | 30 +++++++--------- src/components/widgets/greeting/greeting.jsx | 7 +++- .../widgets/kubernetes/kubernetes.jsx | 29 +++++++--------- src/components/widgets/logo/logo.jsx | 7 +++- src/components/widgets/longhorn/longhorn.jsx | 25 +++++++------- .../widgets/openmeteo/openmeteo.jsx | 34 +++++++++---------- .../widgets/openweathermap/weather.jsx | 33 +++++++++--------- .../widgets/resources/resources.jsx | 7 +++- src/components/widgets/search/search.jsx | 11 +++--- .../widgets/unifi_console/unifi_console.jsx | 26 +++++++------- src/components/widgets/weather/weather.jsx | 33 +++++++++--------- src/pages/api/widgets/longhorn.js | 2 +- 14 files changed, 153 insertions(+), 122 deletions(-) create mode 100644 src/components/widgets/error.jsx diff --git a/src/components/widgets/datetime/datetime.jsx b/src/components/widgets/datetime/datetime.jsx index 86983473..fc883ec3 100644 --- a/src/components/widgets/datetime/datetime.jsx +++ b/src/components/widgets/datetime/datetime.jsx @@ -1,5 +1,6 @@ import { useState, useEffect } from "react"; import { useTranslation } from "next-i18next"; +import classNames from "classnames"; const textSizes = { "4xl": "text-4xl", @@ -17,7 +18,7 @@ export default function DateTime({ options }) { const { i18n } = useTranslation(); const [date, setDate] = useState(""); const dateLocale = locale ?? i18n.language; - + useEffect(() => { const dateFormat = new Intl.DateTimeFormat(dateLocale, { ...format }); const interval = setInterval(() => { @@ -27,7 +28,10 @@ export default function DateTime({ options }) { }, [date, setDate, dateLocale, format]); return ( - <div className="flex flex-col justify-center first:ml-0 ml-4"> + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center grow justify-end"> <span className={`text-theme-800 dark:text-theme-200 tabular-nums ${textSizes[textSize || "lg"]}`}> {date} diff --git a/src/components/widgets/error.jsx b/src/components/widgets/error.jsx new file mode 100644 index 00000000..92e0076a --- /dev/null +++ b/src/components/widgets/error.jsx @@ -0,0 +1,23 @@ +import { useTranslation } from "react-i18next"; +import { BiError } from "react-icons/bi"; +import classNames from "classnames"; + +export default function Error({ options }) { + const { t } = useTranslation(); + + return ( + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> + <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> + ); +} diff --git a/src/components/widgets/glances/glances.jsx b/src/components/widgets/glances/glances.jsx index 85dd44c0..b6daba7b 100644 --- a/src/components/widgets/glances/glances.jsx +++ b/src/components/widgets/glances/glances.jsx @@ -1,11 +1,12 @@ import useSWR from "swr"; import { useContext } from "react"; -import { BiError } from "react-icons/bi"; import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa"; import { FiCpu, FiHardDrive } from "react-icons/fi"; import { useTranslation } from "next-i18next"; +import classNames from "classnames"; import UsageBar from "../resources/usage-bar"; +import Error from "../error"; import { SettingsContext } from "utils/contexts/settings"; @@ -26,23 +27,15 @@ export default function Widget({ options }) { ); if (error || data?.error) { - 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> - ); + return <Error options={options} /> } if (!data) { return ( - <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4"> + <div className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4", + options?.styleBoxed === true && " mb-0 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", + )}> <div className="flex flex-row self-center flex-wrap justify-between"> <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" /> @@ -101,7 +94,10 @@ export default function Widget({ options }) { } return ( - <a href={options.url} target={settings.target ?? "_blank"} className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap"> + <a href={options.url} target={settings.target ?? "_blank"} className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap", + options?.styleBoxed === true && " mb-0 mt-2 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", + )}> <div className="flex flex-row self-center flex-wrap justify-between"> <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" /> @@ -184,7 +180,7 @@ export default function Widget({ options }) { <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", { + {t("common.number", { value: mainTemp, maximumFractionDigits: 1, style: "unit", @@ -196,7 +192,7 @@ export default function Widget({ options }) { {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", { + {t("common.number", { value: maxTemp, maximumFractionDigits: 1, style: "unit", diff --git a/src/components/widgets/greeting/greeting.jsx b/src/components/widgets/greeting/greeting.jsx index da0f063d..2e129560 100644 --- a/src/components/widgets/greeting/greeting.jsx +++ b/src/components/widgets/greeting/greeting.jsx @@ -1,3 +1,5 @@ +import classNames from "classnames"; + const textSizes = { "4xl": "text-4xl", "3xl": "text-3xl", @@ -12,7 +14,10 @@ const textSizes = { export default function Greeting({ options }) { if (options.text) { return ( - <div className="flex flex-row items-center justify-start"> + <div className={classNames( + "flex flex-row items-center justify-start", + options?.styleBoxed === true && " 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-3", + )}> <span className={`text-theme-800 dark:text-theme-200 mr-3 ${textSizes[options.text_size || "xl"]}`}> {options.text} </span> diff --git a/src/components/widgets/kubernetes/kubernetes.jsx b/src/components/widgets/kubernetes/kubernetes.jsx index 78c4caaf..514993da 100644 --- a/src/components/widgets/kubernetes/kubernetes.jsx +++ b/src/components/widgets/kubernetes/kubernetes.jsx @@ -1,12 +1,14 @@ import useSWR from "swr"; -import { BiError } from "react-icons/bi"; import { useTranslation } from "next-i18next"; +import classNames from "classnames"; + +import Error from "../error"; import Node from "./node"; export default function Widget({ options }) { const { cluster, nodes } = options; - const { t, i18n } = useTranslation(); + const { i18n } = useTranslation(); const defaultData = { cpu: { @@ -29,23 +31,15 @@ export default function Widget({ options }) { ); if (error || data?.error) { - 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> - ); + return <Error options={options} /> } if (!data) { return ( - <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap"> + <div className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row self-center flex-wrap justify-between"> {cluster.show && <Node type="cluster" key="cluster" options={options.cluster} data={defaultData} /> @@ -59,7 +53,10 @@ export default function Widget({ options }) { } return ( - <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap"> + <div className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row self-center flex-wrap justify-between"> {cluster.show && <Node key="cluster" type="cluster" options={options.cluster} data={data.cluster} /> diff --git a/src/components/widgets/logo/logo.jsx b/src/components/widgets/logo/logo.jsx index 96e8569f..6cba17bf 100644 --- a/src/components/widgets/logo/logo.jsx +++ b/src/components/widgets/logo/logo.jsx @@ -1,8 +1,13 @@ +import classNames from "classnames"; + import ResolvedIcon from "components/resolvedicon" export default function Logo({ options }) { return ( - <div className="w-12 h-12 flex flex-row items-center align-middle mr-3 self-center"> + <div className={classNames( + "w-12 h-12 flex flex-row items-center align-middle mr-3 self-center", + options?.styleBoxed === true && " 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-3", + )}> {options.icon ? <ResolvedIcon icon={options.icon} width={48} height={48} /> : // fallback to homepage logo diff --git a/src/components/widgets/longhorn/longhorn.jsx b/src/components/widgets/longhorn/longhorn.jsx index 9fcb21b4..5139f00a 100644 --- a/src/components/widgets/longhorn/longhorn.jsx +++ b/src/components/widgets/longhorn/longhorn.jsx @@ -1,37 +1,36 @@ import useSWR from "swr"; -import { BiError } from "react-icons/bi"; -import { useTranslation } from "next-i18next"; +import classNames from "classnames"; + +import Error from "../error"; import Node from "./node"; export default function Longhorn({ options }) { const { expanded, total, labels, include, nodes } = options; - const { t } = useTranslation(); const { data, error } = useSWR(`/api/widgets/longhorn`, { refreshInterval: 1500 }); if (error || data?.error) { - 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> - ); + return <Error options={options} /> } if (!data) { return ( - <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap"> + <div className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row self-center flex-wrap justify-between" /> </div> ); } return ( - <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap"> + <div className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row self-center flex-wrap justify-between"> {data.nodes .filter((node) => { diff --git a/src/components/widgets/openmeteo/openmeteo.jsx b/src/components/widgets/openmeteo/openmeteo.jsx index 0d29aef5..1381cc55 100644 --- a/src/components/widgets/openmeteo/openmeteo.jsx +++ b/src/components/widgets/openmeteo/openmeteo.jsx @@ -1,9 +1,11 @@ import useSWR from "swr"; import { useState } from "react"; -import { BiError } from "react-icons/bi"; import { WiCloudDown } from "react-icons/wi"; import { MdLocationDisabled, MdLocationSearching } from "react-icons/md"; import { useTranslation } from "next-i18next"; +import classNames from "classnames"; + +import Error from "../error"; import Icon from "./icon"; @@ -15,24 +17,15 @@ function Widget({ options }) { ); if (error || data?.error) { - 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> - ); + return <Error options={options} /> } if (!data) { return ( - <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2"> + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="flex flex-col items-center"> <WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" /> @@ -50,7 +43,10 @@ function Widget({ options }) { const timeOfDay = data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night"; return ( - <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2"> + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="flex flex-col items-center"> <Icon condition={data.current_weather.weathercode} timeOfDay={timeOfDay} /> @@ -107,8 +103,10 @@ export default function OpenMeteo({ options }) { <button type="button" onClick={() => requestLocation()} - className="flex flex-col justify-center first:ml-0 ml-4 mr-2" - > + className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="flex flex-col items-center"> {requesting ? ( diff --git a/src/components/widgets/openweathermap/weather.jsx b/src/components/widgets/openweathermap/weather.jsx index 49f428a0..b404039f 100644 --- a/src/components/widgets/openweathermap/weather.jsx +++ b/src/components/widgets/openweathermap/weather.jsx @@ -1,9 +1,11 @@ import useSWR from "swr"; import { useState } from "react"; -import { BiError } from "react-icons/bi"; import { WiCloudDown } from "react-icons/wi"; import { MdLocationDisabled, MdLocationSearching } from "react-icons/md"; import { useTranslation } from "next-i18next"; +import classNames from "classnames"; + +import Error from "../error"; import Icon from "./icon"; @@ -15,24 +17,15 @@ function Widget({ options }) { ); if (error || data?.cod === 401 || data?.error) { - 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> - ); + return <Error options={options} /> } if (!data) { return ( - <div className="flex flex-col justify-center first:ml-auto ml-4 mr-2"> + <div className={classNames( + "flex flex-col justify-center first:ml-auto ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="hidden sm:flex flex-col items-center"> <WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" /> @@ -49,7 +42,10 @@ function Widget({ options }) { const unit = options.units === "metric" ? "celsius" : "fahrenheit"; return ( - <div className="flex flex-col justify-center first:ml-auto ml-2 mr-2"> + <div className={classNames( + "flex flex-col justify-center first:ml-auto ml-2 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="hidden sm:flex flex-col items-center"> <Icon @@ -105,7 +101,10 @@ export default function OpenWeatherMap({ options }) { <button type="button" onClick={() => requestLocation()} - className="flex flex-col justify-center first:ml-auto ml-4 mr-2" + className={classNames( + "flex flex-col justify-center first:ml-auto ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )} > <div className="flex flex-row items-center justify-end"> <div className="hidden sm:flex flex-col items-center"> diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx index 4ff0c81c..5727a2a0 100644 --- a/src/components/widgets/resources/resources.jsx +++ b/src/components/widgets/resources/resources.jsx @@ -1,3 +1,5 @@ +import classNames from "classnames"; + import Disk from "./disk"; import Cpu from "./cpu"; import Memory from "./memory"; @@ -7,7 +9,10 @@ import Uptime from "./uptime"; export default function Resources({ options }) { const { expanded, units } = options; return ( - <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap"> + <div className={classNames( + "flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row self-center flex-wrap justify-between"> {options.cpu && <Cpu expanded={expanded} />} {options.memory && <Memory expanded={expanded} />} diff --git a/src/components/widgets/search/search.jsx b/src/components/widgets/search/search.jsx index 4689567f..bca3eb58 100644 --- a/src/components/widgets/search/search.jsx +++ b/src/components/widgets/search/search.jsx @@ -76,7 +76,7 @@ export default function Search({ options }) { setSelectedProvider(storedProvider); } }, [availableProviderIds]); - + if (!availableProviderIds) { return null; } @@ -102,7 +102,10 @@ export default function Search({ options }) { } return ( - <form className="flex-col relative h-8 my-4 min-w-fit grow first:ml-0 ml-4" onSubmit={handleSubmit}> + <form className={classNames( + "flex-col relative h-8 my-4 min-w-fit grow first:ml-0 ml-4", + options?.styleBoxed === true && " h-14 ml-4 mt-4 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-3", + )} onSubmit={handleSubmit}> <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 type="text" @@ -146,8 +149,8 @@ export default function Search({ options }) { leaveTo="transform opacity-0 scale-95" > <Listbox.Options - className="absolute right-0 z-10 mt-1 origin-top-right rounded-md - bg-theme-100 dark:bg-theme-600 shadow-lg + className="absolute right-0 z-10 mt-1 origin-top-right rounded-md + bg-theme-100 dark:bg-theme-600 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" > <div className="flex flex-col"> diff --git a/src/components/widgets/unifi_console/unifi_console.jsx b/src/components/widgets/unifi_console/unifi_console.jsx index 13c90bd4..1896771f 100644 --- a/src/components/widgets/unifi_console/unifi_console.jsx +++ b/src/components/widgets/unifi_console/unifi_console.jsx @@ -2,6 +2,9 @@ import { BiError, BiWifi, BiCheckCircle, BiXCircle, BiNetworkChart } from "react import { MdSettingsEthernet } from "react-icons/md"; import { useTranslation } from "next-i18next"; import { SiUbiquiti } from "react-icons/si"; +import classNames from "classnames"; + +import Error from "../error"; import useWidgetAPI from "utils/proxy/use-widget-api"; @@ -13,25 +16,17 @@ export default function Widget({ options }) { const { data: statsData, error: statsError } = useWidgetAPI(options, "stat/sites", { index: options.index }); if (statsError) { - 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> - ); + return <Error options={options} /> } const defaultSite = options.site ? statsData?.data.find(s => s.desc === options.site) : statsData?.data?.find(s => s.name === "default"); if (!defaultSite) { return ( - <div className="flex flex-col justify-center first:ml-0 ml-4"> + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="flex flex-col items-center"> <SiUbiquiti className="w-5 h-5 text-theme-800 dark:text-theme-200" /> @@ -57,7 +52,10 @@ export default function Widget({ options }) { const dataEmpty = !(wan.show || lan.show || wlan.show || uptime); return ( - <div className="flex-none flex flex-row items-center mr-3 py-1.5"> + <div className={classNames( + "flex-none flex flex-row items-center mr-3 py-1.5", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-col"> <div className="flex flex-row ml-3 mb-0.5"> <SiUbiquiti className="text-theme-800 dark:text-theme-200 w-3 h-3 mr-1" /> diff --git a/src/components/widgets/weather/weather.jsx b/src/components/widgets/weather/weather.jsx index 20bf3dec..51801455 100644 --- a/src/components/widgets/weather/weather.jsx +++ b/src/components/widgets/weather/weather.jsx @@ -1,9 +1,11 @@ import useSWR from "swr"; import { useState } from "react"; -import { BiError } from "react-icons/bi"; import { WiCloudDown } from "react-icons/wi"; import { MdLocationDisabled, MdLocationSearching } from "react-icons/md"; import { useTranslation } from "next-i18next"; +import classNames from "classnames"; + +import Error from "../error"; import Icon from "./icon"; @@ -15,24 +17,15 @@ function Widget({ options }) { ); if (error || data?.error) { - 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> - ); + return <Error options={options} /> } if (!data) { return ( - <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2"> + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <div className="flex flex-row items-center justify-end"> <div className="flex flex-col items-center"> <WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" /> @@ -49,7 +42,10 @@ function Widget({ options }) { const unit = options.units === "metric" ? "celsius" : "fahrenheit"; return ( - <div className="flex flex-col justify-center first:ml-0 ml-4 mr-2"> + <div className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )}> <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"} /> @@ -106,7 +102,10 @@ export default function WeatherApi({ options }) { <button type="button" onClick={() => requestLocation()} - className="flex flex-col justify-center first:ml-0 ml-4 mr-2" + className={classNames( + "flex flex-col justify-center first:ml-0 ml-4 mr-2", + options?.styleBoxed === true && " 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-3", + )} > <div className="flex flex-row items-center justify-end"> <div className="flex flex-col items-center"> diff --git a/src/pages/api/widgets/longhorn.js b/src/pages/api/widgets/longhorn.js index a6b6781c..d23a7f61 100644 --- a/src/pages/api/widgets/longhorn.js +++ b/src/pages/api/widgets/longhorn.js @@ -46,7 +46,7 @@ function parseLonghornData(data) { export default async function handler(req, res) { const settings = getSettings(); - const longhornSettings = settings?.providers?.longhorn; + const longhornSettings = settings?.providers?.longhorn || {}; const {url, username, password} = longhornSettings; if (!url) {