feat(app): implement cli page with download and example
Some checks failed
Build and Push Docker Image / tests (push) Successful in 1m0s
Build and Push Docker Image / build (push) Failing after 1m1s

This commit is contained in:
Lars Hampe 2024-10-30 23:02:15 +01:00
parent d2d65027f7
commit d72f5c2111
10 changed files with 178 additions and 101 deletions

12
apps/app/public/apple.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="80px" height="80px" viewBox="-1.5 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Dribbble-Light-Preview" transform="translate(-102.000000, -7439.000000)" fill="#71717a">
<g id="icons" transform="translate(56.000000, 160.000000)">
<path d="M57.5708873,7282.19296 C58.2999598,7281.34797 58.7914012,7280.17098 58.6569121,7279 C57.6062792,7279.04 56.3352055,7279.67099 55.5818643,7280.51498 C54.905374,7281.26397 54.3148354,7282.46095 54.4735932,7283.60894 C55.6455696,7283.69593 56.8418148,7283.03894 57.5708873,7282.19296 M60.1989864,7289.62485 C60.2283111,7292.65181 62.9696641,7293.65879 63,7293.67179 C62.9777537,7293.74279 62.562152,7295.10677 61.5560117,7296.51675 C60.6853718,7297.73474 59.7823735,7298.94772 58.3596204,7298.97372 C56.9621472,7298.99872 56.5121648,7298.17973 54.9134635,7298.17973 C53.3157735,7298.17973 52.8162425,7298.94772 51.4935978,7298.99872 C50.1203933,7299.04772 49.0738052,7297.68074 48.197098,7296.46676 C46.4032359,7293.98379 45.0330649,7289.44985 46.8734421,7286.3899 C47.7875635,7284.87092 49.4206455,7283.90793 51.1942837,7283.88393 C52.5422083,7283.85893 53.8153044,7284.75292 54.6394294,7284.75292 C55.4635543,7284.75292 57.0106846,7283.67793 58.6366882,7283.83593 C59.3172232,7283.86293 61.2283842,7284.09893 62.4549652,7285.8199 C62.355868,7285.8789 60.1747177,7287.09489 60.1989864,7289.62485" id="apple-[#173]">
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="80px" height="80px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-60.000000, -7439.000000)" fill="#71717a">
<g transform="translate(56.000000, 160.000000)">
<path d="M13.1458647,7289.43426 C13.1508772,7291.43316 13.1568922,7294.82929 13.1619048,7297.46884 C16.7759398,7297.95757 20.3899749,7298.4613 23.997995,7299 C23.997995,7295.84873 24.002005,7292.71146 23.997995,7289.71311 C20.3809524,7289.71311 16.7649123,7289.43426 13.1458647,7289.43426 M4,7289.43526 L4,7296.22153 C6.72581454,7296.58933 9.45162907,7296.94113 12.1724311,7297.34291 C12.1774436,7294.71736 12.1704261,7292.0908 12.1704261,7289.46524 C9.44661654,7289.47024 6.72380952,7289.42627 4,7289.43526 M4,7281.84344 L4,7288.61071 C6.72581454,7288.61771 9.45162907,7288.57673 12.1774436,7288.57973 C12.1754386,7285.96017 12.1754386,7283.34361 12.1724311,7280.72405 C9.44461153,7281.06486 6.71679198,7281.42567 4,7281.84344 M24,7288.47179 C20.3879699,7288.48578 16.7759398,7288.54075 13.1619048,7288.55175 C13.1598997,7285.88921 13.1598997,7283.22967 13.1619048,7280.56914 C16.7689223,7280.01844 20.3839599,7279.50072 23.997995,7279 C24,7282.15826 23.997995,7285.31353 24,7288.47179">
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -29,7 +29,8 @@ export const AccessTokenColumns: ColumnDef<
{
accessorKey: 'id',
header: '',
size: 10,
size: 20,
maxSize: 20,
cell: (props) => <AccessTokenDelete id={props.row.original.id} />,
},
]

View File

