💄 Polish layouts
This commit is contained in:
@@ -2,7 +2,7 @@ import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
|
||||
import { SSRConfig } from 'next-i18next';
|
||||
import { z } from 'zod';
|
||||
import { Dashboard } from '~/components/Dashboard/Dashboard';
|
||||
import { MainLayout } from '~/components/layout/main';
|
||||
import { BoardLayout } from '~/components/layout/Templates/BoardLayout';
|
||||
import { useInitConfig } from '~/config/init';
|
||||
import { configExists } from '~/tools/config/configExists';
|
||||
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
||||
@@ -10,17 +10,15 @@ import { getServerSideTranslations } from '~/tools/server/getServerSideTranslati
|
||||
import { dashboardNamespaces } from '~/tools/server/translation-namespaces';
|
||||
import { ConfigType } from '~/types/config';
|
||||
|
||||
import { HeaderActions } from '.';
|
||||
|
||||
export default function BoardPage({
|
||||
config: initialConfig,
|
||||
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||
useInitConfig(initialConfig);
|
||||
|
||||
return (
|
||||
<MainLayout headerActions={<HeaderActions />}>
|
||||
<BoardLayout>
|
||||
<Dashboard />
|
||||
</MainLayout>
|
||||
</BoardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
BoardCustomizationFormProvider,
|
||||
useBoardCustomizationForm,
|
||||
} from '~/components/Board/Customize/form';
|
||||
import { MainLayout } from '~/components/layout/main';
|
||||
import { MainLayout } from '~/components/layout/Templates/MainLayout';
|
||||
import { createTrpcServersideHelpers } from '~/server/api/helper';
|
||||
import { getServerAuthSession } from '~/server/auth';
|
||||
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||
|
||||
@@ -1,35 +1,14 @@
|
||||
import { Button, ButtonProps, Text, Title, Tooltip } from '@mantine/core';
|
||||
import { useHotkeys, useWindowEvent } from '@mantine/hooks';
|
||||
import { openContextModal } from '@mantine/modals';
|
||||
import { hideNotification, showNotification } from '@mantine/notifications';
|
||||
import {
|
||||
IconApps,
|
||||
IconBrandDocker,
|
||||
IconEditCircle,
|
||||
IconEditCircleOff,
|
||||
IconSettings,
|
||||
} from '@tabler/icons-react';
|
||||
import Consola from 'consola';
|
||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { SSRConfig, Trans, useTranslation } from 'next-i18next';
|
||||
import Link from 'next/link';
|
||||
import { ForwardedRef, forwardRef } from 'react';
|
||||
import { SSRConfig } from 'next-i18next';
|
||||
import { Dashboard } from '~/components/Dashboard/Dashboard';
|
||||
import { useEditModeStore } from '~/components/Dashboard/Views/useEditModeStore';
|
||||
import { useNamedWrapperColumnCount } from '~/components/Dashboard/Wrappers/gridstack/store';
|
||||
import { MainLayout } from '~/components/layout/main';
|
||||
import { useCardStyles } from '~/components/layout/useCardStyles';
|
||||
import { BoardLayout } from '~/components/layout/Templates/BoardLayout';
|
||||
import { useInitConfig } from '~/config/init';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { env } from '~/env';
|
||||
import { getServerAuthSession } from '~/server/auth';
|
||||
import { prisma } from '~/server/db';
|
||||
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
||||
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||
import { dashboardNamespaces } from '~/tools/server/translation-namespaces';
|
||||
import { ConfigType } from '~/types/config';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
export default function BoardPage({
|
||||
config: initialConfig,
|
||||
@@ -37,9 +16,9 @@ export default function BoardPage({
|
||||
useInitConfig(initialConfig);
|
||||
|
||||
return (
|
||||
<MainLayout headerActions={<HeaderActions />}>
|
||||
<BoardLayout>
|
||||
<Dashboard />
|
||||
</MainLayout>
|
||||
</BoardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,205 +51,3 @@ export const getServerSideProps: GetServerSideProps<BoardGetServerSideProps> = a
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const HeaderActions = () => {
|
||||
const { data: sessionData } = useSession();
|
||||
|
||||
if (!sessionData?.user?.isAdmin) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{env.NEXT_PUBLIC_DOCKER_ENABLED && <DockerButton />}
|
||||
<ToggleEditModeButton />
|
||||
<CustomizeBoardButton />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const DockerButton = () => {
|
||||
const { t } = useTranslation('modules/docker');
|
||||
|
||||
return (
|
||||
<Tooltip label={t('actionIcon.tooltip')}>
|
||||
<HeaderActionButton component={Link} href="/docker">
|
||||
<IconBrandDocker size={20} stroke={1.5} />
|
||||
</HeaderActionButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomizeBoardButton = () => {
|
||||
const { name } = useConfigContext();
|
||||
|
||||
return (
|
||||
<Tooltip label="Customize board">
|
||||
<HeaderActionButton component={Link} href={`/board/${name}/customize`}>
|
||||
<IconSettings size={20} stroke={1.5} />
|
||||
</HeaderActionButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
type SpecificLinkProps = {
|
||||
component: typeof Link;
|
||||
href: string;
|
||||
};
|
||||
type SpecificButtonProps = {
|
||||
onClick: HTMLButtonElement['onclick'];
|
||||
};
|
||||
type HeaderActionButtonProps = Omit<ButtonProps, 'variant' | 'className' | 'h' | 'w' | 'px'> &
|
||||
(SpecificLinkProps | SpecificButtonProps);
|
||||
|
||||
const HeaderActionButton = forwardRef<
|
||||
HTMLButtonElement | HTMLAnchorElement,
|
||||
HeaderActionButtonProps
|
||||
>(({ children, ...props }, ref) => {
|
||||
const { classes } = useCardStyles(true);
|
||||
|
||||
const buttonProps: ButtonProps = {
|
||||
variant: 'default',
|
||||
className: classes.card,
|
||||
h: 38,
|
||||
w: 38,
|
||||
px: 0,
|
||||
...props,
|
||||
};
|
||||
|
||||
if ('component' in props) {
|
||||
return (
|
||||
<Button
|
||||
ref={ref as ForwardedRef<HTMLAnchorElement>}
|
||||
component={props.component}
|
||||
href={props.href}
|
||||
{...buttonProps}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button ref={ref as ForwardedRef<HTMLButtonElement>} {...buttonProps}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
|
||||
const beforeUnloadEventText = 'Exit the edit mode to save your changes';
|
||||
const editModeNotificationId = 'toggle-edit-mode';
|
||||
|
||||
const ToggleEditModeButton = () => {
|
||||
const { enabled, toggleEditMode } = useEditModeStore();
|
||||
const { config, name: configName } = useConfigContext();
|
||||
const { mutateAsync: saveConfig } = api.config.save.useMutation();
|
||||
const namedWrapperColumnCount = useNamedWrapperColumnCount();
|
||||
const { t } = useTranslation(['layout/header/actions/toggle-edit-mode', 'common']);
|
||||
const translatedSize =
|
||||
namedWrapperColumnCount !== null
|
||||
? t(`common:breakPoints.${namedWrapperColumnCount}`)
|
||||
: t('common:loading');
|
||||
|
||||
useHotkeys([['mod+E', toggleEditMode]]);
|
||||
|
||||
useWindowEvent('beforeunload', (event: BeforeUnloadEvent) => {
|
||||
if (enabled) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
event.returnValue = beforeUnloadEventText;
|
||||
return beforeUnloadEventText;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const save = async () => {
|
||||
toggleEditMode();
|
||||
if (!config || !configName) return;
|
||||
await saveConfig({ name: configName, config });
|
||||
Consola.log('Saved config to server', configName);
|
||||
hideNotification(editModeNotificationId);
|
||||
};
|
||||
|
||||
const enableEditMode = () => {
|
||||
toggleEditMode();
|
||||
showNotification({
|
||||
styles: (theme) => ({
|
||||
root: {
|
||||
backgroundColor: theme.colors.orange[7],
|
||||
borderColor: theme.colors.orange[7],
|
||||
|
||||
'&::before': { backgroundColor: theme.white },
|
||||
},
|
||||
title: { color: theme.white },
|
||||
description: { color: theme.white },
|
||||
closeButton: {
|
||||
color: theme.white,
|
||||
'&:hover': { backgroundColor: theme.colors.orange[7] },
|
||||
},
|
||||
}),
|
||||
radius: 'md',
|
||||
id: 'toggle-edit-mode',
|
||||
autoClose: 10000,
|
||||
title: (
|
||||
<Title order={4}>
|
||||
<Trans
|
||||
i18nKey="layout/header/actions/toggle-edit-mode:popover.title"
|
||||
values={{ size: translatedSize }}
|
||||
components={{
|
||||
1: (
|
||||
<Text
|
||||
component="a"
|
||||
style={{ color: 'inherit', textDecoration: 'underline' }}
|
||||
href="https://homarr.dev/docs/customizations/layout"
|
||||
target="_blank"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Title>
|
||||
),
|
||||
message: <Trans i18nKey="layout/header/actions/toggle-edit-mode:popover.text" />,
|
||||
});
|
||||
};
|
||||
|
||||
if (enabled) {
|
||||
return (
|
||||
<Button.Group>
|
||||
<Tooltip label={t('button.disabled')}>
|
||||
<HeaderActionButton onClick={save}>
|
||||
<IconEditCircleOff size={20} stroke={1.5} />
|
||||
</HeaderActionButton>
|
||||
</Tooltip>
|
||||
<AddElementButton />
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tooltip label={t('button.disabled')}>
|
||||
<HeaderActionButton onClick={enableEditMode}>
|
||||
<IconEditCircle size={20} stroke={1.5} />
|
||||
</HeaderActionButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const AddElementButton = () => {
|
||||
const { t } = useTranslation('layout/element-selector/selector');
|
||||
const { classes } = useCardStyles(true);
|
||||
|
||||
return (
|
||||
<Tooltip label={t('actionIcon.tooltip')}>
|
||||
<HeaderActionButton
|
||||
onClick={() =>
|
||||
openContextModal({
|
||||
modal: 'selectElement',
|
||||
title: t('modal.title'),
|
||||
size: 'xl',
|
||||
innerProps: {},
|
||||
})
|
||||
}
|
||||
>
|
||||
<IconApps size={20} stroke={1.5} />
|
||||
</HeaderActionButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Stack } from '@mantine/core';
|
||||
import { ContainerInfo } from 'dockerode';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import { useState } from 'react';
|
||||
import { MainLayout } from '~/components/layout/main';
|
||||
import { MainLayout } from '~/components/layout/Templates/MainLayout';
|
||||
import { env } from '~/env';
|
||||
import ContainerActionBar from '~/modules/Docker/ContainerActionBar';
|
||||
import DockerTable from '~/modules/Docker/DockerTable';
|
||||
|
||||
@@ -8,21 +8,14 @@ import {
|
||||
LoadingOverlay,
|
||||
Menu,
|
||||
SimpleGrid,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { useListState } from '@mantine/hooks';
|
||||
import { modals } from '@mantine/modals';
|
||||
import {
|
||||
IconDotsVertical,
|
||||
IconFile,
|
||||
IconFolderFilled,
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
} from '@tabler/icons-react';
|
||||
import { IconDotsVertical, IconFolderFilled, IconPlus, IconTrash } from '@tabler/icons-react';
|
||||
import Link from 'next/link';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { CommonHeader } from '~/components/layout/common-header';
|
||||
import { sleep } from '~/tools/client/time';
|
||||
import { api } from '~/utils/api';
|
||||
@@ -39,7 +32,7 @@ const BoardsPage = () => {
|
||||
const [deletingDashboards, { append, filter }] = useListState<string>([]);
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<CommonHeader>
|
||||
<title>Boards • Homarr</title>
|
||||
</CommonHeader>
|
||||
@@ -136,7 +129,7 @@ const BoardsPage = () => {
|
||||
))}
|
||||
</SimpleGrid>
|
||||
)}
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useSession } from 'next-auth/react';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||
|
||||
const ManagementPage = () => {
|
||||
@@ -23,7 +23,7 @@ const ManagementPage = () => {
|
||||
const { data: sessionData } = useSession();
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<Head>
|
||||
<title>Manage • Homarr</title>
|
||||
</Head>
|
||||
@@ -98,7 +98,7 @@ const ManagementPage = () => {
|
||||
</Card>
|
||||
</UnstyledButton>
|
||||
</SimpleGrid>
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Title, Text } from '@mantine/core';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { Text, Title } from '@mantine/core';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { CommonHeader } from '~/components/layout/common-header';
|
||||
|
||||
const SettingsPage = () => {
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<CommonHeader>
|
||||
<title>Settings • Homarr</title>
|
||||
</CommonHeader>
|
||||
|
||||
<Title>Settings</Title>
|
||||
<Text>Coming soon!</Text>
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Flex,
|
||||
Group,
|
||||
PasswordInput,
|
||||
Stepper,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { Alert, Button, Card, Flex, Group, Stepper, Table, Text, Title } from '@mantine/core';
|
||||
import { useForm, zodResolver } from '@mantine/form';
|
||||
import {
|
||||
IconArrowLeft,
|
||||
@@ -33,7 +22,7 @@ import {
|
||||
CreateAccountSecurityStep,
|
||||
createAccountSecurityStepValidationSchema,
|
||||
} from '~/components/Admin/CreateNewUser/security-step';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
const CreateNewUserPage = () => {
|
||||
@@ -70,7 +59,7 @@ const CreateNewUserPage = () => {
|
||||
});
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<Head>
|
||||
<title>Create user • Homarr</title>
|
||||
</Head>
|
||||
@@ -217,7 +206,7 @@ const CreateNewUserPage = () => {
|
||||
</Group>
|
||||
</Stepper.Completed>
|
||||
</Stepper>
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
Flex,
|
||||
Group,
|
||||
Pagination,
|
||||
SegmentedControl,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
@@ -18,7 +17,7 @@ import { IconPlus, IconTrash } from '@tabler/icons-react';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
const ManageUsersPage = () => {
|
||||
@@ -31,7 +30,7 @@ const ManageUsersPage = () => {
|
||||
});
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<Head>
|
||||
<title>Users • Homarr</title>
|
||||
</Head>
|
||||
@@ -107,7 +106,9 @@ const ManageUsersPage = () => {
|
||||
<tr>
|
||||
<td colSpan={1}>
|
||||
<Box p={15}>
|
||||
<Text>Your search does not match any entries. Please adjust your filter.</Text>
|
||||
<Text>
|
||||
Your search does not match any entries. Please adjust your filter.
|
||||
</Text>
|
||||
</Box>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -129,7 +130,7 @@ const ManageUsersPage = () => {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,15 +14,14 @@ import { IconPlus, IconTrash } from '@tabler/icons-react';
|
||||
import dayjs from 'dayjs';
|
||||
import Head from 'next/head';
|
||||
import { useState } from 'react';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
const ManageUserInvitesPage = () => {
|
||||
const [activePage, setActivePage] = useState(0);
|
||||
const { data } =
|
||||
api.registrationTokens.getAllInvites.useQuery({
|
||||
page: activePage
|
||||
});
|
||||
const { data } = api.registrationTokens.getAllInvites.useQuery({
|
||||
page: activePage,
|
||||
});
|
||||
|
||||
const { classes } = useStyles();
|
||||
|
||||
@@ -35,7 +34,7 @@ const ManageUserInvitesPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<Head>
|
||||
<title>User invites • Homarr</title>
|
||||
</Head>
|
||||
@@ -133,7 +132,7 @@ const ManageUserInvitesPage = () => {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { GetServerSidePropsContext } from 'next';
|
||||
import { forwardRef } from 'react';
|
||||
import { z } from 'zod';
|
||||
import { AccessibilitySettings } from '~/components/Settings/Customization/Accessibility/AccessibilitySettings';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { CommonHeader } from '~/components/layout/common-header';
|
||||
import { languages } from '~/tools/language';
|
||||
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||
@@ -17,14 +17,14 @@ const PreferencesPage = ({ locale }: InferGetServerSidePropsType<typeof getServe
|
||||
const { data } = api.user.getWithSettings.useQuery();
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<ManageLayout>
|
||||
<CommonHeader>
|
||||
<title>Preferences • Homarr</title>
|
||||
</CommonHeader>
|
||||
<Title mb="xl">Preferences</Title>
|
||||
|
||||
{data && <SettingsComponent settings={data.settings} />}
|
||||
</MainLayout>
|
||||
</ManageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ const SettingsComponent = ({
|
||||
searchable
|
||||
maxDropdownHeight={400}
|
||||
filter={(value, item) =>
|
||||
item.label.toLowerCase().includes(value.toLowerCase().trim()) ||
|
||||
item.label!.toLowerCase().includes(value.toLowerCase().trim()) ||
|
||||
item.description.toLowerCase().includes(value.toLowerCase().trim())
|
||||
}
|
||||
defaultValue={settings.language}
|
||||
|
||||
Reference in New Issue
Block a user