diff --git a/apps/api/src/changelog/version/create.ts b/apps/api/src/changelog/version/create.ts index 88f031a..546e7a5 100644 --- a/apps/api/src/changelog/version/create.ts +++ b/apps/api/src/changelog/version/create.ts @@ -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 } diff --git a/apps/app/src/components/Changelog/CommitList.tsx b/apps/app/src/components/Changelog/CommitList.tsx new file mode 100644 index 0000000..a6f2c39 --- /dev/null +++ b/apps/app/src/components/Changelog/CommitList.tsx @@ -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 ( + + +
+ Commits ({data.length}) +
+
+ +
+ {data?.map((commit) => { + return ( +
+ + {commit.commit} + +

{commit.subject}

+ + + {format(new Date(commit.author.date), 'dd.MM.yyyy')} + +
+ ) + })} +
+
+
+ ) + } + + return
Not found
+} diff --git a/apps/app/src/components/Changelog/Version/Create/Step01.tsx b/apps/app/src/components/Changelog/Version/Create/Step01.tsx new file mode 100644 index 0000000..c6d30fc --- /dev/null +++ b/apps/app/src/components/Changelog/Version/Create/Step01.tsx @@ -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 ( +
+
+ + Automatic + + Coming soon + +
+ + + Manual + +
+ ) +} diff --git a/apps/app/src/components/Changelog/Version/Create/index.tsx b/apps/app/src/components/Changelog/Version/Create/index.tsx new file mode 100644 index 0000000..466de35 --- /dev/null +++ b/apps/app/src/components/Changelog/Version/Create/index.tsx @@ -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 ( + + + + + + + How would you like to create your version? + + 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. + + + + + + + ) +} diff --git a/apps/app/src/components/Changelog/VersionList.tsx b/apps/app/src/components/Changelog/VersionList.tsx new file mode 100644 index 0000000..049f36a --- /dev/null +++ b/apps/app/src/components/Changelog/VersionList.tsx @@ -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 ( + + +
+ Versions ({data.versions?.length}) + + +
+
+ +
+ {data.versions?.map((version) => { + return ( + + + {version.version} + + ) + })} +
+
+
+ ) + } + + return
Not found
+} diff --git a/apps/app/src/hooks/useChangelog.ts b/apps/app/src/hooks/useChangelog.ts index d2a8bee..86757c4 100644 --- a/apps/app/src/hooks/useChangelog.ts +++ b/apps/app/src/hooks/useChangelog.ts @@ -2,6 +2,7 @@ import type { ChangelogCreateInput, ChangelogOutput, ChangelogUpdateInput, + CommitOutput, VersionCreateInput, VersionOutput, VersionUpdateInput, @@ -19,6 +20,8 @@ type Version = z.infer type VersionCreate = z.infer type VersionUpdate = z.infer +type Commit = z.infer + 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> => + 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() diff --git a/apps/app/src/routes/changelog.$id.index.lazy.tsx b/apps/app/src/routes/changelog.$id.index.lazy.tsx index 065749c..a8e1e5b 100644 --- a/apps/app/src/routes/changelog.$id.index.lazy.tsx +++ b/apps/app/src/routes/changelog.$id.index.lazy.tsx @@ -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 ( -
- {!isPending && data && ( -
- - -
- Versions ({data.versions?.length}) - - - - -
-
- -
- {data.versions?.map((version) => { - return ( - - - {version.version} - - ) - })} -
-
-
-
- )} +
+ +
) } diff --git a/apps/app/src/routes/changelog.$id.versionCreate.lazy.tsx b/apps/app/src/routes/changelog.$id.versionCreate.lazy.tsx index 66b9375..a620fae 100644 --- a/apps/app/src/routes/changelog.$id.versionCreate.lazy.tsx +++ b/apps/app/src/routes/changelog.$id.versionCreate.lazy.tsx @@ -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>({ 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) => { versionCreate.mutate(values, { onSuccess(data) { @@ -66,149 +87,213 @@ const Component = () => { } return ( -
- -

New version

-
- - ( - - Version - - - {' '} - - - )} - /> - - ( - - Notes - - ( - <> - - - - - - ), - }), - ]} - {...field} - /> - {' '} - - - )} - /> - -
- ( - - Status - - - - )} - /> + + {' '} + + + )} + /> - ( - - Released at - - - - - - - - field.onChange(date)} - weekStartsOn={1} +
+ ( + + Status + + + + )} + /> + + ( + + Released at + + + + + + + + field.onChange(date)} + weekStartsOn={1} + /> + + + + + )} + /> +
+ + ( + + Changes + + ( + <> + + + + + + ), + }), + ]} + {...field} /> -
-
- -
- )} - /> -
+ {' '} + + + )} + /> + + -
+ + +
+ Associated commits + + +
+
+ + +
+ {changelogCommit.data?.map((commit) => { + return ( + { + return ( + + + { + 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, + ]) + }} + /> + +
+ {commit.subject} +
+
+ ) + }} + /> + ) + })} +
+
+
+
+
- - -
+
+ + ) } diff --git a/bruno/Changelog/Commit/Create.bru b/bruno/Changelog/Commit/Create.bru index c64df3c..f497622 100644 --- a/bruno/Changelog/Commit/Create.bru +++ b/bruno/Changelog/Commit/Create.bru @@ -11,7 +11,7 @@ post { } auth:bearer { - token: abc123 + token: asd123 } body:json { diff --git a/bruno/Changelog/Commit/List.bru b/bruno/Changelog/Commit/List.bru new file mode 100644 index 0000000..f2ae5f1 --- /dev/null +++ b/bruno/Changelog/Commit/List.bru @@ -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": "" + } + ] +} diff --git a/bun.lockb b/bun.lockb index 898f196..08eb98c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/schema/src/version/create.ts b/packages/schema/src/version/create.ts index 1c2c35a..2256a4d 100644 --- a/packages/schema/src/version/create.ts +++ b/packages/schema/src/version/create.ts @@ -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'], diff --git a/packages/ui/package.json b/packages/ui/package.json index 9db2fad..fd16f79 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -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", diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 0a5d79c..6c573fa 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -28,3 +28,4 @@ export * from './global.css' export * from './breadcrumb' export * from './command' export * from './dialog' +export * from './scroll-area' diff --git a/packages/ui/src/scroll-area.tsx b/packages/ui/src/scroll-area.tsx new file mode 100644 index 0000000..698976a --- /dev/null +++ b/packages/ui/src/scroll-area.tsx @@ -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, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = 'vertical', ...props }, ref) => ( + + + +)) +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName + +export { ScrollArea, ScrollBar }