feat: add version create with commit select
This commit is contained in:
parent
5467e78596
commit
625b463287
apps
api/src/changelog/version
app/src
components/Changelog
hooks
routes
bruno/Changelog/Commit
bun.lockbpackages
@ -1,7 +1,12 @@
|
||||
import { changelog, changelog_version, db } from '@boring.tools/database'
|
||||
import {
|
||||
changelog,
|
||||
changelog_commit,
|
||||
changelog_version,
|
||||
db,
|
||||
} from '@boring.tools/database'
|
||||
import { VersionCreateInput, VersionCreateOutput } from '@boring.tools/schema'
|
||||
import { createRoute, type z } from '@hono/zod-openapi'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { and, eq, inArray } from 'drizzle-orm'
|
||||
import { HTTPException } from 'hono/http-exception'
|
||||
import semver from 'semver'
|
||||
|
||||
@ -84,5 +89,10 @@ export const createFunc = async ({
|
||||
})
|
||||
.returning()
|
||||
|
||||
await db
|
||||
.update(changelog_commit)
|
||||
.set({ versionId: versionCreateResult.id })
|
||||
.where(inArray(changelog_commit.id, payload.commitIds))
|
||||
|
||||
return versionCreateResult
|
||||
}
|
||||
|
44
apps/app/src/components/Changelog/CommitList.tsx
Normal file
44
apps/app/src/components/Changelog/CommitList.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@boring.tools/ui'
|
||||
import { useParams } from '@tanstack/react-router'
|
||||
import { format } from 'date-fns'
|
||||
import { useChangelogCommitList } from '../../hooks/useChangelog'
|
||||
|
||||
export const ChangelogCommitList = () => {
|
||||
const { id } = useParams({ from: '/changelog/$id' })
|
||||
const { data } = useChangelogCommitList({ id, limit: 50 })
|
||||
|
||||
if (data) {
|
||||
return (
|
||||
<Card className="w-full max-w-screen-sm">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle>Commits ({data.length})</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-1">
|
||||
{data?.map((commit) => {
|
||||
return (
|
||||
<div
|
||||
className="hover:bg-muted py-1 px-2 rounded transition flex gap-2 items-center"
|
||||
key={commit.id}
|
||||
>
|
||||
<span className="font-mono font-bold text-muted-foreground">
|
||||
{commit.commit}
|
||||
</span>
|
||||
<p className="w-full">{commit.subject}</p>
|
||||
|
||||
<span className="text-xs">
|
||||
{format(new Date(commit.author.date), 'dd.MM.yyyy')}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
return <div className="flex flex-col gap-5">Not found </div>
|
||||
}
|
25
apps/app/src/components/Changelog/Version/Create/Step01.tsx
Normal file
25
apps/app/src/components/Changelog/Version/Create/Step01.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Link, useParams } from '@tanstack/react-router'
|
||||
import { HandIcon, WorkflowIcon } from 'lucide-react'
|
||||
|
||||
export const ChangelogVersionCreateStep01 = () => {
|
||||
const { id } = useParams({ from: '/changelog/$id' })
|
||||
return (
|
||||
<div className="flex gap-10 mt-3">
|
||||
<div className="border rounded border-muted p-5 flex items-center justify-center w-full flex-col">
|
||||
<WorkflowIcon />
|
||||
Automatic
|
||||
<small className="uppercase text-muted-foreground text-xs">
|
||||
Coming soon
|
||||
</small>
|
||||
</div>
|
||||
<Link
|
||||
className="flex-col hover:border-accent border rounded border-muted p-5 flex items-center justify-center w-full"
|
||||
to="/changelog/$id/versionCreate"
|
||||
params={{ id }}
|
||||
>
|
||||
<HandIcon />
|
||||
Manual
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
32
apps/app/src/components/Changelog/Version/Create/index.tsx
Normal file
32
apps/app/src/components/Changelog/Version/Create/index.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@boring.tools/ui'
|
||||
import { PlusCircleIcon } from 'lucide-react'
|
||||
import { ChangelogVersionCreateStep01 } from './Step01'
|
||||
|
||||
export const ChangelogVersionCreate = () => {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<PlusCircleIcon />
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>How would you like to create your version?</DialogTitle>
|
||||
<DialogDescription>
|
||||
You can create your version manually. You have to make every entry
|
||||
yourself. However, if you want to create your changelog attachment
|
||||
from your commit messages, select the automatic option.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<ChangelogVersionCreateStep01 />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
46
apps/app/src/components/Changelog/VersionList.tsx
Normal file
46
apps/app/src/components/Changelog/VersionList.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@boring.tools/ui'
|
||||
import { Link, useParams } from '@tanstack/react-router'
|
||||
import { useChangelogById } from '../../hooks/useChangelog'
|
||||
import { ChangelogVersionCreate } from './Version/Create'
|
||||
import { VersionStatus } from './VersionStatus'
|
||||
|
||||
export const ChangelogVersionList = () => {
|
||||
const { id } = useParams({ from: '/changelog/$id' })
|
||||
const { data } = useChangelogById({ id })
|
||||
|
||||
if (data) {
|
||||
return (
|
||||
<Card className="w-full max-w-screen-sm">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle>Versions ({data.versions?.length})</CardTitle>
|
||||
|
||||
<ChangelogVersionCreate />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-1">
|
||||
{data.versions?.map((version) => {
|
||||
return (
|
||||
<Link
|
||||
className="hover:bg-muted py-1 px-2 rounded transition flex gap-2 items-center"
|
||||
to="/changelog/$id/version/$versionId"
|
||||
params={{
|
||||
id,
|
||||
versionId: version.id,
|
||||
}}
|
||||
key={version.id}
|
||||
>
|
||||
<VersionStatus status={version.status} />
|
||||
{version.version}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
return <div className="flex flex-col gap-5">Not found</div>
|
||||
}
|
@ -2,6 +2,7 @@ import type {
|
||||
ChangelogCreateInput,
|
||||
ChangelogOutput,
|
||||
ChangelogUpdateInput,
|
||||
CommitOutput,
|
||||
VersionCreateInput,
|
||||
VersionOutput,
|
||||
VersionUpdateInput,
|
||||
@ -19,6 +20,8 @@ type Version = z.infer<typeof VersionOutput>
|
||||
type VersionCreate = z.infer<typeof VersionCreateInput>
|
||||
type VersionUpdate = z.infer<typeof VersionUpdateInput>
|
||||
|
||||
type Commit = z.infer<typeof CommitOutput>
|
||||
|
||||
export const useChangelogList = () => {
|
||||
const { getToken } = useAuth()
|
||||
return useQuery({
|
||||
@ -32,6 +35,23 @@ export const useChangelogList = () => {
|
||||
})
|
||||
}
|
||||
|
||||
export const useChangelogCommitList = ({
|
||||
id,
|
||||
limit,
|
||||
offset,
|
||||
}: { id: string; limit?: number; offset?: number }) => {
|
||||
const { getToken } = useAuth()
|
||||
return useQuery({
|
||||
queryKey: ['changelogCommitList'],
|
||||
queryFn: async (): Promise<ReadonlyArray<Commit>> =>
|
||||
await queryFetch({
|
||||
path: `changelog/commit?changelogId=${id}&limit=${limit}&offset=${offset}`,
|
||||
method: 'get',
|
||||
token: await getToken(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
export const useChangelogById = ({ id }: { id: string }) => {
|
||||
const { getToken } = useAuth()
|
||||
|
||||
|
@ -1,58 +1,12 @@
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} 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'
|
||||
import { createLazyFileRoute } from '@tanstack/react-router'
|
||||
import { ChangelogCommitList } from '../components/Changelog/CommitList'
|
||||
import { ChangelogVersionList } from '../components/Changelog/VersionList'
|
||||
|
||||
const Component = () => {
|
||||
const { id } = Route.useParams()
|
||||
const { data, isPending } = useChangelogById({ id })
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
{!isPending && data && (
|
||||
<div>
|
||||
<Card className="w-full max-w-screen-sm">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle>Versions ({data.versions?.length})</CardTitle>
|
||||
|
||||
<Link to="/changelog/$id/versionCreate" params={{ id }}>
|
||||
<Button variant={'ghost'} size={'icon'}>
|
||||
<PlusCircleIcon strokeWidth={1.5} className="w-5 h-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-1">
|
||||
{data.versions?.map((version) => {
|
||||
return (
|
||||
<Link
|
||||
className="hover:bg-muted py-1 px-2 rounded transition flex gap-2 items-center"
|
||||
to="/changelog/$id/version/$versionId"
|
||||
params={{
|
||||
id,
|
||||
versionId: version.id,
|
||||
}}
|
||||
key={version.id}
|
||||
>
|
||||
<VersionStatus status={version.status} />
|
||||
{version.version}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex gap-5 flex-wrap">
|
||||
<ChangelogVersionList />
|
||||
<ChangelogCommitList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -2,8 +2,14 @@ import { VersionCreateInput } from '@boring.tools/schema'
|
||||
import {
|
||||
Button,
|
||||
Calendar,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Checkbox,
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
@ -12,6 +18,7 @@ import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
ScrollArea,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
@ -37,7 +44,10 @@ import { createLazyFileRoute } from '@tanstack/react-router'
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import type { z } from 'zod'
|
||||
import { useChangelogVersionCreate } from '../hooks/useChangelog'
|
||||
import {
|
||||
useChangelogCommitList,
|
||||
useChangelogVersionCreate,
|
||||
} from '../hooks/useChangelog'
|
||||
import '@mdxeditor/editor/style.css'
|
||||
import { format } from 'date-fns'
|
||||
import { CalendarIcon } from 'lucide-react'
|
||||
@ -46,7 +56,9 @@ import { VersionStatus } from '../components/Changelog/VersionStatus'
|
||||
const Component = () => {
|
||||
const { id } = Route.useParams()
|
||||
const navigate = useNavigate({ from: `/changelog/${id}/versionCreate` })
|
||||
const changelogCommit = useChangelogCommitList({ id })
|
||||
const versionCreate = useChangelogVersionCreate()
|
||||
const { data } = useChangelogCommitList({ id })
|
||||
const form = useForm<z.infer<typeof VersionCreateInput>>({
|
||||
resolver: zodResolver(VersionCreateInput),
|
||||
defaultValues: {
|
||||
@ -54,9 +66,18 @@ const Component = () => {
|
||||
version: '',
|
||||
markdown: '',
|
||||
status: 'draft',
|
||||
commitIds: [],
|
||||
},
|
||||
})
|
||||
|
||||
const selectAllCommits = () => {
|
||||
const commitIds = data?.map((commit) => commit.id)
|
||||
if (!commitIds) {
|
||||
return form.setValue('commitIds', [])
|
||||
}
|
||||
form.setValue('commitIds', commitIds)
|
||||
}
|
||||
|
||||
const onSubmit = (values: z.infer<typeof VersionCreateInput>) => {
|
||||
versionCreate.mutate(values, {
|
||||
onSuccess(data) {
|
||||
@ -66,149 +87,213 @@ const Component = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
<Separator />
|
||||
<h1 className="text-xl mb-2">New version</h1>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-8 max-w-screen-md"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="version"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Version</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="v1.0.1" {...field} autoFocus />
|
||||
</FormControl>{' '}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<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={field.value}
|
||||
plugins={[
|
||||
headingsPlugin(),
|
||||
listsPlugin(),
|
||||
thematicBreakPlugin(),
|
||||
quotePlugin(),
|
||||
|
||||
toolbarPlugin({
|
||||
toolbarContents: () => (
|
||||
<>
|
||||
<BlockTypeSelect />
|
||||
<BoldItalicUnderlineToggles />
|
||||
<ListsToggle />
|
||||
<UndoRedo />
|
||||
</>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
{...field}
|
||||
/>
|
||||
</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}
|
||||
>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 w-full">
|
||||
<h1 className="text-2xl">New version</h1>
|
||||
<div className="grid grid-cols-6 gap-5 w-full max-w-screen-xl">
|
||||
<Card className="col-span-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Details</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-5">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="version"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Version</FormLabel>
|
||||
<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>
|
||||
)}
|
||||
/>
|
||||
<Input placeholder="v1.0.1" {...field} autoFocus />
|
||||
</FormControl>{' '}
|
||||
<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}
|
||||
<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>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="markdown"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Changes</FormLabel>
|
||||
<FormControl>
|
||||
<MDXEditor
|
||||
className="dark-theme h-56"
|
||||
contentEditableClassName="prose dark:prose-invert max-w-none"
|
||||
markdown={field.value}
|
||||
plugins={[
|
||||
headingsPlugin(),
|
||||
listsPlugin(),
|
||||
thematicBreakPlugin(),
|
||||
quotePlugin(),
|
||||
toolbarPlugin({
|
||||
toolbarContents: () => (
|
||||
<>
|
||||
<BlockTypeSelect />
|
||||
<BoldItalicUnderlineToggles />
|
||||
<ListsToggle />
|
||||
<UndoRedo />
|
||||
</>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
{...field}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>{' '}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex gap-5">
|
||||
<Card className="col-span-2">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle>Associated commits</CardTitle>
|
||||
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
size={'sm'}
|
||||
onClick={selectAllCommits}
|
||||
>
|
||||
Add all commits
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="w-full h-[350px]">
|
||||
<div className="flex flex-col gap-2">
|
||||
{changelogCommit.data?.map((commit) => {
|
||||
return (
|
||||
<FormField
|
||||
key={commit.id}
|
||||
control={form.control}
|
||||
name={'commitIds'}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md ">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
// checked={field.value}
|
||||
value={commit.id}
|
||||
checked={field.value?.includes(commit.id)}
|
||||
onCheckedChange={() => {
|
||||
const exist = field.value.includes(
|
||||
commit.id,
|
||||
)
|
||||
if (exist) {
|
||||
return field.onChange(
|
||||
field.value.filter(
|
||||
(value) => value !== commit.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
return field.onChange([
|
||||
...field.value,
|
||||
commit.id,
|
||||
])
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel>{commit.subject}</FormLabel>
|
||||
</div>
|
||||
</FormItem>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex gap-5 mt-5 w-full justify-end items-end col-span-6">
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
@ -218,9 +303,9 @@ const Component = () => {
|
||||
</Button>
|
||||
<Button type="submit">Create</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ post {
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: abc123
|
||||
token: asd123
|
||||
}
|
||||
|
||||
body:json {
|
||||
|
37
bruno/Changelog/Commit/List.bru
Normal file
37
bruno/Changelog/Commit/List.bru
Normal file
@ -0,0 +1,37 @@
|
||||
meta {
|
||||
name: List
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{API_URL}}/v1/changelog/commit?changelogId=d83fe688-3331-4e64-9af6-318f82e511d4&limit=1
|
||||
body: none
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
params:query {
|
||||
changelogId: d83fe688-3331-4e64-9af6-318f82e511d4
|
||||
limit: 1
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: asd123
|
||||
}
|
||||
|
||||
body:json {
|
||||
[
|
||||
{
|
||||
"changelogId": "6a14f436-6596-474b-b615-f6e923582c1b",
|
||||
"commit": "abc123",
|
||||
"parent": "abc122",
|
||||
"subject": "some",
|
||||
"author": {
|
||||
"name": "asd",
|
||||
"email": "hello@hashdot.co",
|
||||
"date": "somedate"
|
||||
},
|
||||
"body": ""
|
||||
}
|
||||
]
|
||||
}
|
@ -9,6 +9,7 @@ export const VersionCreateInput = z
|
||||
releasedAt: z.date().or(z.string()).optional(),
|
||||
status: z.enum(['draft', 'review', 'published']).default('draft'),
|
||||
markdown: z.string(),
|
||||
commitIds: z.array(z.string()),
|
||||
})
|
||||
.openapi({
|
||||
required: ['changelogId', 'version', 'markdown', 'releasedAt'],
|
||||
|
@ -15,6 +15,7 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
|
@ -28,3 +28,4 @@ export * from './global.css'
|
||||
export * from './breadcrumb'
|
||||
export * from './command'
|
||||
export * from './dialog'
|
||||
export * from './scroll-area'
|
||||
|
46
packages/ui/src/scroll-area.tsx
Normal file
46
packages/ui/src/scroll-area.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from './lib/cn'
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn('relative overflow-hidden', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = 'vertical', ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
'flex touch-none select-none transition-colors',
|
||||
orientation === 'vertical' &&
|
||||
'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
||||
orientation === 'horizontal' &&
|
||||
'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
Loading…
Reference in New Issue
Block a user