Merge branch 'feature/add-basic-authentication' of https://github.com/ajnart/homarr into feature/add-basic-authentication

This commit is contained in:
Meier Lukas
2023-07-31 23:52:03 +02:00
4 changed files with 118 additions and 44 deletions

View File

@@ -8,7 +8,7 @@ import { createDashboardSchemaValidation } from '~/validations/dashboards';
export const CreateDashboardModal = ({ context, id, innerProps }: ContextModalProps<{}>) => { export const CreateDashboardModal = ({ context, id, innerProps }: ContextModalProps<{}>) => {
const apiContext = api.useContext(); const apiContext = api.useContext();
const { isLoading, mutateAsync } = api.config.save.useMutation({ const { isLoading, mutate } = api.config.save.useMutation({
onSuccess: async () => { onSuccess: async () => {
await apiContext.config.all.invalidate(); await apiContext.config.all.invalidate();
modals.close(id); modals.close(id);
@@ -24,37 +24,43 @@ export const CreateDashboardModal = ({ context, id, innerProps }: ContextModalPr
validate: i18nZodResolver(createDashboardSchemaValidation), validate: i18nZodResolver(createDashboardSchemaValidation),
}); });
const handleSubmit = () => {
const fallbackConfig = getStaticFallbackConfig(form.values.name);
mutate({
name: form.values.name,
config: fallbackConfig,
});
};
return ( return (
<Stack> <form onSubmit={form.onSubmit(handleSubmit)}>
<Text>A name cannot be changed after a dashboard has been created.</Text> <Stack>
<Text>A name cannot be changed after a dashboard has been created.</Text>
<TextInput label="Name" withAsterisk {...form.getInputProps('name')} /> <TextInput label="Name" withAsterisk {...form.getInputProps('name')} />
<Group grow> <Group grow>
<Button <Button
onClick={() => { onClick={() => {
modals.close(id); modals.close(id);
}} }}
variant="light" variant="light"
color="gray" color="gray"
> type="button"
Cancel >
</Button> Cancel
<Button </Button>
onClick={async () => { <Button
const fallbackConfig = getStaticFallbackConfig(form.values.name); type="submit"
await mutateAsync({ onClick={async () => {}}
name: form.values.name, disabled={isLoading}
config: fallbackConfig, variant="light"
}); color="green"
}} >
disabled={isLoading} Create
variant="light" </Button>
color="green" </Group>
> </Stack>
Create </form>
</Button>
</Group>
</Stack>
); );
}; };

View File

@@ -0,0 +1,52 @@
import { Button, Group, Stack, Text } from '@mantine/core';
import { ContextModalProps, modals } from '@mantine/modals';
import { api } from '~/utils/api';
export const DeleteBoardModal = ({
context,
id,
innerProps,
}: ContextModalProps<{ boardName: string; onConfirm: () => Promise<void> }>) => {
const apiContext = api.useContext();
const { isLoading, mutateAsync } = api.config.delete.useMutation({
onSuccess: async () => {
await apiContext.config.all.invalidate();
modals.close(id);
},
});
return (
<Stack>
<Text>
Are you sure, that you want to delete this board? This action cannot be undone and your data
will be lost permanently.
</Text>
<Group grow>
<Button
onClick={() => {
modals.close(id);
}}
variant="light"
color="gray"
>
Cancel
</Button>
<Button
onClick={async () => {
modals.close(id);
await innerProps.onConfirm();
await mutateAsync({
name: innerProps.boardName,
});
}}
disabled={isLoading}
variant="light"
color="red"
>
Delete
</Button>
</Group>
</Stack>
);
};

View File

@@ -11,6 +11,7 @@ import { CreateRegistrationTokenModal } from './create-registration-token/create
import { DeleteRegistrationTokenModal } from './delete-registration-token/delete-registration-token.modal'; import { DeleteRegistrationTokenModal } from './delete-registration-token/delete-registration-token.modal';
import { CreateDashboardModal } from './create-dashboard/create-dashboard.modal'; import { CreateDashboardModal } from './create-dashboard/create-dashboard.modal';
import { CopyRegistrationToken } from './copy-regristration-token/copy-registration-token.modal'; import { CopyRegistrationToken } from './copy-regristration-token/copy-registration-token.modal';
import { DeleteBoardModal } from './delete-board/delete-board.modal';
export const modals = { export const modals = {
editApp: EditAppModal, editApp: EditAppModal,
@@ -25,6 +26,7 @@ export const modals = {
deleteRegistrationTokenModal: DeleteRegistrationTokenModal, deleteRegistrationTokenModal: DeleteRegistrationTokenModal,
createDashboardModal: CreateDashboardModal, createDashboardModal: CreateDashboardModal,
copyRegistrationTokenModal: CopyRegistrationToken, copyRegistrationTokenModal: CopyRegistrationToken,
deleteBoardModal: DeleteBoardModal
}; };
declare module '@mantine/modals' { declare module '@mantine/modals' {

View File

@@ -14,7 +14,13 @@ import {
} from '@mantine/core'; } from '@mantine/core';
import { useListState } from '@mantine/hooks'; import { useListState } from '@mantine/hooks';
import { modals } from '@mantine/modals'; import { modals } from '@mantine/modals';
import { IconDotsVertical, IconPlus, IconTrash } from '@tabler/icons-react'; import {
IconDotsVertical,
IconFile,
IconFolderFilled,
IconPlus,
IconTrash,
} from '@tabler/icons-react';
import Link from 'next/link'; import Link from 'next/link';
import { MainLayout } from '~/components/layout/admin/main-admin.layout'; import { MainLayout } from '~/components/layout/admin/main-admin.layout';
import { CommonHeader } from '~/components/layout/common-header'; import { CommonHeader } from '~/components/layout/common-header';
@@ -45,14 +51,14 @@ const BoardsPage = () => {
onClick={() => { onClick={() => {
modals.openContextModal({ modals.openContextModal({
modal: 'createDashboardModal', modal: 'createDashboardModal',
title: <Text>Create new dashboard</Text>, title: <Text>Create new board</Text>,
innerProps: {}, innerProps: {},
}); });
}} }}
leftIcon={<IconPlus size="1rem" />} leftIcon={<IconPlus size="1rem" />}
variant="default" variant="default"
> >
Create new dashboard Create new board
</Button> </Button>
</Flex> </Flex>
@@ -68,9 +74,13 @@ const BoardsPage = () => {
{data.map((board, index) => ( {data.map((board, index) => (
<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)} /> <LoadingOverlay visible={deletingDashboards.includes(board)} />
<Group position="apart" mb="xs">
<Text weight={500}>{board}</Text> <Text weight={500} mb="xs">
<Badge color="pink" variant="light"> {board}
</Text>
<Group mb="xl">
<Badge leftSection={<IconFolderFilled size=".7rem" />} color="pink" variant="light">
Filesystem Filesystem
</Badge> </Badge>
</Group> </Group>
@@ -100,14 +110,18 @@ const BoardsPage = () => {
<Menu.Dropdown> <Menu.Dropdown>
<Menu.Item <Menu.Item
onClick={async () => { onClick={async () => {
append(board); modals.openContextModal({
// give user feedback, that it's being deleted modal: 'deleteBoardModal',
await sleep(500); title: <Text weight={500}>Delete board</Text>,
deletionMutationAsync({ innerProps: {
name: board, boardName: board,
}).finally(async () => { onConfirm: async () => {
await sleep(500); append(board);
filter((item, _) => item !== board); // give user feedback, that it's being deleted
await sleep(500);
filter((item, _) => item !== board);
},
},
}); });
}} }}
icon={<IconTrash size="1rem" />} icon={<IconTrash size="1rem" />}