121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import { Button } from '@app/client/components/ui/button';
|
|
import { Box, Input, Stack, Textarea } from '@chakra-ui/react';
|
|
import { valibotResolver } from '@hookform/resolvers/valibot';
|
|
import { Link } from '@tanstack/react-router';
|
|
import { useState } from 'react';
|
|
import { useForm } from 'react-hook-form';
|
|
import { Descendant } from 'slate';
|
|
import * as v from 'valibot';
|
|
import { EditorSlate } from './EditorSlate';
|
|
import { Field } from './ui/field';
|
|
|
|
interface BookChapterEditInitialValues {
|
|
readonly title: string;
|
|
readonly foreword?: string;
|
|
readonly footnote?: string;
|
|
readonly content?: Descendant[];
|
|
}
|
|
|
|
interface BookChapterInput {
|
|
readonly title: string;
|
|
readonly foreword?: string;
|
|
readonly footnote?: string;
|
|
readonly content: Descendant[];
|
|
}
|
|
|
|
interface BookChapterFormValues {
|
|
readonly title: string;
|
|
readonly foreword: string;
|
|
readonly footnote: string;
|
|
}
|
|
|
|
export function BookChapterForm({
|
|
bookId,
|
|
initialValues,
|
|
onChapterSubmit
|
|
}: {
|
|
readonly bookId: string;
|
|
readonly initialValues?: BookChapterEditInitialValues | null;
|
|
readonly onChapterSubmit: (chapter: BookChapterInput) => Promise<void>;
|
|
}) {
|
|
const [content, setContent] = useState<Descendant[]>([]);
|
|
|
|
const schema = v.pipe(
|
|
v.object({
|
|
title: v.pipe(v.string(), v.nonEmpty('Title is required'), v.maxLength(60)),
|
|
foreword: v.pipe(v.string()),
|
|
footnote: v.pipe(v.string())
|
|
})
|
|
);
|
|
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
formState: { errors }
|
|
} = useForm<BookChapterFormValues>({
|
|
defaultValues: {
|
|
title: initialValues?.title,
|
|
foreword: initialValues?.foreword,
|
|
footnote: initialValues?.footnote
|
|
},
|
|
resolver: valibotResolver(schema)
|
|
});
|
|
|
|
const onSubmit = handleSubmit(async values =>
|
|
onChapterSubmit({
|
|
title: values.title,
|
|
foreword: values.foreword,
|
|
footnote: values.footnote,
|
|
content
|
|
})
|
|
);
|
|
|
|
return (
|
|
<form onSubmit={onSubmit}>
|
|
<Stack justifyContent="center" alignItems="center" minHeight="88vh">
|
|
<Stack maxWidth="840px" minHeight="80vh" justifyContent="center" alignItems="center">
|
|
<Stack width="100%" direction="row" justifyContent="space-between" alignItems="start">
|
|
<Box>
|
|
<Field invalid={errors.title != null} errorText={errors.title?.message}>
|
|
<Input {...register('title')} placeholder="Chapter title" />
|
|
</Field>
|
|
</Box>
|
|
<Stack direction="row">
|
|
<Button onClick={async () => onSubmit()}>Submit</Button>
|
|
<Link to={`/work/book/${bookId}/overview/contents`}>
|
|
<Button>Go back</Button>
|
|
</Link>
|
|
</Stack>
|
|
</Stack>
|
|
|
|
<Field invalid={errors.foreword != null} errorText={errors.foreword?.message}>
|
|
<Textarea
|
|
color="fg.muted"
|
|
variant="subtle"
|
|
placeholder="Foreword"
|
|
resize="none"
|
|
maxLength={500}
|
|
{...register('foreword')}
|
|
/>
|
|
</Field>
|
|
|
|
<EditorSlate
|
|
initialValue={initialValues?.content}
|
|
onChange={values => setContent(values)}
|
|
/>
|
|
|
|
<Field invalid={errors.footnote != null} errorText={errors.footnote?.message}>
|
|
<Textarea
|
|
color="fg.muted"
|
|
variant="subtle"
|
|
placeholder="Footnote"
|
|
resize="none"
|
|
maxLength={500}
|
|
{...register('footnote')}
|
|
/>
|
|
</Field>
|
|
</Stack>
|
|
</Stack>
|
|
</form>
|
|
);
|
|
}
|