@ -63,7 +63,10 @@ export function DataTable<TData, TValue>({
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
<TableCell
key={cell.id}
style={{ width: cell.column.getSize() }}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}

View File

@ -1,96 +0,0 @@
import { ChevronsUpDown } from 'lucide-react'
import {
Avatar,
AvatarFallback,
AvatarImage,
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from '@boring.tools/ui'
import { SignOutButton, useUser } from '@clerk/clerk-react'
import { Link } from '@tanstack/react-router'
export function SidebarUser() {
const { isMobile } = useSidebar()
const { user } = useUser()
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={user?.imageUrl}
alt={user?.fullName ?? 'Avatar'}
/>
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user?.fullName}</span>
<span className="truncate text-xs">
{user?.primaryEmailAddress?.emailAddress}
</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? 'bottom' : 'right'}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={user?.imageUrl}
alt={user?.fullName ?? 'Avatar'}
/>
<AvatarFallback className="rounded-lg">
{user?.firstName?.substring(0, 1)}
{user?.lastName?.substring(0, 1)}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{user?.fullName}
</span>
<span className="truncate text-xs">
{user?.primaryEmailAddress?.emailAddress}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Link to="/user">Profile</Link>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<SignOutButton>
<button type="button">Sign out</button>
</SignOutButton>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
}

View File

