First commit for custom styles and JS
This commit is contained in:
parent
63f952509e
commit
213e7fdd28
12
package-lock.json
generated
12
package-lock.json
generated
@ -19,6 +19,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json-rpc-2.0": "^1.4.1",
|
"json-rpc-2.0": "^1.4.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
|
"mime": "^3.0.0",
|
||||||
"minecraft-ping-js": "^1.0.2",
|
"minecraft-ping-js": "^1.0.2",
|
||||||
"next": "^12.3.1",
|
"next": "^12.3.1",
|
||||||
"next-i18next": "^12.0.1",
|
"next-i18next": "^12.0.1",
|
||||||
@ -4166,6 +4167,17 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json-rpc-2.0": "^1.4.1",
|
"json-rpc-2.0": "^1.4.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
|
"mime": "^3.0.0",
|
||||||
"minecraft-ping-js": "^1.0.2",
|
"minecraft-ping-js": "^1.0.2",
|
||||||
"next": "^12.3.1",
|
"next": "^12.3.1",
|
||||||
"next-i18next": "^12.0.1",
|
"next-i18next": "^12.0.1",
|
||||||
|
|||||||
@ -38,6 +38,9 @@ dependencies:
|
|||||||
memory-cache:
|
memory-cache:
|
||||||
specifier: ^0.2.0
|
specifier: ^0.2.0
|
||||||
version: 0.2.0
|
version: 0.2.0
|
||||||
|
mime:
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
minecraft-ping-js:
|
minecraft-ping-js:
|
||||||
specifier: ^1.0.2
|
specifier: ^1.0.2
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
@ -2698,6 +2701,12 @@ packages:
|
|||||||
mime-db: 1.52.0
|
mime-db: 1.52.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/mime@3.0.0:
|
||||||
|
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/mimic-fn@2.1.0:
|
/mimic-fn@2.1.0:
|
||||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|||||||
12
src/components/filecontent.jsx
Normal file
12
src/components/filecontent.jsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import useSWR from "swr"
|
||||||
|
|
||||||
|
export default function FileContent({ path, loadingValue, errorValue, emptyValue = '', refresh = 1500 }) {
|
||||||
|
const fetcher = (url) => fetch(url).then((res) => res.text())
|
||||||
|
const { data, error, isLoading } = useSWR(`/api/config/${ path }`, fetcher, {
|
||||||
|
refreshInterval: refresh,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error) return (errorValue)
|
||||||
|
if (isLoading) return (loadingValue)
|
||||||
|
return (data || emptyValue)
|
||||||
|
}
|
||||||
@ -38,7 +38,7 @@ export default function ColorToggle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full self-center">
|
<div id="color" className="w-full self-center">
|
||||||
<Popover className="relative flex items-center">
|
<Popover className="relative flex items-center">
|
||||||
<Popover.Button className="outline-none">
|
<Popover.Button className="outline-none">
|
||||||
<IoColorPalette
|
<IoColorPalette
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export default function Revalidate() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-full flex align-middle self-center mr-3">
|
<div id="revalidate" className="rounded-full flex align-middle self-center mr-3">
|
||||||
<MdRefresh onClick={() => revalidate()} className="text-theme-800 dark:text-theme-200 w-6 h-6 cursor-pointer" />
|
<MdRefresh onClick={() => revalidate()} className="text-theme-800 dark:text-theme-200 w-6 h-6 cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export default function ThemeToggle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-full flex self-end">
|
<div id="theme" className="rounded-full flex self-end">
|
||||||
<MdLightMode className="text-theme-800 dark:text-theme-200 w-5 h-5 m-1.5" />
|
<MdLightMode className="text-theme-800 dark:text-theme-200 w-5 h-5 m-1.5" />
|
||||||
{theme === "dark" ? (
|
{theme === "dark" ? (
|
||||||
<MdToggleOn
|
<MdToggleOn
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export default function Version() {
|
|||||||
const latestRelease = releaseData?.[0];
|
const latestRelease = releaseData?.[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row items-center">
|
<div id="version" className="flex flex-row items-center">
|
||||||
<span className="text-xs text-theme-500 dark:text-theme-400">
|
<span className="text-xs text-theme-500 dark:text-theme-400">
|
||||||
{version === "main" || version === "dev" || version === "nightly" ? (
|
{version === "main" || version === "dev" || version === "nightly" ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export default function DateTime({ options }) {
|
|||||||
}, [date, setDate, dateLocale, format]);
|
}, [date, setDate, dateLocale, format]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container options={options}>
|
<Container options={options} additionalClassNames="information-widget-datetime">
|
||||||
<Raw>
|
<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"]}`}>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useContext } from "react";
|
|||||||
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 classNames from "classnames";
|
||||||
|
|
||||||
import Error from "../widget/error";
|
import Error from "../widget/error";
|
||||||
import Resource from "../widget/resource";
|
import Resource from "../widget/resource";
|
||||||
@ -32,7 +33,7 @@ export default function Widget({ options }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Resources options={options}>
|
return <Resources options={options} additionalClassNames="information-widget-glances">
|
||||||
{ options.cpu !== false && <Resource icon={FiCpu} label={t("glances.wait")} percentage="0" /> }
|
{ options.cpu !== false && <Resource icon={FiCpu} label={t("glances.wait")} percentage="0" /> }
|
||||||
{ options.mem !== false && <Resource icon={FaMemory} label={t("glances.wait")} percentage="0" /> }
|
{ options.mem !== false && <Resource icon={FaMemory} label={t("glances.wait")} percentage="0" /> }
|
||||||
{ options.cputemp && <Resource icon={FaThermometerHalf} label={t("glances.wait")} percentage="0" /> }
|
{ options.cputemp && <Resource icon={FaThermometerHalf} label={t("glances.wait")} percentage="0" /> }
|
||||||
@ -69,8 +70,10 @@ export default function Widget({ options }) {
|
|||||||
: [data.fs.find((d) => d.mnt_point === options.disk)].filter((d) => d);
|
: [data.fs.find((d) => d.mnt_point === options.disk)].filter((d) => d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addedClasses = classNames('information-widget-glances', { 'information-widget-expanded': options.expanded })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Resources options={options} target={settings.target ?? "_blank"}>
|
<Resources options={options} target={settings.target ?? "_blank"} additionalClassNames={addedClasses}>
|
||||||
{options.cpu !== false && <Resource
|
{options.cpu !== false && <Resource
|
||||||
icon={FiCpu}
|
icon={FiCpu}
|
||||||
value={t("common.number", {
|
value={t("common.number", {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const textSizes = {
|
|||||||
|
|
||||||
export default function Greeting({ options }) {
|
export default function Greeting({ options }) {
|
||||||
if (options.text) {
|
if (options.text) {
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-greeting">
|
||||||
<Raw>
|
<Raw>
|
||||||
<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}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export default function Widget({ options }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-kubernetes">
|
||||||
<Raw>
|
<Raw>
|
||||||
<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 &&
|
||||||
@ -50,7 +50,7 @@ export default function Widget({ options }) {
|
|||||||
</Container>;
|
</Container>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-kubernetes">
|
||||||
<Raw>
|
<Raw>
|
||||||
<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 &&
|
||||||
|
|||||||
@ -5,14 +5,14 @@ import ResolvedIcon from "components/resolvedicon"
|
|||||||
|
|
||||||
export default function Logo({ options }) {
|
export default function Logo({ options }) {
|
||||||
return (
|
return (
|
||||||
<Container options={options}>
|
<Container options={options} additionalClassNames={`information-widget-logo ${ options.icon ? 'resolved' : 'fallback'}`}>
|
||||||
<Raw>
|
<Raw>
|
||||||
{options.icon ?
|
{options.icon ?
|
||||||
<div className="mr-3">
|
<div className="resolved mr-3">
|
||||||
<ResolvedIcon icon={options.icon} width={48} height={48} />
|
<ResolvedIcon icon={options.icon} width={48} height={48} />
|
||||||
</div> :
|
</div> :
|
||||||
// fallback to homepage logo
|
// fallback to homepage logo
|
||||||
<div className="w-12 h-12">
|
<div className="fallback w-12 h-12">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 1024 1024"
|
viewBox="0 0 1024 1024"
|
||||||
|
|||||||
@ -17,14 +17,14 @@ export default function Longhorn({ options }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="infomation-widget-longhorn">
|
||||||
<Raw>
|
<Raw>
|
||||||
<div className="flex flex-row self-center flex-wrap justify-between" />
|
<div className="flex flex-row self-center flex-wrap justify-between" />
|
||||||
</Raw>
|
</Raw>
|
||||||
</Container>;
|
</Container>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="infomation-widget-longhorn">
|
||||||
<Raw>
|
<Raw>
|
||||||
<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
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export default function Node({ data, expanded, labels }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return <Resource
|
return <Resource
|
||||||
|
additionalClassNames="information-widget-node"
|
||||||
icon={FaThermometerHalf}
|
icon={FaThermometerHalf}
|
||||||
value={t("common.bytes", { value: data.node.available })}
|
value={t("common.bytes", { value: data.node.available })}
|
||||||
label={t("resources.free")}
|
label={t("resources.free")}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ function Widget({ options }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-openmeteo">
|
||||||
<PrimaryText>{t("weather.updating")}</PrimaryText>
|
<PrimaryText>{t("weather.updating")}</PrimaryText>
|
||||||
<SecondaryText>{t("weather.wait")}</SecondaryText>
|
<SecondaryText>{t("weather.wait")}</SecondaryText>
|
||||||
<WidgetIcon icon={WiCloudDown} size="l" />
|
<WidgetIcon icon={WiCloudDown} size="l" />
|
||||||
@ -35,7 +35,7 @@ function Widget({ options }) {
|
|||||||
const condition = data.current_weather.weathercode;
|
const condition = data.current_weather.weathercode;
|
||||||
const timeOfDay = data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night";
|
const timeOfDay = data.current_weather.time > data.daily.sunrise[0] && data.current_weather.time < data.daily.sunset[0] ? "day" : "night";
|
||||||
|
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-openmeteo">
|
||||||
<PrimaryText>
|
<PrimaryText>
|
||||||
{options.label && `${options.label}, `}
|
{options.label && `${options.label}, `}
|
||||||
{t("common.number", {
|
{t("common.number", {
|
||||||
@ -81,7 +81,7 @@ export default function OpenMeteo({ options }) {
|
|||||||
// if (!requesting && !location) requestLocation();
|
// if (!requesting && !location) requestLocation();
|
||||||
|
|
||||||
if (!location) {
|
if (!location) {
|
||||||
return <ContainerButton options={options} callback={requestLocation} >
|
return <ContainerButton options={options} callback={requestLocation} additionalClassNames="information-widget-openmeteo-location-button">
|
||||||
<PrimaryText>{t("weather.current")}</PrimaryText>
|
<PrimaryText>{t("weather.current")}</PrimaryText>
|
||||||
<SecondaryText>{t("weather.allow")}</SecondaryText>
|
<SecondaryText>{t("weather.allow")}</SecondaryText>
|
||||||
<WidgetIcon icon={ requesting ? MdLocationSearching : MdLocationDisabled} size="m" pulse />
|
<WidgetIcon icon={ requesting ? MdLocationSearching : MdLocationDisabled} size="m" pulse />
|
||||||
|
|||||||
@ -24,7 +24,7 @@ function Widget({ options }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-openweathermap">
|
||||||
<PrimaryText>{t("weather.updating")}</PrimaryText>
|
<PrimaryText>{t("weather.updating")}</PrimaryText>
|
||||||
<SecondaryText>{t("weather.wait")}</SecondaryText>
|
<SecondaryText>{t("weather.wait")}</SecondaryText>
|
||||||
<WidgetIcon icon={WiCloudDown} size="l" />
|
<WidgetIcon icon={WiCloudDown} size="l" />
|
||||||
@ -36,7 +36,7 @@ function Widget({ options }) {
|
|||||||
const condition = data.weather[0].id;
|
const condition = data.weather[0].id;
|
||||||
const timeOfDay = data.dt > data.sys.sunrise && data.dt < data.sys.sunset ? "day" : "night";
|
const timeOfDay = data.dt > data.sys.sunrise && data.dt < data.sys.sunset ? "day" : "night";
|
||||||
|
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-openweathermap">
|
||||||
<PrimaryText>{options.label && `${options.label}, ` }{t("common.number", { value: data.main.temp, style: "unit", unit })}</PrimaryText>
|
<PrimaryText>{options.label && `${options.label}, ` }{t("common.number", { value: data.main.temp, style: "unit", unit })}</PrimaryText>
|
||||||
<SecondaryText>{data.weather[0].description}</SecondaryText>
|
<SecondaryText>{data.weather[0].description}</SecondaryText>
|
||||||
<WidgetIcon icon={mapIcon(condition, timeOfDay)} size="xl" />
|
<WidgetIcon icon={mapIcon(condition, timeOfDay)} size="xl" />
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export default function UsageBar({ percent }) {
|
export default function UsageBar({ percent, additionalClassNames='' }) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-theme-200/20">
|
<div className={`mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-theme-200/20 ${ additionalClassNames}`}>
|
||||||
<div
|
<div
|
||||||
className="bg-theme-800/70 h-1 rounded-full dark:bg-theme-200/50 transition-all duration-1000"
|
className="bg-theme-800/70 h-1 rounded-full dark:bg-theme-200/50 transition-all duration-1000"
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -103,7 +103,7 @@ export default function Search({ options }) {
|
|||||||
localStorage.setItem(localStorageKey, provider.name);
|
localStorage.setItem(localStorageKey, provider.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ContainerForm options={options} callback={submitCallback} additionalClassNames="grow" >
|
return <ContainerForm options={options} callback={submitCallback} additionalClassNames="grow information-widget-search" >
|
||||||
<Raw>
|
<Raw>
|
||||||
<div className="flex-col relative h-8 my-4 min-w-fit">
|
<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" />
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export default function Widget({ options }) {
|
|||||||
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 <Container options={options} additionalClassNames="information-widget-unify-console">
|
||||||
<PrimaryText>{t("unifi.wait")}</PrimaryText>
|
<PrimaryText>{t("unifi.wait")}</PrimaryText>
|
||||||
<WidgetIcon icon={SiUbiquiti} />
|
<WidgetIcon icon={SiUbiquiti} />
|
||||||
</Container>;
|
</Container>;
|
||||||
@ -43,7 +43,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 <Container options={options} additionalClassNames="information-widget-unify-console">
|
||||||
<Raw>
|
<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">
|
||||||
|
|||||||
@ -24,7 +24,7 @@ function Widget({ options }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-weather">
|
||||||
<PrimaryText>{t("weather.updating")}</PrimaryText>
|
<PrimaryText>{t("weather.updating")}</PrimaryText>
|
||||||
<SecondaryText>{t("weather.wait")}</SecondaryText>
|
<SecondaryText>{t("weather.wait")}</SecondaryText>
|
||||||
<WidgetIcon icon={WiCloudDown} size="l" />
|
<WidgetIcon icon={WiCloudDown} size="l" />
|
||||||
@ -35,7 +35,7 @@ function Widget({ options }) {
|
|||||||
const condition = data.current.condition.code;
|
const condition = data.current.condition.code;
|
||||||
const timeOfDay = data.current.is_day ? "day" : "night";
|
const timeOfDay = data.current.is_day ? "day" : "night";
|
||||||
|
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-weather">
|
||||||
<PrimaryText>
|
<PrimaryText>
|
||||||
{options.label && `${options.label}, `}
|
{options.label && `${options.label}, `}
|
||||||
{t("common.number", {
|
{t("common.number", {
|
||||||
|
|||||||
@ -35,9 +35,9 @@ export function getAllClasses(options, additionalClassNames = '') {
|
|||||||
|
|
||||||
export function getInnerBlock(children) {
|
export function getInnerBlock(children) {
|
||||||
// children won't be an array if it's Raw component
|
// children won't be an array if it's Raw component
|
||||||
return Array.isArray(children) && <div className="flex flex-row items-center justify-end">
|
return Array.isArray(children) && <div className="flex flex-row items-center justify-end widget-inner">
|
||||||
<div className="flex flex-col items-center">{children.find(child => child.type === WidgetIcon)}</div>
|
<div className="flex flex-col items-center widget-inner-icon">{children.find(child => child.type === WidgetIcon)}</div>
|
||||||
<div className="flex flex-col ml-3 text-left">
|
<div className="flex flex-col ml-3 text-left widget-inner-text">
|
||||||
{children.find(child => child.type === PrimaryText)}
|
{children.find(child => child.type === PrimaryText)}
|
||||||
{children.find(child => child.type === SecondaryText)}
|
{children.find(child => child.type === SecondaryText)}
|
||||||
</div>
|
</div>
|
||||||
@ -54,7 +54,7 @@ export function getBottomBlock(children) {
|
|||||||
|
|
||||||
export default function Container({ children = [], options, additionalClassNames = '' }) {
|
export default function Container({ children = [], options, additionalClassNames = '' }) {
|
||||||
return (
|
return (
|
||||||
<div className={getAllClasses(options, additionalClassNames)}>
|
<div className={getAllClasses(options, `${ additionalClassNames } widget-container`)}>
|
||||||
{getInnerBlock(children)}
|
{getInnerBlock(children)}
|
||||||
{getBottomBlock(children)}
|
{getBottomBlock(children)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { getAllClasses, getInnerBlock, getBottomBlock } from "./container";
|
|||||||
|
|
||||||
export default function ContainerButton ({ children = [], options, additionalClassNames = '', callback }) {
|
export default function ContainerButton ({ children = [], options, additionalClassNames = '', callback }) {
|
||||||
return (
|
return (
|
||||||
<button type="button" onClick={callback} className={getAllClasses(options, additionalClassNames)}>
|
<button type="button" onClick={callback} className={`${ getAllClasses(options, additionalClassNames) } information-widget-container-button`}>
|
||||||
{getInnerBlock(children)}
|
{getInnerBlock(children)}
|
||||||
{getBottomBlock(children)}
|
{getBottomBlock(children)}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { getAllClasses, getInnerBlock, getBottomBlock } from "./container";
|
|||||||
|
|
||||||
export default function ContainerForm ({ children = [], options, additionalClassNames = '', callback }) {
|
export default function ContainerForm ({ children = [], options, additionalClassNames = '', callback }) {
|
||||||
return (
|
return (
|
||||||
<form type="button" onSubmit={callback} className={getAllClasses(options, additionalClassNames)}>
|
<form type="button" onSubmit={callback} className={`${ getAllClasses(options, additionalClassNames) } information-widget-form`}>
|
||||||
{getInnerBlock(children)}
|
{getInnerBlock(children)}
|
||||||
{getBottomBlock(children)}
|
{getBottomBlock(children)}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { getAllClasses, getInnerBlock, getBottomBlock } from "./container";
|
|||||||
|
|
||||||
export default function ContainerLink ({ children = [], options, additionalClassNames = '', target }) {
|
export default function ContainerLink ({ children = [], options, additionalClassNames = '', target }) {
|
||||||
return (
|
return (
|
||||||
<a href={options.url} target={target} className={getAllClasses(options, additionalClassNames)}>
|
<a href={options.url} target={target} className={`${ getAllClasses(options, additionalClassNames) } information-widget-link`}>
|
||||||
{getInnerBlock(children)}
|
{getInnerBlock(children)}
|
||||||
{getBottomBlock(children)}
|
{getBottomBlock(children)}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import WidgetIcon from "./widget_icon";
|
|||||||
export default function Error({ options }) {
|
export default function Error({ options }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return <Container options={options}>
|
return <Container options={options} additionalClassNames="information-widget-error">
|
||||||
<PrimaryText>{t("widget.api_error")}</PrimaryText>
|
<PrimaryText>{t("widget.api_error")}</PrimaryText>
|
||||||
<WidgetIcon icon={BiError} size="l" />
|
<WidgetIcon icon={BiError} size="l" />
|
||||||
</Container>;
|
</Container>;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export default function PrimaryText({ children }) {
|
export default function PrimaryText({ children }) {
|
||||||
return (
|
return (
|
||||||
<span className="text-theme-800 dark:text-theme-200 text-sm">{children}</span>
|
<span className="primary-text text-theme-800 dark:text-theme-200 text-sm">{children}</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import UsageBar from "../resources/usage-bar";
|
import UsageBar from "../resources/usage-bar";
|
||||||
|
|
||||||
export default function Resource({ children, icon, value, label, expandedValue = "", expandedLabel = "", percentage, expanded = false }) {
|
export default function Resource({ children, icon, value, label, expandedValue = "", expandedLabel = "", percentage, expanded = false, additionalClassNames='' }) {
|
||||||
const Icon = icon;
|
const Icon = icon;
|
||||||
|
|
||||||
return <div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
return <div className={`flex-none flex flex-row items-center mr-3 py-1.5 information-widget-resource ${ additionalClassNames }`}>
|
||||||
<Icon className="text-theme-800 dark:text-theme-200 w-5 h-5"/>
|
<Icon className="text-theme-800 dark:text-theme-200 w-5 h-5 resource-icon"/>
|
||||||
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
<div className={ `flex flex-col ml-3 text-left min-w-[85px] resource-label${ expanded ? ' expanded' : ''}`}>
|
||||||
<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">{value}</div>
|
<div className="pl-0.5">{value}</div>
|
||||||
<div className="pr-1">{label}</div>
|
<div className="pr-1">{label}</div>
|
||||||
@ -15,7 +15,7 @@ export default function Resource({ children, icon, value, label, expandedValue =
|
|||||||
<div className="pr-1">{expandedLabel}</div>
|
<div className="pr-1">{expandedLabel}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{ percentage >= 0 && <UsageBar percent={percentage} /> }
|
{ percentage >= 0 && <UsageBar percent={percentage} additionalClassNames="resource-usage" /> }
|
||||||
{ children }
|
{ children }
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
import ContainerLink from "./container_link";
|
import ContainerLink from "./container_link";
|
||||||
import Resource from "./resource";
|
import Resource from "./resource";
|
||||||
import Raw from "./raw";
|
import Raw from "./raw";
|
||||||
import WidgetLabel from "./widget_label";
|
import WidgetLabel from "./widget_label";
|
||||||
|
|
||||||
export default function Resources({ options, children, target }) {
|
export default function Resources({ options, children, target, additionalClassNames }) {
|
||||||
const widgetParts = [].concat(...children);
|
const widgetParts = [].concat(...children);
|
||||||
|
const addedClassNames = classNames('information-widget-resources', additionalClassNames);
|
||||||
|
|
||||||
return <ContainerLink options={options} target={target}>
|
return <ContainerLink options={options} target={target} additionalClassNames={ addedClassNames }>
|
||||||
<Raw>
|
<Raw>
|
||||||
<div className="flex flex-row self-center flex-wrap justify-between">
|
<div className="flex flex-row self-center flex-wrap justify-between">
|
||||||
{ widgetParts.filter(child => child && child.type === Resource) }
|
{ widgetParts.filter(child => child && child.type === Resource) }
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export default function SecondaryText({ children }) {
|
export default function SecondaryText({ children }) {
|
||||||
return (
|
return (
|
||||||
<span className="text-theme-800 dark:text-theme-200 text-xs">{children}</span>
|
<span className="secondary-text text-theme-800 dark:text-theme-200 text-xs">{children}</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export default function WidgetIcon({ icon, size = "s", pulse = false }) {
|
export default function WidgetIcon({ icon, size = "s", pulse = false }) {
|
||||||
const Icon = icon;
|
const Icon = icon;
|
||||||
let additionalClasses = "text-theme-800 dark:text-theme-200 ";
|
let additionalClasses = "information-widget-icon text-theme-800 dark:text-theme-200 ";
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case "m": additionalClasses += "w-6 h-6 "; break;
|
case "m": additionalClasses += "w-6 h-6 "; break;
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export default function WidgetLabel({ label = "" }) {
|
export default function WidgetLabel({ label = "" }) {
|
||||||
return <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{label}</div>
|
return <div className="information-widget-label pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{label}</div>
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/pages/api/config/[path].js
Normal file
51
src/pages/api/config/[path].js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
import mime from "mime";
|
||||||
|
|
||||||
|
import { CONF_DIR } from "utils/config/config";
|
||||||
|
import createLogger from "utils/logger";
|
||||||
|
|
||||||
|
const logger = createLogger("configFileService");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the config file paths are in subdirectory
|
||||||
|
* @param {string} parent Parent initial folder
|
||||||
|
* @param {string} child Supposed child path
|
||||||
|
* @returns {boolean} true if in a subdirectory
|
||||||
|
*/
|
||||||
|
function isSubDirectory(parent, child) {
|
||||||
|
return path.relative(child, parent).startsWith('..');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("next").NextApiRequest} req
|
||||||
|
* @param {import("next").NextApiResponse} res
|
||||||
|
*/
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
const { path: relativePath } = req.query;
|
||||||
|
|
||||||
|
const filePath = path.join(CONF_DIR, relativePath);
|
||||||
|
|
||||||
|
if(!isSubDirectory(CONF_DIR, filePath))
|
||||||
|
{
|
||||||
|
logger.error(`Forbidden access to parent file: ${ filePath }`);
|
||||||
|
res.status(403).end('Forbidden access to parent file');
|
||||||
|
}
|
||||||
|
|
||||||
|
const mimeType = mime.getType(relativePath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read the content of the CSS file
|
||||||
|
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
|
||||||
|
// Set the response header to indicate that this is a CSS file
|
||||||
|
res.setHeader('Content-Type', mimeType);
|
||||||
|
|
||||||
|
// Send the CSS content as the API response
|
||||||
|
res.status(200).send(fileContent);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
res.status(500).end('Internal Server Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ import { useEffect, useContext, useState, useMemo } from "react";
|
|||||||
import { BiError } from "react-icons/bi";
|
import { BiError } from "react-icons/bi";
|
||||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||||
|
|
||||||
|
import FileContent from "components/filecontent";
|
||||||
import ServicesGroup from "components/services/group";
|
import ServicesGroup from "components/services/group";
|
||||||
import BookmarksGroup from "components/bookmarks/group";
|
import BookmarksGroup from "components/bookmarks/group";
|
||||||
import Widget from "components/widgets/widget";
|
import Widget from "components/widgets/widget";
|
||||||
@ -311,6 +312,22 @@ function Home({ initialSettings }) {
|
|||||||
<meta name="msapplication-TileColor" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
|
<meta name="msapplication-TileColor" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
|
||||||
<meta name="theme-color" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
|
<meta name="theme-color" content={themes[settings.color || "slate"][settings.theme || "dark"]} />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
|
<style alt="custom">
|
||||||
|
<FileContent path="custom.css"
|
||||||
|
loadingValue="/* Loading custom CSS... */"
|
||||||
|
errorValue="/* Failed to load custom CSS... */"
|
||||||
|
emptyValue="/* No custom CSS */"
|
||||||
|
/>
|
||||||
|
</style>
|
||||||
|
<script alt="custom">
|
||||||
|
<FileContent path="custom.js"
|
||||||
|
loadingValue="/* Loading custom JS... */"
|
||||||
|
errorValue="/* Failed to load custom JS... */"
|
||||||
|
emptyValue="/* No custom JS */"
|
||||||
|
/>
|
||||||
|
</script>
|
||||||
|
|
||||||
<div className="relative container m-auto flex flex-col justify-start z-10 h-full">
|
<div className="relative container m-auto flex flex-col justify-start z-10 h-full">
|
||||||
<QuickLaunch
|
<QuickLaunch
|
||||||
servicesAndBookmarks={servicesAndBookmarks}
|
servicesAndBookmarks={servicesAndBookmarks}
|
||||||
@ -321,6 +338,7 @@ function Home({ initialSettings }) {
|
|||||||
searchProvider={settings.quicklaunch?.hideInternetSearch ? null : searchProvider}
|
searchProvider={settings.quicklaunch?.hideInternetSearch ? null : searchProvider}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
id="information-widgets"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"flex flex-row flex-wrap justify-between",
|
"flex flex-row flex-wrap justify-between",
|
||||||
headerStyles[headerStyle],
|
headerStyles[headerStyle],
|
||||||
@ -335,7 +353,7 @@ function Home({ initialSettings }) {
|
|||||||
<Widget key={i} widget={widget} style={{ header: headerStyle, isRightAligned: false, cardBlur: settings.cardBlur }} />
|
<Widget key={i} widget={widget} style={{ header: headerStyle, isRightAligned: false, cardBlur: settings.cardBlur }} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className={classNames(
|
<div id="information-widgets-right" className={classNames(
|
||||||
"m-auto flex flex-wrap grow sm:basis-auto justify-between md:justify-end",
|
"m-auto flex flex-wrap grow sm:basis-auto justify-between md:justify-end",
|
||||||
headerStyle === "boxedWidgets" ? "sm:ml-4" : "sm:ml-2"
|
headerStyle === "boxedWidgets" ? "sm:ml-4" : "sm:ml-2"
|
||||||
)}>
|
)}>
|
||||||
@ -351,14 +369,14 @@ function Home({ initialSettings }) {
|
|||||||
|
|
||||||
{servicesAndBookmarksGroups}
|
{servicesAndBookmarksGroups}
|
||||||
|
|
||||||
<div className="flex flex-col mt-auto p-8 w-full">
|
<div id="footer" className="flex flex-col mt-auto p-8 w-full">
|
||||||
<div className="flex w-full justify-end">
|
<div id="style" className="flex w-full justify-end">
|
||||||
{!settings?.color && <ColorToggle />}
|
{!settings?.color && <ColorToggle />}
|
||||||
<Revalidate />
|
<Revalidate />
|
||||||
{!settings.theme && <ThemeToggle />}
|
{!settings.theme && <ThemeToggle />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex mt-4 w-full justify-end">
|
<div id="version" className="flex mt-4 w-full justify-end">
|
||||||
{!settings.hideVersion && <Version />}
|
{!settings.hideVersion && <Version />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
0
src/skeleton/custom.css
Normal file
0
src/skeleton/custom.css
Normal file
0
src/skeleton/custom.js
Normal file
0
src/skeleton/custom.js
Normal file
Loading…
Reference in New Issue
Block a user