Improve overall dashboard design

This commit is contained in:
Nicolas Meienberger 2022-04-23 17:58:06 +02:00
parent 206b822cac
commit 1f1c44be6c
35 changed files with 303 additions and 58 deletions

View file

@ -44,7 +44,6 @@ services:
- ${APP_PORT}:80 - ${APP_PORT}:80
volumes: volumes:
- ${APP_DATA_DIR}/data/nextcloud:/var/www/html - ${APP_DATA_DIR}/data/nextcloud:/var/www/html
- /volumes/nfs:/nfs
environment: environment:
- POSTGRES_HOST=db-nextcloud - POSTGRES_HOST=db-nextcloud
- REDIS_HOST=redis-nextcloud - REDIS_HOST=redis-nextcloud

View file

@ -16,8 +16,7 @@ services:
TZ: ${TZ} TZ: ${TZ}
WEBPASSWORD: ${APP_PASSWORD} WEBPASSWORD: ${APP_PASSWORD}
PIHOLE_DNS_: 127.0.0.1#5335 PIHOLE_DNS_: 127.0.0.1#5335
FTLCONF_REPLY_ADDR4: 192.168.2.132 FTLCONF_REPLY_ADDR4: ${INTERNAL_IP}
PIHOLE_DNS_: 127.0.0.1#5335
DNSSEC: "true" DNSSEC: "true"
DNSMASQ_LISTENING: single DNSMASQ_LISTENING: single
networks: networks:

2
dashboard/.eslintignore Normal file
View file

@ -0,0 +1,2 @@
*.config.js
.eslintrc.js

View file

@ -1,10 +1,10 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
console.log(process.env); const { NODE_ENV, INTERNAL_IP } = process.env;
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
env: { env: {
INTERNAL_IP: process.env.INTERNAL_IP, INTERNAL_IP: NODE_ENV === 'development' ? 'localhost' : INTERNAL_IP,
}, },
}; };

View file

@ -12,6 +12,7 @@
"@chakra-ui/react": "^1.8.7", "@chakra-ui/react": "^1.8.7",
"@emotion/react": "^11", "@emotion/react": "^11",
"@emotion/styled": "^11", "@emotion/styled": "^11",
"@fontsource/open-sans": "^4.5.8",
"axios": "^0.26.1", "axios": "^0.26.1",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"final-form": "^4.20.6", "final-form": "^4.20.6",

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,20 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2263 5057 c-67 -34 -125 -65 -128 -68 -3 -4 53 -126 125 -271 l132
-263 -122 -275 c-66 -151 -130 -295 -142 -320 -11 -25 -37 -83 -58 -130 -21
-47 -347 -706 -725 -1465 -378 -759 -786 -1578 -906 -1820 l-219 -440 2337 -3
c1285 -1 2338 0 2340 2 2 2 -397 809 -888 1792 -706 1417 -931 1879 -1085
2224 l-194 434 134 267 133 267 -135 66 c-103 51 -137 64 -143 54 -5 -7 -41
-79 -81 -160 -40 -81 -75 -147 -78 -148 -3 0 -27 44 -54 98 -101 203 -111 222
-116 221 -3 0 -60 -29 -127 -62z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1,003 B

View file

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

