feat: use translation keys for the dashboard

This commit is contained in:
Nicolas Meienberger 2023-05-09 08:42:09 +02:00 committed by Nicolas Meienberger
parent f19508bdf2
commit 17dd675035
5 changed files with 36 additions and 15 deletions

View file

@ -5,6 +5,7 @@ import clsx from 'clsx';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { Tooltip } from 'react-tooltip';
import { useTranslations } from 'next-intl';
import { getUrl } from '../../../core/helpers/url-helpers';
import { useUIStore } from '../../../state/uiStore';
import { NavBar } from '../NavBar';
@ -24,6 +25,7 @@ export const Header: React.FC<IProps> = ({ isUpdateAvailable }) => {
router.push('/login');
},
});
const t = useTranslations('header');
return (
<header className="navbar navbar-expand-md navbar-dark navbar-overlap d-print-none">
@ -54,24 +56,24 @@ export const Header: React.FC<IProps> = ({ isUpdateAvailable }) => {
<div className="btn-list">
<a href="https://github.com/meienberger/runtipi" target="_blank" rel="noreferrer" className="btn btn-dark">
<IconBrandGithub data-testid="icon-github" className="me-1 icon" size={24} />
Source code
{t('source-code')}
</a>
<a href="https://github.com/meienberger/runtipi?sponsor=1" target="_blank" rel="noreferrer" className="btn btn-dark">
<IconHeart className="me-1 icon text-pink" size={24} />
Sponsor
{t('sponsor')}
</a>
</div>
</div>
<div style={{ zIndex: 1 }} className="d-flex">
<Tooltip anchorSelect=".darkMode">Dark mode</Tooltip>
<Tooltip anchorSelect=".darkMode">{t('dark-mode')}</Tooltip>
<div onClick={() => setDarkMode(true)} role="button" aria-hidden="true" className="darkMode nav-link px-0 hide-theme-dark cursor-pointer" data-testid="dark-mode-toggle">
<IconMoon data-testid="icon-moon" size={20} />
</div>
<Tooltip anchorSelect=".lightMode">Light mode</Tooltip>
<Tooltip anchorSelect=".lightMode">{t('light-mode')}</Tooltip>
<div onClick={() => setDarkMode(false)} aria-hidden="true" className="lightMode nav-link px-0 hide-theme-light cursor-pointer" data-testid="light-mode-toggle">
<IconSun data-testid="icon-sun" size={20} />
</div>
<Tooltip anchorSelect=".logOut">Log out</Tooltip>
<Tooltip anchorSelect=".logOut">{t('logout')}</Tooltip>
<div onClick={() => logout.mutate()} tabIndex={0} onKeyPress={() => logout.mutate()} role="button" className="logOut nav-link px-0 cursor-pointer" data-testid="logout-button">
<IconLogout size={20} />
</div>

View file

@ -1,5 +1,6 @@
import { IconApps, IconBrandAppstore, IconHome, IconSettings, Icon } from '@tabler/icons-react';
import clsx from 'clsx';
import { useTranslations } from 'next-intl';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React from 'react';
@ -9,6 +10,7 @@ interface IProps {
}
export const NavBar: React.FC<IProps> = ({ isUpdateAvailable }) => {
const t = useTranslations('header');
const router = useRouter();
const path = router.pathname.split('/')[1];
@ -32,12 +34,12 @@ export const NavBar: React.FC<IProps> = ({ isUpdateAvailable }) => {
<div id="navbar-menu" className="collapse navbar-collapse" style={{}}>
<div className="d-flex flex-column flex-md-row flex-fill align-items-stretch align-items-md-center">
<ul className="navbar-nav">
{renderItem('Dashboard', '', IconHome)}
{renderItem('My Apps', 'apps', IconApps)}
{renderItem('App Store', 'app-store', IconBrandAppstore)}
{renderItem('Settings', 'settings', IconSettings)}
{renderItem(t('dashboard'), '', IconHome)}
{renderItem(t('my-apps'), 'apps', IconApps)}
{renderItem(t('app-store'), 'app-store', IconBrandAppstore)}
{renderItem(t('settings'), 'settings', IconSettings)}
</ul>
{Boolean(isUpdateAvailable) && <span className="ms-2 badge bg-green d-none d-lg-block">Update available</span>}
{Boolean(isUpdateAvailable) && <span className="ms-2 badge bg-green d-none d-lg-block">{t('update-available')}</span>}
</div>
</div>
);

View file

@ -87,5 +87,17 @@
"subtitle": "Uninstall apps to reduce load"
}
}
},
"header": {
"dashboard": "Dashboard",
"my-apps": "My Apps",
"app-store": "App Store",
"settings": "Settings",
"logout": "Logout",
"dark-mode": "Dark Mode",
"light-mode": "Light Mode",
"sponsor": "Sponsor",
"source-code": "Source code",
"update-available": "Update available"
}
}

