mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-23 07:52:18 +01:00
♻️ Remove unused fields on board customization page
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
{
|
||||
"text": "Customizations allow you to configure and adjust your experience with Homarr to your preferences.",
|
||||
"accordeon": {
|
||||
"layout": {
|
||||
"name": "Layout",
|
||||
"description": "Enable and disable elements on your header and dashboard tiles"
|
||||
},
|
||||
"gridstack": {
|
||||
"name": "Gridstack",
|
||||
"description": "Customize the behaviour and columns of your dashboard area"
|
||||
"network": {
|
||||
"name": "Network",
|
||||
"description": "Enable or disable pings for this board"
|
||||
},
|
||||
"pageMetadata": {
|
||||
"name": "Page Metadata",
|
||||
@@ -17,13 +13,9 @@
|
||||
"name": "Appearance",
|
||||
"description": "Customize the background, colors and apps appearance"
|
||||
},
|
||||
"accessibility": {
|
||||
"name": "Accessibility",
|
||||
"description": "Configure Homarr for disabled and handicapped users"
|
||||
},
|
||||
"access": {
|
||||
"name": "Acccess",
|
||||
"description": "Configure who has access to your board"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { Input, Slider } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { GridstackBreakpoints } from '~/constants/gridstack-breakpoints';
|
||||
|
||||
import { useBoardCustomizationFormContext } from '../form';
|
||||
|
||||
export const GridstackCustomization = () => {
|
||||
const { t } = useTranslation('settings/customization/gridstack');
|
||||
const form = useBoardCustomizationFormContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input.Wrapper
|
||||
label={t('columnsCount.labelPreset', { size: t('common:breakPoints.small') })}
|
||||
description={t('columnsCount.descriptionPreset', { pixels: GridstackBreakpoints.medium })}
|
||||
mb="md"
|
||||
>
|
||||
<Slider min={1} max={8} mt="xs" {...form.getInputProps('gridstack.sm')} />
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper
|
||||
label={t('columnsCount.labelPreset', { size: t('common:breakPoints.medium') })}
|
||||
description={t('columnsCount.descriptionPreset', { pixels: GridstackBreakpoints.large })}
|
||||
mb="md"
|
||||
>
|
||||
<Slider min={3} max={16} mt="xs" {...form.getInputProps('gridstack.md')} />
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper
|
||||
label={t('columnsCount.labelPreset', { size: t('common:breakPoints.large') })}
|
||||
description={t('columnsCount.descriptionExceedsPreset', {
|
||||
pixels: GridstackBreakpoints.large,
|
||||
})}
|
||||
>
|
||||
<Slider min={5} max={20} mt="xs" {...form.getInputProps('gridstack.lg')} />
|
||||
</Input.Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Checkbox, Grid, Stack } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import { useBoardCustomizationFormContext } from '../form';
|
||||
import { LayoutPreview } from './LayoutPreview';
|
||||
|
||||
export const LayoutCustomization = () => {
|
||||
const { t } = useTranslation('settings/common');
|
||||
const form = useBoardCustomizationFormContext();
|
||||
|
||||
return (
|
||||
<Grid gutter="xl" align="stretch">
|
||||
<Grid.Col span={12} sm={6}>
|
||||
<LayoutPreview
|
||||
showLeftSidebar={form.values.layout.leftSidebarEnabled}
|
||||
showRightSidebar={form.values.layout.rightSidebarEnabled}
|
||||
showPings={form.values.layout.pingsEnabled}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={12} sm={6}>
|
||||
<Stack spacing="sm" h="100%" justify="space-between">
|
||||
<Stack spacing="xs">
|
||||
<Checkbox
|
||||
label={t('layout.enablelsidebar')}
|
||||
description={t('layout.enablelsidebardesc')}
|
||||
{...form.getInputProps('layout.leftSidebarEnabled', { type: 'checkbox' })}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t('layout.enablersidebar')}
|
||||
description={t('layout.enablersidebardesc')}
|
||||
{...form.getInputProps('layout.rightSidebarEnabled', { type: 'checkbox' })}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t('layout.enableping')}
|
||||
{...form.getInputProps('layout.pingsEnabled', { type: 'checkbox' })}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -1,124 +0,0 @@
|
||||
import { Flex, Group, Indicator, Paper, Stack, createStyles } from '@mantine/core';
|
||||
import { Logo } from '~/components/layout/Common/Logo';
|
||||
import { createDummyArray } from '~/tools/client/arrays';
|
||||
|
||||
type LayoutPreviewProps = {
|
||||
showLeftSidebar: boolean;
|
||||
showRightSidebar: boolean;
|
||||
showPings: boolean;
|
||||
};
|
||||
export const LayoutPreview = ({
|
||||
showLeftSidebar,
|
||||
showRightSidebar,
|
||||
showPings,
|
||||
}: LayoutPreviewProps) => {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
<Stack spacing="xs">
|
||||
<Paper px="xs" py={4} withBorder>
|
||||
<Group position="apart">
|
||||
<div style={{ flex: 1 }}>
|
||||
<Logo size="xs" />
|
||||
</div>
|
||||
<BaseElement width={60} height={10} />
|
||||
<Group style={{ flex: 1 }} position="right">
|
||||
<BaseElement width={10} height={10} />
|
||||
</Group>
|
||||
</Group>
|
||||
</Paper>
|
||||
|
||||
<Flex gap={6}>
|
||||
{showLeftSidebar && (
|
||||
<Paper className={classes.secondaryWrapper} p="xs" withBorder>
|
||||
<Flex gap={5} wrap="wrap" align="end" w={65}>
|
||||
{createDummyArray(5).map((_item, index) => (
|
||||
<PlaceholderElement
|
||||
height={index % 4 === 0 ? 60 + 5 : 30}
|
||||
width={30}
|
||||
key={`example-item-right-sidebard-${index}`}
|
||||
index={index}
|
||||
showPing={showPings}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
<Paper className={classes.primaryWrapper} p="xs" withBorder>
|
||||
<Flex gap={5} wrap="wrap">
|
||||
{createDummyArray(10).map((_item, index) => (
|
||||
<PlaceholderElement
|
||||
height={30}
|
||||
width={index % 5 === 0 ? 60 + 5 : 30}
|
||||
key={`example-item-main-${index}`}
|
||||
index={index}
|
||||
showPing={showPings}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</Paper>
|
||||
|
||||
{showRightSidebar && (
|
||||
<Paper className={classes.secondaryWrapper} p="xs" withBorder>
|
||||
<Flex gap={5} align="start" wrap="wrap" w={65}>
|
||||
{createDummyArray(5).map((_item, index) => (
|
||||
<PlaceholderElement
|
||||
height={30}
|
||||
width={index % 4 === 0 ? 60 + 5 : 30}
|
||||
key={`example-item-right-sidebard-${index}`}
|
||||
index={index}
|
||||
showPing={showPings}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
</Paper>
|
||||
)}
|
||||
</Flex>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
primaryWrapper: {
|
||||
flexGrow: 2,
|
||||
},
|
||||
secondaryWrapper: {
|
||||
flexGrow: 1,
|
||||
maxWidth: 100,
|
||||
},
|
||||
}));
|
||||
|
||||
const BaseElement = ({ height, width }: { height: number; width: number }) => (
|
||||
<Paper
|
||||
sx={(theme) => ({
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.gray[8] : theme.colors.gray[1],
|
||||
})}
|
||||
h={height}
|
||||
p={2}
|
||||
w={width}
|
||||
/>
|
||||
);
|
||||
|
||||
type PlaceholderElementProps = {
|
||||
height: number;
|
||||
width: number;
|
||||
showPing: boolean;
|
||||
index: number;
|
||||
};
|
||||
const PlaceholderElement = ({ height, width, showPing, index }: PlaceholderElementProps) => {
|
||||
if (showPing) {
|
||||
return (
|
||||
<Indicator
|
||||
position="bottom-end"
|
||||
size={5}
|
||||
offset={10}
|
||||
color={index % 4 === 0 ? 'red' : 'green'}
|
||||
>
|
||||
<BaseElement width={width} height={height} />
|
||||
</Indicator>
|
||||
);
|
||||
}
|
||||
|
||||
return <BaseElement width={width} height={height} />;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Stack, Switch } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import { useBoardCustomizationFormContext } from '../form';
|
||||
|
||||
export const NetworkCustomization = () => {
|
||||
const { t } = useTranslation('settings/common');
|
||||
const form = useBoardCustomizationFormContext();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Switch
|
||||
label={t('layout.enableping')}
|
||||
{...form.getInputProps('network.pingsEnabled', { type: 'checkbox' })}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Container,
|
||||
Grid,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
@@ -13,17 +14,16 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { showNotification, updateNotification } from '@mantine/notifications';
|
||||
import {
|
||||
IconAccessPoint,
|
||||
IconArrowLeft,
|
||||
IconBrush,
|
||||
IconChartCandle,
|
||||
IconCheck,
|
||||
IconDragDrop,
|
||||
IconLayout,
|
||||
IconLock,
|
||||
IconX,
|
||||
TablerIconsProps,
|
||||
} from '@tabler/icons-react';
|
||||
import { GetServerSideProps, GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
|
||||
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
@@ -32,8 +32,7 @@ import { ReactNode } from 'react';
|
||||
import { z } from 'zod';
|
||||
import { AccessCustomization } from '~/components/Board/Customize/Access/AccessCustomization';
|
||||
import { AppearanceCustomization } from '~/components/Board/Customize/Appearance/AppearanceCustomization';
|
||||
import { GridstackCustomization } from '~/components/Board/Customize/Gridstack/GridstackCustomization';
|
||||
import { LayoutCustomization } from '~/components/Board/Customize/Layout/LayoutCustomization';
|
||||
import { NetworkCustomization } from '~/components/Board/Customize/Network/NetworkCustomization';
|
||||
import { PageMetadataCustomization } from '~/components/Board/Customize/PageMetadata/PageMetadataCustomization';
|
||||
import {
|
||||
BoardCustomizationFormProvider,
|
||||
@@ -71,9 +70,7 @@ export default function CustomizationPage({
|
||||
access: {
|
||||
allowGuests: board.allowGuests ?? false,
|
||||
},
|
||||
layout: {
|
||||
leftSidebarEnabled: false,
|
||||
rightSidebarEnabled: false,
|
||||
network: {
|
||||
pingsEnabled: board.isPingEnabled ?? false,
|
||||
},
|
||||
appearance: {
|
||||
@@ -84,11 +81,6 @@ export default function CustomizationPage({
|
||||
opacity: board.appOpacity ?? 50,
|
||||
customCss: board.customCss ?? '',
|
||||
},
|
||||
gridstack: {
|
||||
sm: 3, //config?.settings.customization.gridstack?.columnCountSmall ?? 3,
|
||||
md: 6, //config?.settings.customization.gridstack?.columnCountMedium ?? 6,
|
||||
lg: 12, //config?.settings.customization.gridstack?.columnCountLarge ?? 12,
|
||||
},
|
||||
pageMetadata: {
|
||||
pageTitle: board.pageTitle ?? '',
|
||||
metaTitle: board.metaTitle ?? '',
|
||||
@@ -219,22 +211,24 @@ export default function CustomizationPage({
|
||||
</Group>
|
||||
<BoardCustomizationFormProvider form={form}>
|
||||
<Stack spacing="xl">
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="layout" icon={IconLayout} />
|
||||
<LayoutCustomization />
|
||||
</Stack>
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="access" icon={IconLock} />
|
||||
<AccessCustomization />
|
||||
</Stack>
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="gridstack" icon={IconDragDrop} />
|
||||
<GridstackCustomization />
|
||||
</Stack>
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="pageMetadata" icon={IconChartCandle} />
|
||||
<PageMetadataCustomization />
|
||||
</Stack>
|
||||
<Grid>
|
||||
<Grid.Col span={12} sm={6}>
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="access" icon={IconLock} />
|
||||
<AccessCustomization />
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={12} sm={6}>
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="network" icon={IconAccessPoint} />
|
||||
<NetworkCustomization />
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Stack spacing="xs">
|
||||
<SectionTitle type="appereance" icon={IconBrush} />
|
||||
<AppearanceCustomization />
|
||||
@@ -249,7 +243,7 @@ export default function CustomizationPage({
|
||||
}
|
||||
|
||||
type SectionTitleProps = {
|
||||
type: 'layout' | 'gridstack' | 'pageMetadata' | 'appereance' | 'access';
|
||||
type: 'network' | 'pageMetadata' | 'appereance' | 'access';
|
||||
icon: (props: TablerIconsProps) => ReactNode;
|
||||
};
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ export const boardRouter = createTRPCRouter({
|
||||
.update(boards)
|
||||
.set({
|
||||
allowGuests: input.customization.access.allowGuests,
|
||||
isPingEnabled: input.customization.layout.pingsEnabled,
|
||||
isPingEnabled: input.customization.network.pingsEnabled,
|
||||
appOpacity: input.customization.appearance.opacity,
|
||||
backgroundImageUrl: input.customization.appearance.backgroundSrc,
|
||||
primaryColor: input.customization.appearance.primaryColor,
|
||||
|
||||
@@ -5,12 +5,12 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { z } from 'zod';
|
||||
import { configExists } from '~/tools/config/configExists';
|
||||
import { getConfig } from '~/tools/config/getConfig';
|
||||
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
||||
import { BackendConfigType, ConfigType } from '~/types/config';
|
||||
import { boardCustomizationSchema } from '~/validations/boards';
|
||||
import { IRssWidget } from '~/widgets/rss/RssWidgetTile';
|
||||
|
||||
import { getConfig } from '~/tools/config/getConfig';
|
||||
import { adminProcedure, createTRPCRouter, publicProcedure } from '../trpc';
|
||||
|
||||
export const configNameSchema = z.string().regex(/^[a-zA-Z0-9-_]+$/);
|
||||
@@ -70,7 +70,7 @@ export const configRouter = createTRPCRouter({
|
||||
if (process.env.DISABLE_EDIT_MODE?.toLowerCase() === 'true') {
|
||||
throw new TRPCError({
|
||||
code: 'METHOD_NOT_SUPPORTED',
|
||||
message: 'Edit is not allowed, because edit mode is disabled'
|
||||
message: 'Edit is not allowed, because edit mode is disabled',
|
||||
});
|
||||
}
|
||||
Consola.info(`Saving updated configuration of '${input.name}' config.`);
|
||||
@@ -183,47 +183,4 @@ export const configRouter = createTRPCRouter({
|
||||
|
||||
return await getFrontendConfig(input.name);
|
||||
}),
|
||||
saveCusomization: adminProcedure
|
||||
.input(boardCustomizationSchema.and(z.object({ name: configNameSchema })))
|
||||
.mutation(async ({ input }) => {
|
||||
const previousConfig = getConfig(input.name);
|
||||
const newConfig = {
|
||||
...previousConfig,
|
||||
settings: {
|
||||
...previousConfig.settings,
|
||||
access: {
|
||||
...previousConfig.settings.access,
|
||||
allowGuests: input.access.allowGuests,
|
||||
},
|
||||
customization: {
|
||||
...previousConfig.settings.customization,
|
||||
appOpacity: input.appearance.opacity,
|
||||
backgroundImageUrl: input.appearance.backgroundSrc,
|
||||
colors: {
|
||||
primary: input.appearance.primaryColor,
|
||||
secondary: input.appearance.secondaryColor,
|
||||
shade: input.appearance.shade as MantineTheme['primaryShade'],
|
||||
},
|
||||
customCss: input.appearance.customCss,
|
||||
faviconUrl: input.pageMetadata.faviconSrc,
|
||||
gridstack: {
|
||||
columnCountSmall: input.gridstack.sm,
|
||||
columnCountMedium: input.gridstack.md,
|
||||
columnCountLarge: input.gridstack.lg,
|
||||
},
|
||||
layout: {
|
||||
...previousConfig.settings.customization.layout,
|
||||
enabledLeftSidebar: input.layout.leftSidebarEnabled,
|
||||
enabledRightSidebar: input.layout.rightSidebarEnabled,
|
||||
enabledPing: input.layout.pingsEnabled,
|
||||
},
|
||||
logoImageUrl: input.pageMetadata.logoSrc,
|
||||
metaTitle: input.pageMetadata.metaTitle,
|
||||
pageTitle: input.pageMetadata.pageTitle,
|
||||
},
|
||||
},
|
||||
} satisfies BackendConfigType;
|
||||
const targetPath = path.join('data/configs', `${input.name}.json`);
|
||||
fs.writeFileSync(targetPath, JSON.stringify(newConfig, null, 2), 'utf8');
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -9,16 +9,9 @@ export const boardCustomizationSchema = z.object({
|
||||
access: z.object({
|
||||
allowGuests: z.boolean(),
|
||||
}),
|
||||
layout: z.object({
|
||||
leftSidebarEnabled: z.boolean(),
|
||||
rightSidebarEnabled: z.boolean(),
|
||||
network: z.object({
|
||||
pingsEnabled: z.boolean(),
|
||||
}),
|
||||
gridstack: z.object({
|
||||
sm: z.number().min(1).max(8),
|
||||
md: z.number().min(3).max(16),
|
||||
lg: z.number().min(5).max(20),
|
||||
}),
|
||||
pageMetadata: z.object({
|
||||
pageTitle: z.string().min(1),
|
||||
metaTitle: z.string(),
|
||||
|
||||
Reference in New Issue
Block a user