BIN
dashboard/public/tipi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -1,4 +1,4 @@
import { Box, SlideFade, Image } from '@chakra-ui/react'; import { Box, SlideFade, Image, useColorModeValue } from '@chakra-ui/react';
import Link from 'next/link'; import Link from 'next/link';
import React from 'react'; import React from 'react';
import { FiChevronRight } from 'react-icons/fi'; import { FiChevronRight } from 'react-icons/fi';
@ -6,10 +6,12 @@ import { AppConfig } from '../../core/types';
import AppStatus from './AppStatus'; import AppStatus from './AppStatus';
const AppTile: React.FC<{ app: AppConfig }> = ({ app }) => { const AppTile: React.FC<{ app: AppConfig }> = ({ app }) => {
const bg = useColorModeValue('white', '#1a202c');
return ( return (
<Link href={`/apps/${app.id}`} passHref> <Link href={`/apps/${app.id}`} passHref>
<SlideFade in className="flex flex-1" offsetY="20px"> <SlideFade in className="flex flex-1" offsetY="20px">
<Box minWidth={400} className="flex flex-1 bg-white drop-shadow-lg rounded-lg p-3 items-center cursor-pointer group hover:drop-shadow-md hover:bg-gray-100 transition-all"> <Box minWidth={400} bg={bg} className="flex flex-1 border-2 drop-shadow-sm rounded-lg p-3 items-center cursor-pointer group hover:drop-shadow-md transition-all">
<Image alt={`${app.name} logo`} className="rounded-md drop-shadow mr-3 group-hover:scale-105 transition-all" src={app.image} width={100} height={100} /> <Image alt={`${app.name} logo`} className="rounded-md drop-shadow mr-3 group-hover:scale-105 transition-all" src={app.image} width={100} height={100} />
<div className="mr-3 flex-1"> <div className="mr-3 flex-1">
<h3 className="font-bold text-xl">{app.name}</h3> <h3 className="font-bold text-xl">{app.name}</h3>

View file

@ -9,14 +9,14 @@ interface IProps {
const Header: React.FC<IProps> = ({ onClickMenu }) => { const Header: React.FC<IProps> = ({ onClickMenu }) => {
return ( return (
<header style={{ width: '100%' }} className="flex"> <header style={{ width: '100%' }} className="flex h-12 md:h-0">
<Flex className="items-center bg-gray-700 drop-shadow-md px-5 flex-1"> <Flex className="items-center border-b-2 bg-graycool px-5 flex-1 py-2">
<div onClick={onClickMenu} className="visible md:invisible absolute cursor-pointer py-2"> <div onClick={onClickMenu} className="visible md:invisible absolute cursor-pointer py-2">
<FiMenu color="white" /> <FiMenu color="black" />
</div> </div>
<Flex justifyContent="center" flex="1"> <Flex justifyContent="center" flex="1">
<Link href="/" passHref> <Link href="/" passHref>
<img src="/logo.png" alt="Tipi" width={230} height={60} /> <img src="/tipi.png" alt="Tipi Logo" width={30} height={30} />
</Link> </Link>
</Flex> </Flex>
</Flex> </Flex>

View file

@ -1,4 +1,5 @@
import { Flex, useDisclosure, Spinner, Breadcrumb, BreadcrumbItem } from '@chakra-ui/react'; import { Flex, useDisclosure, Spinner, Breadcrumb, BreadcrumbItem, useColorModeValue, Box } from '@chakra-ui/react';
import Head from 'next/head';
import Link from 'next/link'; import Link from 'next/link';
import React from 'react'; import React from 'react';
import { FiChevronRight } from 'react-icons/fi'; import { FiChevronRight } from 'react-icons/fi';
@ -12,7 +13,9 @@ interface IProps {
} }
const Layout: React.FC<IProps> = ({ children, loading, breadcrumbs }) => { const Layout: React.FC<IProps> = ({ children, loading, breadcrumbs }) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onClose, onOpen } = useDisclosure();
const menubg = useColorModeValue('#F1F3F4', '#202736');
const bg = useColorModeValue('white', '#1a202c');
const renderContent = () => { const renderContent = () => {
if (loading) { if (loading) {
@ -41,23 +44,26 @@ const Layout: React.FC<IProps> = ({ children, loading, breadcrumbs }) => {
}; };
return ( return (
<Flex height="100vh" className="drop-shadow-md border-r-8" direction="column"> <>
<MenuDrawer isOpen={isOpen} onClose={onClose}> <Head>
<Menu /> <title>Tipi</title>
</MenuDrawer> </Head>
<Header onClickMenu={onOpen} /> <Flex height="100vh" direction="column">
<Flex flex="1"> <MenuDrawer isOpen={isOpen} onClose={onClose}>
<Flex className="invisible md:visible w-0 md:w-56">
<Menu /> <Menu />
</Flex> </MenuDrawer>
<Flex className="bg-slate-200 flex flex-1 p-5"> <Header onClickMenu={onOpen} />
<div className="flex-1 flex flex-col"> <Flex flex={1}>
<Flex height="100vh" bg={menubg} className="sticky top-0 invisible md:visible w-0 md:w-64">
<Menu />
</Flex>
<Box bg={bg} className="flex-1 px-4 py-4 md:px-10 md:py-8">
{renderBreadcrumbs()} {renderBreadcrumbs()}
<div className="flex-1 ">{renderContent()}</div> {renderContent()}
</div> </Box>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </>
); );
}; };

View file

@ -1,5 +1,7 @@
import { AiOutlineDashboard, AiOutlineSetting, AiOutlineAppstore } from 'react-icons/ai'; import { AiOutlineDashboard, AiOutlineSetting, AiOutlineAppstore } from 'react-icons/ai';
import { Divider, List, ListItem } from '@chakra-ui/react'; import { FaRegMoon } from 'react-icons/fa';
import Package from '../../../package.json';
import { Box, Divider, Flex, List, ListItem, Switch, useColorMode } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import Link from 'next/link'; import Link from 'next/link';
import clsx from 'clsx'; import clsx from 'clsx';
@ -8,32 +10,54 @@ import { IconType } from 'react-icons';
const SideMenu: React.FC = () => { const SideMenu: React.FC = () => {
const router = useRouter(); const router = useRouter();
const { colorMode, setColorMode } = useColorMode();
const path = router.pathname.split('/')[1]; const path = router.pathname.split('/')[1];
const renderMenuItem = (title: string, name: string, Icon: IconType) => { const renderMenuItem = (title: string, name: string, Icon: IconType) => {
const selected = path === name; const selected = path === name;
const itemClass = clsx('mx-3 border-transparent rounded-lg p-3 transition-colors border-1', {
'drop-shadow-sm border-gray-200': selected && colorMode === 'light',
'bg-white': selected && colorMode === 'light',
});
return ( return (
<Link href={`/${name}`} passHref> <Link href={`/${name}`} passHref>
<div className={clsx('mx-3 rounded-lg p-3 transition-colors', { 'bg-slate-200 drop-shadow-sm': selected })}> <div className={itemClass}>
<ListItem className={'flex items-center cursor-pointer hover:font-bold'}> <ListItem className={'flex items-center cursor-pointer hover:font-bold'}>
<Icon size={20} className="mr-3" /> <Icon size={20} className={clsx('mr-3', { 'text-red-600': selected && colorMode === 'light', 'text-red-200': selected && colorMode === 'dark' })} />
<p className={clsx({ 'font-bold': selected })}>{title}</p> <p className={clsx({ 'font-bold': selected, 'text-red-600': selected && colorMode === 'light', 'text-red-200': selected && colorMode === 'dark' })}>{title}</p>
</ListItem> </ListItem>
</div> </div>
</Link> </Link>
); );
}; };
const handleChangeColorMode = (checked: boolean) => {
setColorMode(checked ? 'dark' : 'light');
};
return ( return (
<List spacing={3} className="pt-5 flex-1 bg-white md:border-r-2"> <Box className="flex-1 flex flex-col p-0 md:p-4">
{renderMenuItem('Dashboard', '', AiOutlineDashboard)} <img className="self-center mb-5 logo mt-0 md:mt-5" src="/tipi.png" width={512} height={512} />
<Divider /> <List spacing={3} className="pt-5">
{renderMenuItem('Apps', 'apps', AiOutlineAppstore)} {renderMenuItem('Dashboard', '', AiOutlineDashboard)}
<Divider /> {renderMenuItem('Apps', 'apps', AiOutlineAppstore)}
{renderMenuItem('Settings', 'settings', AiOutlineSetting)} {renderMenuItem('Settings', 'settings', AiOutlineSetting)}
</List> </List>
<Divider className="my-3" />
<Flex flex="1" />
<List>
<div className="mx-3">
<ListItem className="flex items-center">
<FaRegMoon size={20} className="mr-3" />
<p className="flex-1">Dark mode</p>
<Switch checked={colorMode === 'dark'} onChange={(event) => handleChangeColorMode(event.target.checked)} />
</ListItem>
</div>
</List>
<div className="pb-1 text-center text-sm text-gray-400 mt-5">Tipi version {Package.version}</div>
</Box>
); );
}; };

View file

@ -1,4 +1,4 @@
import { Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerFooter, DrawerHeader, DrawerOverlay } from '@chakra-ui/react'; import { Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerHeader, DrawerOverlay, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
interface IProps { interface IProps {
@ -7,16 +7,15 @@ interface IProps {
} }
const MenuDrawer: React.FC<IProps> = ({ children, isOpen, onClose }) => { const MenuDrawer: React.FC<IProps> = ({ children, isOpen, onClose }) => {
const menubg = useColorModeValue('#F1F3F4', '#202736');
return ( return (
<Drawer size="xs" isOpen={isOpen} placement="left" onClose={onClose}> <Drawer size="xs" isOpen={isOpen} placement="left" onClose={onClose}>
<DrawerOverlay /> <DrawerOverlay />
<DrawerContent> <DrawerContent bg={menubg}>
<DrawerCloseButton /> <DrawerCloseButton />
<DrawerHeader>My Tipi</DrawerHeader> <DrawerHeader>My Tipi</DrawerHeader>
<DrawerBody>{children}</DrawerBody> <DrawerBody display="flex">{children}</DrawerBody>
<DrawerFooter>
<div>Github</div>
</DrawerFooter>
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
); );

View file

@ -1,8 +1,6 @@
import axios, { Method } from 'axios'; import axios, { Method } from 'axios';
export const BASE_URL = 'http://192.168.2.132:3001'; export const BASE_URL = `http://${process.env.INTERNAL_IP}:3001`;
console.log(process.env);
interface IFetchParams { interface IFetchParams {
endpoint: string; endpoint: string;

View file

@ -28,7 +28,7 @@ const AppActions: React.FC<IProps> = ({ app, onInstall, onUninstall, onStart, on
<FiTrash2 className="ml-1" /> <FiTrash2 className="ml-1" />
</Button> </Button>
{hasSettings && ( {hasSettings && (
<Button onClick={onUpdate} width={160} className="mt-3 mr-2"> <Button onClick={onUpdate} width={160} colorScheme="gray" className="mt-3 mr-2">
Settings Settings
<FiSettings className="ml-1" /> <FiSettings className="ml-1" />
</Button> </Button>

View file

@ -93,7 +93,7 @@ const AppDetails: React.FC<IProps> = ({ app }) => {
return ( return (
<SlideFade in className="flex flex-1" offsetY="20px"> <SlideFade in className="flex flex-1" offsetY="20px">
<div className="flex flex-1 bg-white p-4 mt-3 rounded-lg drop-shadow-xl flex-col"> <div className="flex flex-1 p-4 mt-3 rounded-lg flex-col">
<Flex className="flex-col md:flex-row"> <Flex className="flex-col md:flex-row">
<Image src={app?.image} height={180} width={180} className="rounded-xl self-center sm:self-auto" alt={app.name} /> <Image src={app?.image} height={180} width={180} className="rounded-xl self-center sm:self-auto" alt={app.name} />
<VStack align="flex-start" justify="space-between" className="ml-0 md:ml-4"> <VStack align="flex-start" justify="space-between" className="ml-0 md:ml-4">

View file

@ -1,8 +1,11 @@
import { ChakraProvider } from '@chakra-ui/react'; import '@fontsource/open-sans/700.css';
import '@fontsource/open-sans/400.css';
import '../styles/globals.css'; import '../styles/globals.css';
import { ChakraProvider } from '@chakra-ui/react';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useNetworkStore } from '../state/networkStore'; import { useNetworkStore } from '../state/networkStore';
import { theme } from '../styles/theme';
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
const { fetchInternalIp } = useNetworkStore(); const { fetchInternalIp } = useNetworkStore();
@ -12,7 +15,7 @@ function MyApp({ Component, pageProps }: AppProps) {
}, [fetchInternalIp]); }, [fetchInternalIp]);
return ( return (
<ChakraProvider> <ChakraProvider theme={theme}>
<Component {...pageProps} /> <Component {...pageProps} />
</ChakraProvider> </ChakraProvider>
); );

View file

@ -0,0 +1,25 @@
import React from 'react';
import { Html, Head, Main, NextScript } from 'next/document';
import { ColorModeScript } from '@chakra-ui/react';
import { theme } from '../styles/theme';
export default function MyDocument() {
return (
<Html lang="en">
<Head>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
</Head>
<body>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<Main />
<NextScript />
</body>
</Html>
);
}

View file

@ -19,13 +19,13 @@ const Apps: NextPage = () => {
return ( return (
<Layout loading={loading}> <Layout loading={loading}>
<Flex className="flex-col"> <Flex className="flex-col">
{installedCount > 0 && <h1 className="font-bold text-2xl mb-3">Your Apps ({installedCount})</h1>} {installedCount > 0 && <h1 className="font-bold text-3xl mb-5">Your Apps ({installedCount})</h1>}
<SimpleGrid minChildWidth="400px" spacing="20px"> <SimpleGrid minChildWidth="400px" spacing="20px">
{installed().map((app) => ( {installed().map((app) => (
<AppTile key={app.name} app={app} /> <AppTile key={app.name} app={app} />
))} ))}
</SimpleGrid> </SimpleGrid>
{available().length && <h1 className="font-bold text-2xl mb-3 mt-3">Available Apps</h1>} {available().length && <h1 className="font-bold text-3xl mb-5 mt-5">Available Apps</h1>}
<SimpleGrid minChildWidth="400px" spacing="20px"> <SimpleGrid minChildWidth="400px" spacing="20px">
{available().map((app) => ( {available().map((app) => (
<AppTile key={app.name} app={app} /> <AppTile key={app.name} app={app} />

View file

@ -1,10 +1,70 @@
import { Progress, SimpleGrid, Stat, StatHelpText, StatLabel, StatNumber, Text } from '@chakra-ui/react';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import { useEffect } from 'react';
import Layout from '../components/Layout'; import Layout from '../components/Layout';
import { useSytemStore } from '../state/systemStore';
import { BsCpu } from 'react-icons/bs';
import { FiHardDrive } from 'react-icons/fi';
import { FaMemory } from 'react-icons/fa';
const Home: NextPage = () => { const Home: NextPage = () => {
const { fetchDiskSpace, fetchCpuLoad, fetchMemoryLoad, disk, cpuLoad, memory } = useSytemStore();
useEffect(() => {
fetchDiskSpace();
fetchCpuLoad();
fetchMemoryLoad();
const interval = setInterval(() => {
fetchDiskSpace();
fetchCpuLoad();
fetchMemoryLoad();
}, 10000);
return () => clearInterval(interval);
}, [fetchCpuLoad, fetchDiskSpace]);
// Convert bytes to GB
const diskFree = Math.round(disk.available / 1024 / 1024 / 1024);
const diskSize = Math.round(disk.size / 1024 / 1024 / 1024);
const diskUsed = diskSize - diskFree;
const percentUsed = Math.round((diskUsed / diskSize) * 100);
const memoryTotal = Math.round(memory?.total / 1024 / 1024 / 1024);
const memoryUsed = Math.round(memory?.used / 1024 / 1024 / 1024);
const percentUsedMemory = Math.round((memoryUsed / memoryTotal) * 100);
return ( return (
<Layout> <Layout>
<div>Content</div> <Text fontSize="3xl" className="font-bold">
Tipi Dashboard
</Text>
<Text fontSize="xl" color="gray.500">
Welcome home!
</Text>
<SimpleGrid className="mt-5" minChildWidth="180px" spacing="20px">
<Stat className="border-2 px-5 py-3 rounded-lg">
<StatLabel>Disk space</StatLabel>
<StatNumber>{diskUsed} GB</StatNumber>
<StatHelpText>Used out of {diskSize} GB</StatHelpText>
<Progress value={percentUsed} size="sm" />
<FiHardDrive size={30} className="absolute top-3 right-3" />
</Stat>
<Stat className="border-2 px-5 py-3 rounded-lg">
<StatLabel>CPU Load</StatLabel>
<StatNumber>{cpuLoad.toFixed(2)}%</StatNumber>
<StatHelpText>Uninstall apps if there is to much load</StatHelpText>
<Progress value={cpuLoad} size="sm" />
<BsCpu size={30} className="absolute top-3 right-3" />
</Stat>
<Stat className="border-2 px-5 py-3 rounded-lg">
<StatLabel>Memory Used</StatLabel>
<StatNumber>{percentUsedMemory}%</StatNumber>
<StatHelpText>{memoryTotal} GB</StatHelpText>
<Progress value={percentUsedMemory} size="sm" />
<FaMemory size={30} className="absolute top-3 right-3" />
</Stat>
</SimpleGrid>
</Layout> </Layout>
); );
}; };

View file

@ -0,0 +1,41 @@
import create from 'zustand';
import api from '../core/api';
type Store = {
cpuLoad: number;
disk: { size: number; used: number; available: number };
memory: { total: number; used: number; free: number };
fetchDiskSpace: () => void;
fetchCpuLoad: () => void;
fetchMemoryLoad: () => void;
};
export const useSytemStore = create<Store>((set) => ({
cpuLoad: 0,
memory: { total: 0, used: 0, free: 0 },
disk: { size: 0, used: 0, available: 0 },
fetchDiskSpace: async () => {
const response = await api.fetch<any>({
endpoint: '/system/disk',
method: 'get',
});
set({ disk: response });
},
fetchCpuLoad: async () => {
const response = await api.fetch<any>({
endpoint: '/system/cpu',
method: 'get',
});
set({ cpuLoad: response.load });
},
fetchMemoryLoad: async () => {
const response = await api.fetch<any>({
endpoint: '/system/memory',
method: 'get',
});
set({ memory: response });
},
}));

View file

@ -7,7 +7,7 @@ body {
padding: 0; padding: 0;
overflow-x: hidden; overflow-x: hidden;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family: Open Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
} }
@ -19,3 +19,20 @@ a {
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
.bg-graycool {
background-color:#F1F3F4;
}
.border-graycool {
border-color:#F1F3F4;
}
.border-1 {
border-width: 1px;
}
.logo {
width: 80px;
height: 80px;
}

View file

@ -0,0 +1,17 @@
import { extendTheme, type ThemeConfig, type Theme, withDefaultColorScheme } from '@chakra-ui/react';
const config: ThemeConfig = {
initialColorMode: 'light',
useSystemColorMode: false,
};
export const theme: Theme = extendTheme(
{
config,
fonts: {
heading: 'Open Sans, sans-serif',
body: 'Open Sans, sans-serif',
},
},
withDefaultColorScheme({ colorScheme: 'red' }),
) as Theme;

View file

@ -13,7 +13,8 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true "incremental": true,
"strictNullChecks": true
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -755,6 +755,11 @@
minimatch "^3.0.4" minimatch "^3.0.4"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@fontsource/open-sans@^4.5.8":
version "4.5.8"
resolved "https://registry.yarnpkg.com/@fontsource/open-sans/-/open-sans-4.5.8.tgz#31f727353e89ce886e1076bd58536834e0778fda"
integrity sha512-3b94XDdRLqL7OlE7OjWg/4pgG825Juw8PLVEDm6h5pio0gMU89ICxfatGxHsBxMGfqad+wnvdmUweZWlELDFpQ==
"@humanwhocodes/config-array@^0.9.2": "@humanwhocodes/config-array@^0.9.2":
version "0.9.5" version "0.9.5"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"

View file

@ -89,8 +89,6 @@ compose() {
export APP_DATA_DIR="${app_data_dir}" export APP_DATA_DIR="${app_data_dir}"
export APP_DIR="${app_dir}" export APP_DIR="${app_dir}"
# TODO: Fix for dynamic detection
export DEVICE_IP="192.168.2.132"
export ROOT_FOLDER="${ROOT_FOLDER}" export ROOT_FOLDER="${ROOT_FOLDER}"
# Docker-compose does not support multiple env files # Docker-compose does not support multiple env files