@ -1,4 +1,4 @@
import { ChevronsUpDown, KeyRoundIcon } from 'lucide-react'
import { ChevronsUpDown, KeyRoundIcon, TerminalIcon } from 'lucide-react'
import {
Avatar,
@ -37,6 +37,14 @@ export function SidebarUser() {
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton asChild tooltip="Access Tokens">
<Link to="/cli" activeProps={{ className: 'bg-sidebar-accent' }}>
<TerminalIcon />
<span>CLI</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarSeparator />
<SidebarMenuItem>
<DropdownMenu>

View File

@ -17,6 +17,7 @@ import { Route as ChangelogIdVersionVersionIdImport } from './routes/changelog.$
// Create Virtual Routes
const CliLazyImport = createFileRoute('/cli')()
const IndexLazyImport = createFileRoute('/')()
const UserIndexLazyImport = createFileRoute('/user/')()
const PageIndexLazyImport = createFileRoute('/page/')()
@ -37,6 +38,11 @@ const ChangelogIdEditLazyImport = createFileRoute('/changelog/$id/edit')()
// Create/Update Routes
const CliLazyRoute = CliLazyImport.update({
path: '/cli',
getParentRoute: () => rootRoute,
} as any).lazy(() => import('./routes/cli.lazy').then((d) => d.Route))
const IndexLazyRoute = IndexLazyImport.update({
path: '/',
getParentRoute: () => rootRoute,
@ -146,6 +152,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexLazyImport
parentRoute: typeof rootRoute
}
'/cli': {
id: '/cli'
path: '/cli'
fullPath: '/cli'
preLoaderRoute: typeof CliLazyImport
parentRoute: typeof rootRoute
}
'/access-tokens/new': {
id: '/access-tokens/new'
path: '/access-tokens/new'
@ -290,6 +303,7 @@ const PageIdLazyRouteWithChildren = PageIdLazyRoute._addFileChildren(
export interface FileRoutesByFullPath {
'/': typeof IndexLazyRoute
'/cli': typeof CliLazyRoute
'/access-tokens/new': typeof AccessTokensNewLazyRoute
'/changelog/$id': typeof ChangelogIdLazyRouteWithChildren
'/changelog/create': typeof ChangelogCreateLazyRoute
@ -309,6 +323,7 @@ export interface FileRoutesByFullPath {
export interface FileRoutesByTo {
'/': typeof IndexLazyRoute
'/cli': typeof CliLazyRoute
'/access-tokens/new': typeof AccessTokensNewLazyRoute
'/changelog/create': typeof ChangelogCreateLazyRoute
'/page/create': typeof PageCreateLazyRoute
@ -327,6 +342,7 @@ export interface FileRoutesByTo {
export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexLazyRoute
'/cli': typeof CliLazyRoute
'/access-tokens/new': typeof AccessTokensNewLazyRoute
'/changelog/$id': typeof ChangelogIdLazyRouteWithChildren
'/changelog/create': typeof ChangelogCreateLazyRoute
@ -348,6 +364,7 @@ export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths:
| '/'
| '/cli'
| '/access-tokens/new'
| '/changelog/$id'
| '/changelog/create'
@ -366,6 +383,7 @@ export interface FileRouteTypes {
fileRoutesByTo: FileRoutesByTo
to:
| '/'
| '/cli'
| '/access-tokens/new'
| '/changelog/create'
| '/page/create'
@ -382,6 +400,7 @@ export interface FileRouteTypes {
id:
| '__root__'
| '/'
| '/cli'
| '/access-tokens/new'
| '/changelog/$id'
| '/changelog/create'
@ -402,6 +421,7 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
IndexLazyRoute: typeof IndexLazyRoute
CliLazyRoute: typeof CliLazyRoute
AccessTokensNewLazyRoute: typeof AccessTokensNewLazyRoute
ChangelogIdLazyRoute: typeof ChangelogIdLazyRouteWithChildren
ChangelogCreateLazyRoute: typeof ChangelogCreateLazyRoute
@ -415,6 +435,7 @@ export interface RootRouteChildren {
const rootRouteChildren: RootRouteChildren = {
IndexLazyRoute: IndexLazyRoute,
CliLazyRoute: CliLazyRoute,
AccessTokensNewLazyRoute: AccessTokensNewLazyRoute,
ChangelogIdLazyRoute: ChangelogIdLazyRouteWithChildren,
ChangelogCreateLazyRoute: ChangelogCreateLazyRoute,
@ -439,6 +460,7 @@ export const routeTree = rootRoute
"filePath": "__root.tsx",
"children": [
"/",
"/cli",
"/access-tokens/new",
"/changelog/$id",
"/changelog/create",
@ -453,6 +475,9 @@ export const routeTree = rootRoute
"/": {
"filePath": "index.lazy.tsx"
},
"/cli": {
"filePath": "cli.lazy.tsx"
},
"/access-tokens/new": {
"filePath": "access-tokens.new.lazy.tsx"
},

View File

@ -0,0 +1,107 @@
import {
Button,
Card,
CardContent,
CardHeader,
CardTitle,
} from '@boring.tools/ui'
import { Link, createLazyFileRoute } from '@tanstack/react-router'
import { CopyIcon } from 'lucide-react'
import { PageWrapper } from '../components/PageWrapper'
const Platforms = [
{
name: 'Linux',
arch: 'x86',
svg: '/linux.svg',
path: '/cli/linux/bt',
filename: 'bt',
},
{
name: 'Apple Intel',
arch: 'Intel',
svg: '/apple.svg',
path: '/cli/mac-intel/bt',
filename: 'bt',
},
{
name: 'Apple ARM',
arch: 'ARM',
svg: '/apple.svg',
path: '/cli/mac-arm/bt',
filename: 'bt',
},
{
name: 'Windows',
arch: 'x86',
svg: '/windows.svg',
path: '/cli/windows/bt.exe',
filename: 'bt.exe',
},
]
const Component = () => {
return (
<PageWrapper breadcrumbs={[{ name: 'CLI', to: '/cli' }]}>
<div className="flex flex-col gap-5 w-full max-w-screen-lg">
<h1 className="text-3xl">CLI</h1>
<p className="text-muted-foreground">
With our CLI you can upload your commits for your changelog in just a
few seconds.
</p>
<h2 className="text-xl">Download</h2>
<div className="grid grid-cols-4 gap-10">
{Platforms.map((platform) => {
return (
<a
href={`https://cdn.boring.tools${platform.path}`}
key={`${platform.arch}-${platform.name}`}
download={platform.filename}
>
<Card className="hover:border-accent transition">
<CardHeader>
<img
src={platform.svg}
alt={platform.name}
className="h-20"
/>
</CardHeader>
<CardContent className="text-center">
{platform.arch}
</CardContent>
</Card>
</a>
)
})}
</div>
<h2 className="text-xl">Usage</h2>
<pre className="bg-muted text-xl p-3 rounded text-center flex justify-between items-center">
bt --changelogId=... --token=bt_...
</pre>
<p className="text-muted-foreground">
Alternatively, you can use an .env file:
</p>
<pre className="bg-muted text-xl p-3 rounded text-center flex justify-between items-center">
BT_CHANGELOG_ID=...
<br />
BT_AUTH_TOKEN=bt_...
</pre>
<p className="text-muted-foreground">
If you have not yet created an Access Token, you can do so{' '}
<Link to="/access-tokens/new" className="text-accent font-bold">
here
</Link>
.
</p>
</div>
</PageWrapper>
)
}
export const Route = createLazyFileRoute('/cli')({
component: Component,
})

View File

@ -1,8 +1,8 @@
import { parseArgs } from 'node:util'
import { z } from 'zod'
const ENV_ID = Bun.env.CHANGELOG_ID
const ENV_TOKEN = Bun.env.AUTH_TOKEN
const ENV_ID = Bun.env.BT_CHANGELOG_ID
const ENV_TOKEN = Bun.env.BT_AUTH_TOKEN
const schema = z.object({
token: z.string(),