mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-17 10:41:10 +01:00
✨ Add default dashboard to preferences
This commit is contained in:
@@ -19,6 +19,8 @@ import {
|
|||||||
IconDotsVertical,
|
IconDotsVertical,
|
||||||
IconFolderFilled,
|
IconFolderFilled,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
|
IconStar,
|
||||||
|
IconStarFilled,
|
||||||
IconTrash,
|
IconTrash,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
@@ -69,14 +71,28 @@ const BoardsPage = () => {
|
|||||||
<Card key={index} shadow="sm" padding="lg" radius="md" pos="relative" withBorder>
|
<Card key={index} shadow="sm" padding="lg" radius="md" pos="relative" withBorder>
|
||||||
<LoadingOverlay visible={deletingDashboards.includes(board.name)} />
|
<LoadingOverlay visible={deletingDashboards.includes(board.name)} />
|
||||||
|
|
||||||
|
<Group mb="xl" position="apart" noWrap>
|
||||||
<Text weight={500} mb="xs">
|
<Text weight={500} mb="xs">
|
||||||
{board.name}
|
{board.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Group spacing="xs" noWrap>
|
||||||
<Group mb="xl">
|
<Badge
|
||||||
<Badge leftSection={<IconFolderFilled size=".7rem" />} color="pink" variant="light">
|
leftSection={<IconFolderFilled size=".7rem" />}
|
||||||
|
color="pink"
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
Filesystem
|
Filesystem
|
||||||
</Badge>
|
</Badge>
|
||||||
|
{board.isDefaultForUser && (
|
||||||
|
<Badge
|
||||||
|
leftSection={<IconStarFilled size=".7rem" />}
|
||||||
|
color="yellow"
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
Default
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Group, Select, Stack, Text, Title } from '@mantine/core';
|
import { Button, Group, LoadingOverlay, Select, Stack, Text, Title } from '@mantine/core';
|
||||||
import { createFormContext } from '@mantine/form';
|
import { createFormContext } from '@mantine/form';
|
||||||
import type { InferGetServerSidePropsType } from 'next';
|
import type { InferGetServerSidePropsType } from 'next';
|
||||||
import { GetServerSidePropsContext } from 'next';
|
import { GetServerSidePropsContext } from 'next';
|
||||||
@@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { AccessibilitySettings } from '~/components/User/Preferences/AccessibilitySettings';
|
import { AccessibilitySettings } from '~/components/User/Preferences/AccessibilitySettings';
|
||||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||||
|
import { sleep } from '~/tools/client/time';
|
||||||
import { languages } from '~/tools/language';
|
import { languages } from '~/tools/language';
|
||||||
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
|
||||||
import { manageNamespaces } from '~/tools/server/translation-namespaces';
|
import { manageNamespaces } from '~/tools/server/translation-namespaces';
|
||||||
@@ -65,15 +66,23 @@ const SettingsComponent = ({
|
|||||||
validateInputOnChange: true,
|
validateInputOnChange: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutate } = api.user.updateSettings.useMutation();
|
const context = api.useContext();
|
||||||
|
const { mutate, isLoading } = api.user.updateSettings.useMutation({
|
||||||
|
onSettled: () => {
|
||||||
|
void context.boards.all.invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
|
sleep(500).then(() => {
|
||||||
mutate(form.values);
|
mutate(form.values);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider form={form}>
|
<FormProvider form={form}>
|
||||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
<form style={{ position: 'relative' }} onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
|
<LoadingOverlay visible={isLoading} overlayBlur={2} />
|
||||||
<Stack spacing={5}>
|
<Stack spacing={5}>
|
||||||
<Title order={2} size="lg">
|
<Title order={2} size="lg">
|
||||||
{t('boards.title')}
|
{t('boards.title')}
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||||
|
|
||||||
|
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
all: publicProcedure.query(async ({ ctx }) => {
|
all: publicProcedure.query(async ({ ctx }) => {
|
||||||
const files = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json'));
|
const files = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json'));
|
||||||
|
|
||||||
|
const userSettings = await ctx.prisma.userSettings.findUniqueOrThrow({
|
||||||
|
where: {
|
||||||
|
userId: ctx.session?.user.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
files.map(async (file) => {
|
files.map(async (file) => {
|
||||||
const name = file.replace('.json', '');
|
const name = file.replace('.json', '');
|
||||||
@@ -17,7 +25,8 @@ export const boardRouter = createTRPCRouter({
|
|||||||
name: name,
|
name: name,
|
||||||
countApps: countApps,
|
countApps: countApps,
|
||||||
countWidgets: config.widgets.length,
|
countWidgets: config.widgets.length,
|
||||||
countCategories: config.categories.length
|
countCategories: config.categories.length,
|
||||||
|
isDefaultForUser: name === userSettings.defaultBoard
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { TRPCError } from '@trpc/server';
|
import { TRPCError } from '@trpc/server';
|
||||||
|
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
||||||
|
import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants';
|
||||||
|
|
||||||
import { hashPassword } from '~/utils/security';
|
import { hashPassword } from '~/utils/security';
|
||||||
import {
|
import {
|
||||||
colorSchemeParser,
|
colorSchemeParser,
|
||||||
@@ -9,9 +15,6 @@ import {
|
|||||||
updateSettingsValidationSchema,
|
updateSettingsValidationSchema,
|
||||||
} from '~/validations/user';
|
} from '~/validations/user';
|
||||||
|
|
||||||
import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants';
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
|
||||||
|
|
||||||
export const userRouter = createTRPCRouter({
|
export const userRouter = createTRPCRouter({
|
||||||
createFromInvite: publicProcedure
|
createFromInvite: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -151,7 +154,8 @@ export const userRouter = createTRPCRouter({
|
|||||||
update: {
|
update: {
|
||||||
disablePingPulse: input.disablePingPulse,
|
disablePingPulse: input.disablePingPulse,
|
||||||
replacePingWithIcons: input.replaceDotsWithIcons,
|
replacePingWithIcons: input.replaceDotsWithIcons,
|
||||||
language: input.language,
|
defaultBoard: input.defaultBoard,
|
||||||
|
language: input.language
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user