mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-16 04:22:31 +01:00
Change the board creation modal to an inline form
This commit is contained in:
@@ -24,7 +24,8 @@
|
||||
"test:coverage": "SKIP_ENV_VALIDATION=1 vitest run --coverage",
|
||||
"docker:build": "turbo build && docker build . -t homarr:local-dev",
|
||||
"docker:start": "docker run -p 7575:7575 --name homarr-development homarr:local-dev",
|
||||
"db:migrate": "dotenv ts-node drizzle/migrate/migrate.ts ./drizzle"
|
||||
"db:migrate": "dotenv ts-node drizzle/migrate/migrate.ts ./drizzle",
|
||||
"db:create": "dotenv drizzle-kit push:sqlite"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/drizzle-adapter": "^0.3.2",
|
||||
@@ -234,4 +235,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ActionIcon, Button, Group, Loader, Stack, Switch, TextInput } from '@mantine/core';
|
||||
import { useForm, zodResolver } from '@mantine/form';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { ContextModalProps } from '@mantine/modals';
|
||||
import { ContextModalProps, closeModal } from '@mantine/modals';
|
||||
import { IconAlertTriangle, IconCheck, IconPencil, IconPencilOff } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
@@ -9,7 +9,7 @@ import { z } from 'zod';
|
||||
import { api } from '~/utils/api';
|
||||
import { createBoardSchema } from '~/validations/boards';
|
||||
|
||||
export const CreateBoardModal = ({ id, context }: ContextModalProps<Record<string, never>>) => {
|
||||
export const CreateBoardModal = ({ id }: { id: string }) => {
|
||||
const [autoBoardName, setAutoBoardName] = useState(true);
|
||||
const router = useRouter();
|
||||
const form = useForm<FormType>({
|
||||
@@ -34,7 +34,7 @@ export const CreateBoardModal = ({ id, context }: ContextModalProps<Record<strin
|
||||
createBoard(values, {
|
||||
onSuccess: async () => {
|
||||
await router.push(`/board/${values.boardName}`);
|
||||
context.closeModal(id);
|
||||
closeModal(id);
|
||||
utils.boards.checkNameAvailable.invalidate({ boardName: values.boardName });
|
||||
},
|
||||
});
|
||||
@@ -43,55 +43,60 @@ export const CreateBoardModal = ({ id, context }: ContextModalProps<Record<strin
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label="Page Title"
|
||||
required
|
||||
{...form.getInputProps('pageTitle')}
|
||||
onChange={(e) => {
|
||||
form.getInputProps('pageTitle').onChange(e);
|
||||
if (!autoBoardName) return;
|
||||
form.setFieldValue(
|
||||
'boardName',
|
||||
e.currentTarget.value
|
||||
.replace(/([A-z0-9])[^A-z0-9]+([A-z0-9])/g, '$1-$2')
|
||||
.replace(/([A-z0-9])[^A-z0-9\-]+([A-z0-9])/g, '$1-$2')
|
||||
.replace(/[^A-z\-0-9]+/g, '')
|
||||
.replace(/-+/g, '-')
|
||||
.toLowerCase()
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Routename"
|
||||
required
|
||||
{...form.getInputProps('boardName')}
|
||||
readOnly={autoBoardName}
|
||||
rightSection={
|
||||
<ActionIcon variant="transparent" onClick={() => setAutoBoardName((c) => !c)}>
|
||||
{autoBoardName ? (
|
||||
<IconPencil size="1.25rem" stroke={1.5} />
|
||||
) : (
|
||||
<IconPencilOff size="1.25rem" stroke={1.5} />
|
||||
)}
|
||||
</ActionIcon>
|
||||
}
|
||||
icon={
|
||||
debouncedRouteName !== form.values.boardName || isFetching ? (
|
||||
<Loader size="xs" />
|
||||
) : boardNameAvailable === true ? (
|
||||
<IconCheck size="1.25rem" stroke={1.5} color="green" />
|
||||
) : boardNameAvailable === false ? (
|
||||
<IconAlertTriangle size="1.25rem" stroke={1.5} color="red" />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
<Group grow>
|
||||
<TextInput
|
||||
// TODO: Add translations
|
||||
label="Page Title"
|
||||
description="The title of the page as it shows on your browser tab"
|
||||
required
|
||||
{...form.getInputProps('pageTitle')}
|
||||
onChange={(e) => {
|
||||
form.getInputProps('pageTitle').onChange(e);
|
||||
if (!autoBoardName) return;
|
||||
form.setFieldValue(
|
||||
'boardName',
|
||||
e.currentTarget.value
|
||||
.replace(/([A-z0-9])[^A-z0-9]+([A-z0-9])/g, '$1-$2')
|
||||
.replace(/([A-z0-9])[^A-z0-9\-]+([A-z0-9])/g, '$1-$2')
|
||||
.replace(/[^A-z\-0-9]+/g, '')
|
||||
.replace(/-+/g, '-')
|
||||
.toLowerCase()
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Routename"
|
||||
description="The name that will be used in the URL to access your board"
|
||||
required
|
||||
{...form.getInputProps('boardName')}
|
||||
readOnly={autoBoardName}
|
||||
rightSection={
|
||||
<ActionIcon variant="transparent" onClick={() => setAutoBoardName((c) => !c)}>
|
||||
{autoBoardName ? (
|
||||
<IconPencil size="1.25rem" stroke={1.5} />
|
||||
) : (
|
||||
<IconPencilOff size="1.25rem" stroke={1.5} />
|
||||
)}
|
||||
</ActionIcon>
|
||||
}
|
||||
icon={
|
||||
debouncedRouteName !== form.values.boardName || isFetching ? (
|
||||
<Loader size="xs" />
|
||||
) : boardNameAvailable === true ? (
|
||||
<IconCheck size="1.25rem" stroke={1.5} color="green" />
|
||||
) : boardNameAvailable === false ? (
|
||||
<IconAlertTriangle size="1.25rem" stroke={1.5} color="red" />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</Group>
|
||||
<Switch
|
||||
label="Allow Guests"
|
||||
description="Allow users that are not logged in to view your board"
|
||||
{...form.getInputProps('allowGuests')}
|
||||
/>
|
||||
<Group position="right">
|
||||
<Button type="button" color="gray" variant="subtle">
|
||||
<Button type="button" color="gray" variant="subtle" onClick={() => closeModal(id)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" color="teal" loading={isLoading || isSuccess}>
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useListState } from '@mantine/hooks';
|
||||
import { useDisclosure, useListState } from '@mantine/hooks';
|
||||
import {
|
||||
IconBox,
|
||||
IconCategory,
|
||||
@@ -30,7 +30,7 @@ import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { openCreateBoardModal } from '~/components/Manage/Board/create-board.modal';
|
||||
import { CreateBoardModal } from '~/components/Board/BoardCreateModal';
|
||||
import { openDeleteBoardModal } from '~/components/Manage/Board/delete-board.modal';
|
||||
import { ManageLayout } from '~/components/layout/Templates/ManageLayout';
|
||||
import { boardRouter } from '~/server/api/routers/board';
|
||||
@@ -59,6 +59,7 @@ const BoardsPage = ({
|
||||
const [deletingDashboards, { append, filter }] = useListState<string>([]);
|
||||
|
||||
const { t } = useTranslation('manage/boards');
|
||||
const [opened, handlers] = useDisclosure(false);
|
||||
|
||||
const metaTitle = `${t('metaTitle')} • Homarr`;
|
||||
|
||||
@@ -71,15 +72,14 @@ const BoardsPage = ({
|
||||
<Group position="apart">
|
||||
<Title mb="xl">{t('pageTitle')}</Title>
|
||||
{session?.user.isAdmin && (
|
||||
<Button
|
||||
onClick={openCreateBoardModal}
|
||||
leftIcon={<IconPlus size="1rem" />}
|
||||
variant="default"
|
||||
>
|
||||
<Button onClick={handlers.toggle} leftIcon={<IconPlus size="1rem" />} variant="default">
|
||||
{t('buttons.create')}
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
<Card display={opened ? 'block' : 'none'} mb="lg">
|
||||
<CreateBoardModal id={'none'} />
|
||||
</Card>
|
||||
|
||||
<SimpleGrid
|
||||
cols={3}
|
||||
|
||||
Reference in New Issue
Block a user