View file

@ -1,5 +1,6 @@
import { IconCircuitResistor, IconCpu, IconDatabase } from '@tabler/icons-react';
import React from 'react';
import { useTranslations } from 'next-intl';
import { SystemRouterOutput } from '../../../../server/routers/system/system.router';
import SystemStat from '../components/SystemStat';
@ -7,6 +8,7 @@ type IProps = { data: SystemRouterOutput['systemInfo'] };
export const DashboardContainer: React.FC<IProps> = ({ data }) => {
const { disk, memory, cpu } = data;
const t = useTranslations('dashboard');
// Convert bytes to GB
const diskFree = Math.round(disk.available / 1024 / 1024 / 1024);
const diskSize = Math.round(disk.total / 1024 / 1024 / 1024);
@ -19,9 +21,9 @@ export const DashboardContainer: React.FC<IProps> = ({ data }) => {
return (
<div className="row row-deck row-cards">
<SystemStat title="Disk space" metric={`${diskUsed} GB`} subtitle={`Used out of ${diskSize} GB`} icon={IconDatabase} progress={percentUsed} />
<SystemStat title="CPU Load" metric={`${cpu.load.toFixed(2)}%`} subtitle="Uninstall apps if there is to much load" icon={IconCpu} progress={cpu.load} />
<SystemStat title="Memory Used" metric={`${percentUsedMemory || 0}%`} subtitle={`${memoryTotal} GB`} icon={IconCircuitResistor} progress={percentUsedMemory} />
<SystemStat title={t('cards.disk.title')} metric={`${diskUsed} GB`} subtitle={t('cards.disk.subtitle', { total: diskSize })} icon={IconDatabase} progress={percentUsed} />
<SystemStat title={t('cards.cpu.title')} metric={`${cpu.load.toFixed(2)}%`} subtitle={t('cards.cpu.subtitle')} icon={IconCpu} progress={cpu.load} />
<SystemStat title={t('cards.memory.title')} metric={`${percentUsedMemory || 0}%`} subtitle={`${memoryTotal} GB`} icon={IconCircuitResistor} progress={percentUsedMemory} />
</div>
);
};

View file

@ -1,17 +1,20 @@
import React from 'react';
import type { NextPage } from 'next';
import { useTranslations } from 'next-intl';
import type { MessageKey } from '@/server/utils/errors';
import { DashboardContainer } from '../../containers/DashboardContainer';
import { trpc } from '../../../../utils/trpc';
import { Layout } from '../../../../components/Layout';
import { ErrorPage } from '../../../../components/ui/ErrorPage';
export const DashboardPage: NextPage = () => {
const t = useTranslations();
const { data, error } = trpc.system.systemInfo.useQuery();
return (
<Layout title="Dashboard">
<Layout title={t('dashboard.title')}>
{data && <DashboardContainer data={data} />}
{error && <ErrorPage error={error.message} />}
{error && <ErrorPage error={t(error.data?.translatedError || (error.message as MessageKey))} />}
</Layout>
);
};