boring.tools-poc/apps/app/src/routes/changelog.$id.version.$versionId.tsx
Lars Hampe 99c3452a5f
All checks were successful
Build and Push Docker Image / tests (push) Successful in 38s
Build and Push Docker Image / build (push) Successful in 2m26s
feat(app): use new sidebar navigation
2024-10-20 09:43:29 +02:00

249 lines
8.2 KiB
TypeScript

import { VersionUpdateInput } from '@boring.tools/schema'
import {
Button,
Calendar,
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
Popover,
PopoverContent,
PopoverTrigger,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
cn,
} from '@boring.tools/ui'
import { zodResolver } from '@hookform/resolvers/zod'
import {
BlockTypeSelect,
BoldItalicUnderlineToggles,
ListsToggle,
MDXEditor,
type MDXEditorMethods,
UndoRedo,
headingsPlugin,
listsPlugin,
quotePlugin,
thematicBreakPlugin,
toolbarPlugin,
} from '@mdxeditor/editor'
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useForm } from 'react-hook-form'
import type { z } from 'zod'
import {
useChangelogVersionById,
useChangelogVersionUpdate,
} from '../hooks/useChangelog'
import '@mdxeditor/editor/style.css'
import { format } from 'date-fns'
import { CalendarIcon } from 'lucide-react'
import { useEffect, useRef } from 'react'
import { ChangelogVersionDelete } from '../components/Changelog/VersionDelete'
import { VersionStatus } from '../components/Changelog/VersionStatus'
const Component = () => {
const { id, versionId } = Route.useParams()
const mdxEditorRef = useRef<MDXEditorMethods>(null)
const navigate = useNavigate({ from: `/changelog/${id}/versionCreate` })
const versionUpdate = useChangelogVersionUpdate()
const { data, error, isPending, refetch } = useChangelogVersionById({
id: versionId,
})
const form = useForm<z.infer<typeof VersionUpdateInput>>({
resolver: zodResolver(VersionUpdateInput),
defaultValues: data,
})
const onSubmit = (values: z.infer<typeof VersionUpdateInput>) => {
versionUpdate.mutate(
{ id: versionId, payload: values },
{
onSuccess() {
navigate({ to: '/changelog/$id', params: { id } })
},
},
)
}
useEffect(() => {
if (data) {
mdxEditorRef.current?.setMarkdown(data.markdown)
form.reset(data)
}
}, [data, form.reset])
if (error) {
return (
<div className="flex items-center justify-center mt-32 flex-col">
<h1 className="text-3xl">Changelogs</h1>
<p>Please try again later</p>
<Button onClick={() => refetch()}>Retry</Button>
</div>
)
}
return (
<div className="flex flex-col gap-5">
<hr />
{!isPending && data && (
<div>
<h1 className="text-xl mb-2">Version: {data.version}</h1>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 max-w-screen-md"
>
<FormField
control={form.control}
name="markdown"
render={({ field }) => (
<FormItem>
<FormLabel>Notes</FormLabel>
<FormControl>
<MDXEditor
className="dark-theme"
contentEditableClassName="prose dark:prose-invert max-w-none"
markdown={''}
ref={mdxEditorRef}
onChange={field.onChange}
onBlur={field.onBlur}
plugins={[
headingsPlugin(),
listsPlugin(),
thematicBreakPlugin(),
quotePlugin(),
toolbarPlugin({
toolbarContents: () => (
<>
<BlockTypeSelect />
<BoldItalicUnderlineToggles />
<ListsToggle />
<UndoRedo />
</>
),
}),
]}
/>
</FormControl>{' '}
<FormMessage />
</FormItem>
)}
/>
<div className="flex gap-5 items-center">
<FormField
control={form.control}
name="status"
render={({ field }) => (
<FormItem>
<FormLabel>Status</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select your version status" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="draft">
<div className="flex gap-2 items-center">
<VersionStatus status={'draft'} />
<span>Draft</span>
</div>
</SelectItem>
<SelectItem value="review">
<div className="flex gap-2 items-center">
<VersionStatus status={'review'} />
<span>Review</span>
</div>
</SelectItem>
<SelectItem value="published">
<div className="flex gap-2 items-center">
<VersionStatus status={'published'} />
<span>Published</span>
</div>
</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="releasedAt"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel className="mb-2">Released at</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={'outline'}
size={'lg'}
className={cn(
'w-[240px] pl-3 text-left font-normal',
!field.value && 'text-muted-foreground',
)}
>
{field.value ? (
format(field.value, 'PPP')
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value as Date}
onSelect={(date) => field.onChange(date)}
weekStartsOn={1}
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex gap-5">
<Button
type="button"
variant={'ghost'}
onClick={() =>
navigate({ to: '/changelog/$id', params: { id } })
}
>
Cancel
</Button>
<Button type="submit">Update</Button>
</div>
</form>
</Form>
<ChangelogVersionDelete id={id} versionId={versionId} />
</div>
)}
</div>
)
}
export const Route = createFileRoute('/changelog/$id/version/$versionId')({
component: Component,
})