feat(app): extend version create/update with status and releasedAt date
Some checks failed
Build and Push Docker Image / tests (push) Failing after 28s
Build and Push Docker Image / build (push) Has been skipped

This commit is contained in:
Lars Hampe 2024-10-10 22:07:42 +02:00
parent 5fc2592bd5
commit 4ac4304862
4 changed files with 373 additions and 21 deletions

View File

@ -131,3 +131,43 @@ export const useChangelogVersionCreate = () => {
},
})
}
export const useChangelogVersionById = ({ id }: { id: string }) => {
const { getToken } = useAuth()
return useQuery({
queryKey: ['changelogVersionById', id],
queryFn: async (): Promise<Readonly<Version>> =>
await queryFetch({
path: `changelog/version/${id}`,
method: 'get',
token: await getToken(),
}),
})
}
export const useChangelogVersionUpdate = () => {
const { getToken } = useAuth()
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({
id,
payload,
}: {
id: string
payload: VersionUpdate
}): Promise<Readonly<Version>> =>
await queryFetch({
path: `changelog/version/${id}`,
data: payload,
method: 'put',
token: await getToken(),
}),
onSuccess: (data) => {
queryClient.invalidateQueries({
queryKey: ['changelogById', data.id],
})
},
})
}

View File

@ -7,21 +7,9 @@ import {
} from '@boring.tools/ui'
import { Link, createLazyFileRoute } from '@tanstack/react-router'
import { PlusCircleIcon } from 'lucide-react'
import { VersionStatus } from '../components/Changelog/VersionStatus'
import { useChangelogById } from '../hooks/useChangelog'
const VersionStatus = ({ status }: { status: string }) => {
switch (status) {
case 'draft':
return <div className="w-3 h-3 rounded-full bg-amber-600" />
case 'published':
return <div className="w-3 h-3 rounded-full bg-emerald-600" />
case 'review':
return <div className="w-3 h-3 rounded-full bg-sky-600" />
default:
return <div className="w-3 h-3 rounded-full bg-neutral-600" />
}
}
const Component = () => {
const { id } = Route.useParams()
const { data, isPending } = useChangelogById({ id })

View File

@ -1,10 +1,80 @@
import { Button } from '@boring.tools/ui'
import { createFileRoute } from '@tanstack/react-router'
import { useChangelogById } from '../hooks/useChangelog'
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 { VersionStatus } from '../components/Changelog/VersionStatus'
const Component = () => {
const { id } = Route.useParams()
const { data, error, isPending, refetch } = useChangelogById({ id })
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 (
@ -19,7 +89,153 @@ const Component = () => {
return (
<div className="flex flex-col gap-5">
{!isPending && data && <div>version page</div>}
<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>
</div>
)}
</div>
)
}

View File

@ -1,6 +1,7 @@
import { VersionCreateInput } from '@boring.tools/schema'
import {
Button,
Calendar,
Form,
FormControl,
FormField,
@ -8,6 +9,15 @@ import {
FormLabel,
FormMessage,
Input,
Popover,
PopoverContent,
PopoverTrigger,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
cn,
} from '@boring.tools/ui'
import { zodResolver } from '@hookform/resolvers/zod'
import {
@ -28,6 +38,9 @@ import { useForm } from 'react-hook-form'
import type { z } from 'zod'
import { useChangelogVersionCreate } from '../hooks/useChangelog'
import '@mdxeditor/editor/style.css'
import { format } from 'date-fns'
import { CalendarIcon } from 'lucide-react'
import { VersionStatus } from '../components/Changelog/VersionStatus'
const Component = () => {
const { id } = Route.useParams()
@ -39,6 +52,7 @@ const Component = () => {
changelogId: id,
version: '',
markdown: '',
status: 'draft',
},
})
@ -52,7 +66,8 @@ const Component = () => {
return (
<div className="flex flex-col gap-5">
<h1 className="text-3xl">New version</h1>
<hr />
<h1 className="text-xl mb-2">New version</h1>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
@ -108,7 +123,100 @@ const Component = () => {
)}
/>
<Button type="submit">Create</Button>
<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">Create</Button>
</div>
</form>
</Form>
</div>